gss-lang
v0.0.6
Published
Write 3D scenes in CSS: selectors, the cascade, @keyframes, and more compiled into GLSL and WGSL shaders for WebGL2 and WebGPU.
Maintainers
Readme
GSS (GPU Style Sheets) is a 3D language for people who already speak CSS. You declare shapes in a
@scene, style them with selectors, and the compiler turns the whole thing into GLSL and WGSL
fragment shaders: signed distance fields, raymarched in WebGL2 or WebGPU. No Three.js, no meshes, no UVs.
@scene {
torus#ring;
sphere#ball;
}
#ring {
translate: 0 1.2 0;
rotate-x: 70deg;
radius: 1.1;
thickness: 0.14;
color: #e8e6e1;
}
#ball {
translate: 0 1.2 0;
radius: 0.55;
color: #ff5a36;
material: jelly(0.6);
animation: float 2s ease-in-out alternate;
}
@keyframes float {
from { translate: 0 1.2 0; }
to { translate: 0 1.5 0; }
}Install
Three ways to put a scene on a page, using [email protected].
The installation guide covers npm, Vite and CDN setup.
A tag, no build step. Load embed.js once, then point a <gss-scene> at a .gss file, or
write the scene inside it:
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/lib/embed.js"></script>
<gss-scene src="logo.gss"></gss-scene>
<gss-scene controls="none">
<script type="text/gss"> @scene { sphere; } </script>
</gss-scene>A function, with any bundler. The compiler runs in the page:
npm install [email protected]import { mount } from "gss-lang";
const scene = mount(canvas, "@scene { sphere; }");
scene.update(otherSource);
scene.destroy();Compiled at build time, with Vite. The plugin is included in [email protected]; no separate plugin package is needed. The page ships a small runtime and the shader, not the
compiler:
// vite.config.ts
import gss from "gss-lang/vite";
export default { plugins: [gss()] };
// main.ts
import { mount } from "gss-lang/runtime";
import logo from "./logo.gss";
mount(canvas, logo);With TypeScript, add "types": ["gss-lang/client"] to tsconfig.json so .gss imports are typed.
An embedded scene behaves like the playground: drag turns the camera, the wheel zooms, :hover
works (controls="none" keeps only :hover). It starts when it comes into view, sleeps when it
leaves it, and stands still under prefers-reduced-motion. Images are read next to the .gss
file, like url() in a stylesheet.
<gss-scene poster="cover.jpg"> shows an image until the scene draws, like a <video>. On a
computer without a graphics card, the processor would draw the scene slowly enough to freeze the
page: <gss-scene> then keeps its poster and shows a button, and draws only when the reader asks.
With mount(), softwareRendering() returns true on such a computer, to do the same.
WebGPU, with a WebGL2 fallback. Use the asynchronous API to select a rendering backend:
import { mountAsync } from "gss-lang";
const scene = await mountAsync(canvas, "@scene { sphere; }", { backend: "auto" });
console.log(scene.backend); // "webgpu" or "webgl"
await scene.update(otherSource);
scene.destroy();mountAsync is also available from gss-lang/runtime for Vite-compiled scenes.
auto (the default for this API) tries WebGPU, then uses WebGL2 if adapter or device acquisition
fails. Use backend: "webgpu" or "webgl" to force a backend. WebGPU requires HTTPS or localhost.
The synchronous mount() API keeps using WebGL2. A tag can opt in with
<gss-scene backend="auto" src="logo.gss"></gss-scene>; its scene.update() then returns a promise.
compile(source) includes both targets: scene.shader is GLSL, scene.wgsl is WGSL, including
filter passes and media variants. compile(source, { target: "glsl" }) omits WGSL when only WebGL2
is needed. Older precompiled scenes without WGSL work with WebGL2; recompile them to use WebGPU.
Shader errors reject an asynchronous update while keeping the previous scene visible. Runtime
device loss is reported through a gss-error event on the canvas; recreate the scene with a new
canvas to recover. Automatic fallback is an initialization feature, not device-loss recovery.
Why
Front-end developers already know how to describe what things look like. The moment they want to go 3D, they hit GLSL: distance functions, vectors and math, written in a way that has little to do with how they think about design. GSS is a way in: a familiar syntax to start creating in 3D today, and a readable path toward the shader underneath. It is not meant to replace GLSL.
What it can do
- Structure:
@scene { cube.corner * 4; torus#hero; }, multiplication with auto-numbered ids,group#g { … }to move several shapes together. - Selectors and the cascade:
shape,.class,#id,*, lists, descendant (a b), child (a > b) and sibling (a + b,a ~ b) combinators, specificity,!important,::face(front)/::top/::bottomto style one face,:hover(#letters:hover cubelights up a whole group,sphere:hover + cubereacts to its neighbour) and:active(pressed, with the mouse or a finger).:has()checks descendants or relative selectors:group:has(> cube),cube:has(+ sphere:hover).:nth-child(odd),:nth-of-type(),:first-child… count the copies of* none by one, and:not()leaves some out:cube:not(:first-child, :last-child). Rules nest like CSS:#g { cube { &:hover { color: white; } } }. - Shapes:
cube,sphere,torus,cylinder,cone,capsule,plane,path(a tube along an SVG path),prism(apolygon()orpath()contour, extruded into a flat object) andlathe(the same contour, turned around an axis into a round one: a vase, a bowl);pyramid,octahedronandtube(a hollow cylinder, a pipe). - Materials:
matte(),metal(),jelly(),glass()with refraction and frost,emissive()(a surface that gives its own light: a lamp, a neon),iridescent()(a thin film whose colors turn with the angle of view, like a soap bubble), and the shortcutsgold,chrome,copper,silver,brass,aluminum,ice;opacity, transparent colors andfilter: opacity(), the objects behind showing through. - Textures:
texture: url("dirt.png")projected on each face, a different image per face (the Minecraft grass block),image-rendering: pixelatedandtexture-sizeto repeat it;texture: paint(rings)draws a texture with a fragment shader written in a@paint rings { … }block, in GLSL, for the patterns CSS cannot describe (WebGL2 only, for now: the GLSL is not translated for WebGPU). - Motion:
@keyframes, animation controls and easings (includingsteps()), computed on the GPU;transitionfor hover changes; scroll-driven animations withanimation-timeline: scroll()andview(); a motion path withoffset-path, andtransform-originto turn and scale around any point (a door on its hinge). - Math:
calc(),min(),max(),clamp(), trigonometry, andsibling-index()/sibling-count()for CSS-style loops. - Variables:
--size: 2andvar(--size, 1), inherited from the scene to groups to objects, and animatable in@keyframes. A variable registered with@propertyis set from the page at any moment, without compiling again:scene.setProperty("--lift", "2")(set variables from JavaScript). With@property-panel { display: open; }, the playground shows a slider for each of them. - Colors: hex,
rgb(),hsl(),oklch(),color-mix()and the other CSS color functions, the CSS named colors (tomato) andcurrentColor, with math andvar()inside:hsl(calc(sibling-index() * 45) 90% 60%). - Gradients, noise and filters: linear, radial and conic gradients on backgrounds and objects, animated with
@keyframesand:hover(the scene animates its background);noise(), colors placed by a 3D noise (clouds, stone, marble), and the patternschecker()andstripes(); layers of background, with transparent colors and the blend modes of CSS (background-blend-mode); holes cut in objects bymask-image; images moved by a map withdisplace(), like SVGfeDisplacementMap; color filters, blur, bloom and grain on scenes, objects and groups, and the lens effectsvignette()andchromatic-aberration()on the scene. - Responsive scenes:
@media,light-dark()and conditionalif()values. - Generative values: deterministic
random(), inverse trigonometry, rounding, logarithms andprogress(). - The scene:
floor,background, a sun with its color (light), a coloredambientlight,fog, soft or hardshadows, an orbit camera, andshape-rendering: geometricPrecisionfor smooth silhouettes. Lights are elements of the scene too:@scene { light#bulb; }, placed, animated and hovered like an object, withcolorandintensity.
Every property, shape, selector and function has its page in the reference, with a live example to edit.
How it works
GSS text → tokenizer → parser → scene expansion → cascade → validation → shader generation
├─ GLSL → WebGL2
└─ WGSL → WebGPUEverything that can be decided at compile time is: the cascade, the selectors, the units,
var(), the math and the colors, in that order. The shader receives final values. The runtime
only holds what changes every frame (time, camera, the hovered and pressed objects, the scroll, the
variables the page sets) and sends it as uniforms.
Tools
- Playground: gss-lang.dev/playground. Every error
of a scene at once, each under its line; examples, generated GLSL and WGSL, backend selection,
a panel of sliders for the variables of
@property-panel, the chipsview: shadedandview: distance(the isolines of the distance field over the scene), share by URL, export to Shadertoy, a formatter (Shift+Alt+F) and a performance panel (Alt+P) for WebGL2 and WebGPU. It shows FPS, frame and CPU times, resolution, shader preparation time, and GPU time when the backend exposes timer queries. WebGPU uses optionaltimestamp-query; without it, GPU time is marked unavailable while the other measurements remain available. - Reference: gss-lang.dev/docs, searchable, one live example per entry, each page with the version that added it.
- Editor support: the GSS extension for VS Code, and the editors built on it (Cursor, VSCodium,
Windsurf): highlighting, formatting, the
.gssfile icon, and HTML snippets for<gss-scene>.code --install-extension lukyvj.gss-language, or search for GSS in the Extensions view. - Showcase: gss-lang.dev/showcase, what you can make, for designers, creative coders and developers.
Status
Version 0.0.6: early, and moving fast. The syntax may still change between versions.
See the changelog for release history and unreleased changes.
Community
- X: @GSS_lang, for news, scenes, questions and ideas. Share what you make with GSS there: mention the account.
- GitHub: issues for bugs and feature requests.
License
Brand
The mark, the wordmark, the icon and the social cards are on gss-lang.dev/brand.
