Changelog
What changed in GSS.
Every version, the newest first: what it added, what it changed and what it fixed. The package is on npm, the code on GitHub.
Unreleased
Added
The home page thanks the contributors: a section "Contributors" with the avatar and the name of each person whose pull request is in GSS, and what each pull request brought, linked to it.
The footer links to it.
A page for the changelog,
/changelog: every version, the newest first, each with its anchor (/changelog#0.0.7).Each change shows its first sentence, and "more" unfolds the rest. The version in the footer of every page opens it, where it opened npm.
- The films of GSS on the brand page: the teaser, the launch film and the films of 0.0.6 and 0.0.7, to play in place or to download in 16:9 or 9:16.
0.0.7
Added
rangein@property-panel: the scene gives the slider of a variable its ends and its step.@property-panel { range: --col 0 3 1, --row 0 3 1; }makes two sliders from 0 to 3, one whole number at a time, for a cube on a board of 4 by 4. A number typed past an end is held there, and the panel says what the end is; a variable without a range keeps its slider as before.Attribute selectors, like CSS:
[attr],[attr=value],~=,|=,^=,$=,*=, and the flagsiands.An object gets its attributes in
@scene, like HTML:@scene { cube[data-col=2]; sphere[lit]; }, then[data-col=2] { color: red; }. Its id and its classes count as the attributesidandclass, so[id^="hero-"]styles every copy oftorus#hero * 3. An attribute weighs like a class.texture-mapping: spherewraps the image oftexturearound the object, like a map around a globe: a map of the Earth, the Moon or a planet, twice as wide as it is tall, covers a sphere in one piece.The middle of the image faces the front, its top is the top, and its left and right edges meet at the back.
box, the default, keeps the image on each face, as before. The docs have the Earth and the Moon to try, on maps by NASA.content: a text on the surface of an object.cube { content: "hello"; }writes the word on the faces of the cube: it turns and takes the light with it, and goes behind what stands in front. The text is as large as each face lets it be, andcube::face(front) { content: "1"; }gives one face its own, for a die or a sign. On a round shape the text wraps around it like a label on a can; a pyramid and an octahedron write on each of their faces; any other shape takes it like a sticker. It reads a variable (content: var(--word)), and takes its font fromfont-family,font-weightandfont-style, like CSS, and its color fromtext-color; without one it is black on a light object and white on a dark one."one\A two"breaks a line, like CSS;font-size: 0.3sets the height of the letters in the units of the scene and breaks the lines at the width of the face, andtext-alignsets their side. An emoji keeps its colors. On:hoverand in@keyframes,text-colorandfont-sizechange smoothly, and the text and its font change halfway, like CSS. Unlike CSS,contentgoes on the object itself, not on a::before.::face()takes a number, like:nth-child():::face(3),::face(odd),::face(n + 5).A cube is numbered like a die (front 1, top 2, right 3, left 4, bottom 5, back 6), a pyramid from its front slope turning right then its base, an octahedron from its front-right top triangle.
mix-blend-modeon objects, like CSS: an object blends its color with what is behind it, the objects behind it, the floor and the background.sphere { mix-blend-mode: multiply; }tints what shows through the sphere; three spheres withscreenadd up to white where they meet. Every mode ofbackground-blend-mode, andplus-lighter. It works withopacity, on a group (each of its objects takes the mode), on:hoverand in@keyframes, where it changes from one mode to the other halfway through, like CSS.Mixins, like CSS:
@mixin --name { … }names a block of declarations and rules, and@apply --name;puts it in any rule — an object,:hover, a nested rule, a@media, even a frame of@keyframes.A mixin takes parameters with defaults, read with
var()(@mixin --ball(--size: 0.4) { radius: var(--size); }, then@apply --ball(0.7);), and@contentsplaces the block given to@apply, inside a@mediafor example. Close to the@mixinand@includeof Sass, written the way CSS writes it. The scene compiles as if the mixin were written out in each rule, and a mistake (an unknown mixin, a value too many, a mixin that applies itself) is an error that points at it.Loops,
@forand@each, like Sass, written with CSS variables.@for --i from 1 through 4 { … }writes its block once for each number, withvar(--i)replaced by it;toleaves the last number out,bysets the step, and the numbers can take a unit (from 0deg to 360deg by 45deg).@each --x in tomato, gold, teal { … }does the same for each value of a list. A loop goes anywhere: in@sceneto make objects, even of a different shape at each step (@each --shape in cube, sphere, cone { var(--shape); }), around rules, wherevar(--i)works in the selector too (.step:nth-child(var(--i))), in a rule, in@keyframes, in a mixin, in another loop for a grid. The scene compiles as if each step were written by hand, and a mistake (nofrom, a step of 0, more than 1000 steps) is an error that points at it.controls: the gestures that move the camera, onsceneor on an object.scene { controls: none; }keeps the camera where the GSS puts it and lets the wheel scroll the page;controls: orbitkeeps the drag,controls: zoomthe wheel. On an object,controls: nonelets it be pressed without turning the camera, while a drag from the floor still turns it: the pieces of Back Rank and the buttons of Midnight Arcade, in the playground, now hold still. Likecursor, a group passes it to its objects, and@mediacan change it. On a page,<gss-scene controls="none">still turns every gesture off.Docs on GitHub: the reference of gss-lang.dev/docs as Markdown pages in
docs/, one per section, from Getting started and Installation to every property, with an index.The README links to them, so the repository is enough to start with GSS.
Playground: four scenes for the features of 0.0.6, in a group "New in 0.0.6" at the top of the examples.
Midnight Arcade: two cabinets after closing time, one screen drawn by a
@paintshader, the other a game of Pong in a<canvas>shown withelement(), neon inemissive()underbloom(), a flickering sign, the lens ofvignette()andchromatic-aberration(), coins that drop into their slot on a click. Back Rank: a checkmate on achecker()board, with pyramids, tubes and octahedrons in silver and copper, outlines on hover, and the king in check in a reddoubleoutline. Lighthouse: scroll to build it, then to light it, four parts of the way of the scene across the page, set byanimation-rangeonview(). Bubble Works: a copper machine that blowsiridescentbubbles to pop,stripes()climbing a glass pipe, and gauges in the shaded styles ofoutline. The scenes of 0.0.4 and 0.0.5 move to a group of their own.Showcase: an archive of every scene of the page, the studies included, in place of the inspiration grid.
Each scene has its capture, its name, what it is, a badge of the version of GSS it came with, and the features it uses, each linked to its page of the docs. Newest first, as a grid of cards or as a list; the page remembers the choice.
Changed
Docs: a figure on every page, in line drawings that move.
Every one of the 154 pages opens on a drawing of what it is about. A selector shows a scene as a row of objects and lights the ones it targets; a shape turns under the pointer, with the properties that size it (
radius,height,size…) drawn on it;translate,scaleand the rotations move a cube; the longhands ofanimationare lanes to compare, each easing a curve with a point that follows it; an image, a text or a pattern is shown flat, then on the faces of a cube; a color function fills chips with the very colors it gives; the light, the fog, the camera, an outline and the operations between objects each have their scene. "Your first scene" shows its scene one declaration at a time. The lines draw themselves when a page opens; withprefers-reduced-motion, nothing moves. Code in a figure is written in words (@mixin,@apply); a pattern is filled with its two colors (checker(),stripes()); a shadow is dark on a floor in the light; a point light is a point with its light around it, and the side of a ball it reaches.Docs: "On this page" follows you down the page.
The link of the part you are reading lights up, and thin lines in front of the links grow toward it like a sound wave, with their peak there; the peak slides to the next link as you scroll. A link under the pointer grows a second, lower peak.
Docs: less to read on each page.
The paragraph under the table of a page is now one or two sentences, what you need before writing the property; the limits, the special cases and how things are drawn are folded under "More details", one click away. 52 pages of the reference changed, and nothing they said is gone: "copy page" and the search still read the folded text. In the guides, "In the page" of Embedding a scene and the new "Slow computers" of the CDN page are tables, one line per thing to know.
Showcase: the studies wait for play.
The viewer at the top of the page opens on a still image of the study and a large play button, so the page no longer draws its heaviest scene on arrival; choosing another study in the list draws it at once.
- The install lines are colored like the rest of the code: the three of the home page (npm, the Vite plugin, the CDN) and the shell lines of the docs, the command, its flags and the version of the package.
Fixed
- The formatter of the playground and of the VS Code extension no longer breaks a selector with a pseudo-class inside a
@media:.gauge:nth-child(9)stayed as written, where it became.gauge: nth-child(9), which no longer compiled. In the playground, the slider over the render moves an animation of
scroll()withanimation-range: the playground does not scroll, so the animation stood at its end whatever the slider said.The example "the middle of the scroll" of
animation-rangeturns again; in a page that scrolls, nothing changes.shape-rendering: geometricPrecisionaround apath, aprismor alathedrawn small: grey lines, rings and a faint rectangle no longer fill the box around the shape.Only its silhouette is smoothed, like any other object.
shape-rendering: geometricPrecisionon a large canvas (a 4K screen, or a Retina screen atdpr: 2): silhouettes are smoothed as much as on a small one, where they stayed sharp.An object can look up to a quarter of a pixel larger than with
auto, and a scene with many edges takes a little more work at that size.outlineon apath, aprismor alathedrawn small follows its shape: it drew the whole rectangle around it, in lines, rings and a filled area.
0.0.6
Added
Docs: a second
element()example — a<canvas>and its one-line WebGL shader inside the element, captured frame after frame on the face of the cube.The docs now say a canvas inside the element is captured with it.
outline: a line around the silhouette of an object, like CSS:outline: 0.03 solid #111;for a drawn look, or an outline that appears on:hover.Its width and
outline-offsetare in the units of the scene, so the line gets thinner far away, like the object. Every style of CSS:solid,dashed,dotted,double,groove,ridge,insetandoutset. Its color is the object's own by default, and width, color and offset can be animated.In the playground and in Try it,
@mediareads the size of the render, like the result of CodePen: make the render narrower to see amax-widthquery apply.On a page, it still reads the window, like CSS.
display: noneleaves an object, or a whole group, out of the scene, and turns a light off; with@media, a phone can draw fewer objects.It still counts for
:nth-child().visibility: hiddenhides an object: not drawn, no shadow, and the mouse goes through it.Like CSS, a group passes it to its objects, and it can change on
:hoverand in@keyframes, for a blinking light or an object shown by hovering another.cursor: pointer,grab,grabbingand the other cursors of CSS: the mouse pointer over an object, also in:hoverand:activerules (cube:active { cursor: grabbing; }).animation-range,animation-range-startandanimation-range-end: the part of a scroll timeline the animation plays on, like CSS:animation-range: entryplays while the scene comes into view,entry 50% exit 50%from half in to half out, and25% 75%on the middle of the scroll.filter: vignette()andchromatic-aberration()on the scene: darker corners, and red and blue that split towards the edges, like a lens.material: iridescent(): a thin film, like a soap bubble or the back of a CD, whose colors turn with the angle you see it at.emissiveandiridescentalso work as keywords, likejellyandglass.checker()andstripes(): a checkerboard and bands of two colors, wherever a gradient goes (color, a material,background,floor).Like
noise(), they are cut in the object's own space, with no seam, and can be animated:stripes(4 at 0 -0.25 0, …)in a@keyframesmakes the bands scroll.- Three shapes:
pyramid, on a square base and sized bysizelike a cube;octahedron, eight triangles, sized byradius; andtube, a hollow cylinder, with an outerradius, aheightand thethicknessof its wall. material: emissive(): a surface that gives its own light, a lamp, a screen or a neon, from a strength of 0 to 4.With
bloom()in the scene'sfilter, it glows.material: copper,silver,brassandaluminum: four more metals, ready to use likegoldandchrome.@paintandtexture: paint(name)(WebGL2 only, for now): a texture drawn by a fragment shader, the way CSSpaint()draws an image from code.The block of
@paint rings { … }is GLSL, written as it is for the web:out vec4 color;orgl_FragColor, withtime,resolutionandmousefilled by GSS (u_time,u_resolution,u_mousetoo). It is drawn into a 512 × 512 texture, again at every frame when it readstime, once otherwise, and the object shows it like an image. An error in its GLSL names the@paintand its line. Its GLSL is not translated for WebGPU: a scene withpaint()is drawn with WebGL2, which<gss-scene>,mountAsync()and the playground pick by themselves; on WebGPU, the object keeps its color. The playground, the docs and the VS Code extension color the block as GLSL, and the formatter keeps it as written.scene { shape-rendering: geometricPrecision; }smooths the silhouettes of the objects: a pixel that the edge of an object only partly covers blends the object with what is behind it.It takes about 15 % more work from the graphics card for now, so it is off by default:
autodraws the sharp edges of before, unchanged.- VS Code / Cursor extension: HTML snippets for both documented
<gss-scene>forms, a scene loaded from a.gssfile and an inline<script type="text/gss">scene. - Docs: every page says the version that added it, beside its title:
since v0.0.3. - Docs: the contents mark the pages of the latest version with
new in v0.0.5, and their group with a dot, so a folded group says it holds something new. GSS is open source on GitHub: github.com/LukyVj/GSS.
The site links to it from its top bars and footers, the docs at the top of Installation, and the package and the VS Code extension declare it, so npm and the extension registries link to the code and the issues.
Computers without a graphics card: the processor would draw a scene so slowly that the page could freeze, so the scene waits for a click.
<gss-scene>shows its poster and a button, "Draw it anyway"; the site does the same on the home page, in the playground and in Try it, where the code still shows.softwareRendering(), fromgss-langandgss-lang/runtime, tells a page that usesmount().<gss-scene poster="cover.jpg">: an image shown until the scene draws, like the poster of a<video>.- A scene too heavy for the computer stops instead of freezing the page: after a second or two of frozen frames at its lowest density, it stops drawing,
<gss-scene>and the site show "Draw it anyway", andmount()fires agss-too-heavyevent on the canvas, wherescene.play()draws it anyway. mount(canvas, scene, { adaptDpr: false }): thedpras written, never lowered and never stopped, for a capture or a benchmark.Showcase: an eighth study, the GSS 0.0.5 announcement.
A 30-second film written as one stylesheet: "css" types itself and turns into "gss.", the braces close the rule, then eight features take the stage one after the other. It opens in the playground, in the "Studies" group.
An
animationin a:hoveror:activerule starts with the state and plays to its end, like a button that fires: the pointer can leave, the button can go up, the animation goes on, once unless it says how many times; the state stays on until it ends, or for good withforwards, and the next press plays it again.cube:active { animation: topple 0.8s forwards; }.Showcase: "Le Chonk", a pixel 4 in the inspiration grid.
A
prismfrom a staircase contour, pixelated tiles from a texture; a click tips it over to the left, then on its back, with an animation started by:active.Showcase: the Utah teapot joins the inspiration grid.
A
lathebody and lid, aconespout and apathhandle melted into the body; hover lifts the lid, a click sends the teapot over a speed bump, withlinear()transitions that overshoot and settle.Showcase: the Product Hunt mark in 3D joins the inspiration grid.
An orange coin with the P of the flat logo pushed out of it, its
dpasted intopath(); no sun, two lights sweep the stage and the coin floats over its own shadows.Showcase: a room lit by one shaft of sun, after Maxime Heckel's global illumination study, closes the inspiration grid.
The sun comes in low through a window out of view and lands on the wall behind an orange ball; a warm light makes the ball glow on the floor, a cool one stands for the sky.
GSS is on X: @GSS_lang, for news, scenes, questions and ideas.
The site links to it from its top bars and footers, with a contact line in every footer (X for questions, GitHub for issues), the docs say so at the top of Installation, and a link shared on X attributes the card to the account. In the playground, once the share link is copied, the status bar offers to post it on X, with the account mentioned.
Docs search: once a word is typed, the right column shows the page of the selected result, by the pointer or the arrows: its sentence, its syntax and an example (the one the result lands on), with a link to open it.
With an empty box, the four drawings come back.
Docs search: the columns on each side of the results can be widened or narrowed, by dragging the line between them or with the arrow keys, and the search keeps the widths for next time.
Each column arranges itself for its width: the contents show the icon of each group once there is room, and a wide right column puts each drawing beside its text.
Docs search: "Try it" on the example of a preview.
The search slides to the left and the example opens live beside it, code and render, to edit without leaving the results; "Close" brings the search back. On a screen too narrow for both, it opens the example in the playground, in a new tab.
Docs: "Editor support", at the end of Installation.
The GSS extension for VS Code (highlighting, formatting, the
.gssfile icon) is on the Visual Studio Marketplace, and on Open VSX for Cursor, VSCodium and Windsurf: search for *GSS* in the Extensions view, or runcode --install-extension lukyvj.gss-language.Docs: "Editor support" opens on a square button that holds the marks of VS Code and Cursor like letters in a pocket; on hover they come out, one after the other.
It leads to the extension, with a line on what it does and where Cursor installs it.
Changed
A smaller package: a page that compiles GSS downloads 87 kB gzipped instead of 130,
<gss-scene>from jsDelivr 89 instead of 113, and a scene compiled at build time 12 instead of 16.The compiler no longer carries the documentation,
lib/is minified, andgss-lang/embedshares its files withgss-langinstead of holding its own copy: the npm package goes from 263 to 114 kB.A scene drawn with WebGL2 no longer freezes the page while it compiles.
On Windows, the graphics driver can take seconds to prepare a large scene; the page now goes on in the meantime, the scene before it stays on screen, and
<gss-scene>keeps its poster until the new scene can draw. Itsloadevent comes once the scene is on screen.- The live demo of the home page and the Try it of the docs stop drawing when they are scrolled off screen, and resume where they were, like
<gss-scene>andmount()already did. Every scene starts at a density of 0.5, then climbs to its
dprin under a second while the frames keep up, and lowers it while they are slow: inmount(),<gss-scene>, the home page and the showcase too, not only in the docs and the playground.A weak graphics card no longer draws the first frames of a heavy scene at full density, which froze some browsers on the home page.
The site on small screens: the side padding follows the width (56, 32, then 16 pixels), the top bars and the footers with it.
The bars shrink in steps: the gaps first, then the GitHub and X links keep their mark and lose their text (under 800 pixels), then, on a phone, the home bar is two rows and the footers stack. A link never breaks on two lines.
- The eight studies render at a pixel density of 1.5 (
dpr: 1.5), in the showcase and in the playground: sharper than before on a standard screen, lighter than before on a high-density one.
Fixed
An animation that stops on its end (a number of iterations with
forwardsorboth, the end of a scroll timeline) holds exactly its last keyframe, like CSS.With
steps()orstep-end, an animation whose last keyframe came after one at 80% or 99% stopped one step short; an object hidden by its last keyframe stayed visible. Witheaseand the othercubic-bezier()easings, an object animated fromvisibility: hiddentovisibleno longer shows before the keyframe that shows it.A material with a color of its own keeps it when
coloris a gradient or a pattern, as it does over a plain color:brasson a striped object is brass, andmetal(#3a7bff)stays blue.metal(0.2)and the other materials without a color still take the gradient. The transparent stops of the gradient still show.- Docs search: a result whose title holds a tag, like the chapter "From <gss-scene>", shows the tag as text; it was drawn as a real element in the list.
0.0.5
Added
lathe, a new shape: a contour turned around the vertical axis, like clay on a potter's wheel, where aprismpushes it into a flat plate.Draw half the outline right of the axis x = 0, with
d: polygon(…)ord: path("…"): a vase, a bottle, a bowl that is hollow, a chess piece, or a ring when the contour does not touch the axis.view-boxcenters its height.floortakes an image, likecoloron an object: a gradient, anoise(), or one of them moved bydisplace().floor: noise(2 2, #10182b, #273d62 55%, #0b1020)is stone;floor: radial-gradient(circle, #4a4f6a, #0b1020 12%)is a pool of light under the scene. A gradient covers a square of 40 units centered under the scene, seen from above; anoise()is read at each point of the floor. A valuefloorcannot take now says so, instead of an error aboutcolor.@property-panel { display: open | folded | none; }: the playground and Try it in the docs show a panel at the top left of the render, with a control for each variable of@property: a slider and its number, or a color picker for a color.It sets the variable as it moves, without compiling the scene again; a number typed past the end of a slider widens it. A start value changed in the code gives the variable back to the code, and a button resets a moved one.
foldedstarts with only its title. A page that embeds the scene shows no panel. The VS Code grammar colors@property-paneland@property.view: distanceon the scene: the isolines of the distance to the objects, one every 0.25 and fainter as they go away, drawn over the scene on the plane that faces the camera throughcamera-target, the field the shader marches through.view: shadedis the default. In the playground and in Try it, the chipsview: shadedandview: distanceat the top left of the render switch it without touching the code; when the code changes itsview, the code wins again.Playground: a "New features" group in the examples menu.
Astral Greenhouse shows the ten latest additions in one scene; Property Control Room has three variables for the panel; Noise Atmosphere, Mask & Displacement and an HTML card shown with
element()show one feature each.Playground: a separator between the editor and the scene.
Drag it to resize the editor (its height on a phone), double-click it for the default size, move it with the arrow keys, or fold the editor into a thin rail by dragging it to the edge; a click opens it again. The size is kept in this browser.
Playground and docs: a dpr menu at the top right of the render.
auto, the default, lowers the pixel density while the frames are slow and raises it back when they are fast, never above the scene'sdpr; a number fixes it. The choice is kept in the browser.Seven studies open the showcase, one at a time in a viewer: Glass Circuit, Soft Relic, Candy Garden, Chromatic Bloom, the Zdog character and burger reconstructions, and a Nikon F exterior with an illustrative exploded lens.
A study driven by
scroll()has a slider. Each one opens in the playground, where they have their own group, "Studies".Docs search: a wider window in three columns.
On the left, the contents of the docs; on the right, four drawings that explain GSS: objects in
@scene, the cascade, space, and how the shader draws the scene. In the middle, before a word is typed: where to start, the pages the latest version added ("New in 0.0.5"), and, once there are enough searches, the ones people make the most; a click on one types it.Docs: an icon for each group of the sidebar, before its title: a cube for Shapes and groups, a drop for Colors, a camera for Camera.
They take the colour of the title, and the accent on the page being read. On hover, each one moves in its own way: the arrow drops into the tray, the camera's shutter closes, a dot rides the easing curve. The brand page shows all of them and offers each one as an SVG file.
Changed
A scene that does not move draws nothing until something changes: the mouse over an object with
:hover, a variable set from the page, an image that arrives, the size of the canvas.The image stays on screen and the GPU rests, instead of drawing the same frame 60 or 120 times a second. A scene that moves draws every frame, as before; the performance panel, while open, too.
Scenes with many objects draw much faster, with the same image: the shader groups the objects by where they are, whatever the groups of the code, and skips those far from each point.
In the bench, a scene of 82 objects went from 75 ms to under 20 ms a frame, a field of 64 tufts of grass from 26 ms to 5 ms.
Docs: every page of the reference is shorter and cut into parts, like the reference of a popular language: a lead of three sentences at most, a table of its values, a paragraph, then its examples, each under a heading with a sentence that says what it shows.
"On this page" lists them, and the code in the text shows as code. "Embedding a scene" and "Set variables from JavaScript" have chapters.
Docs navigation: "light (sun)" and "light (point)" instead of two "light"; Colors is split into Colors, Color functions, and Gradients and noise; Selectors into Selectors, Combinators, and Pseudo-classes.
The examples of the reference have telling names, in the docs and in the Examples menu of the playground.
- Showcase: the note under "On your site" no longer says GSS has no transparency: a transparent object shows the scene behind it, not the page, so the scene still takes the background of the page.
- Docs: a callout under
element()andtexturesays how to seeelement()today: behindchrome://flags/#canvas-draw-elementin Chromium, or on a site with the HTML-in-Canvas origin trial and its token, until the trial ends on October 20, 2026.
Fixed
Docs search: every page is found again,
opacity,shadows,fog,mask-imageand the point light included, and a result lands on what it found: an example, the table of values or a chapter, not the top of the page.A link to a part of a page works before the page script runs.
0.0.4
Added
Nesting, like CSS: a rule holds rules, and
&stands for the selector around it:#g { cube { &:hover { color: white; } } > sphere { … } }.A nested selector without
&is a descendant (cubeis#g cube),&works inside:has()and:not(), and a@mediacan go inside a rule. Autocompletion and the VS Code grammar know nested rules.transform-origin, like CSS: the point an object turns and scales around.Keywords and percentages on the box of the object (
left,top right,0% 100%), or a point from its center liketranslate(transform-origin: 0 0.5 0), then z. Animatable, and on groups with numbers.@propertyandsetProperty(): a variable registered like CSS (syntax,inherits,initial-value; numbers, angles, percentages, colors) is set from the page without compiling again:scene.setProperty("--lift", "2"),getPropertyValue(),removeProperty(), on whatmount()returns and on<gss-scene>.It goes wherever a value reaches the shader: transforms, colors, the sizes of the shapes, the numbers of a gradient, materials, the floor, the ambient light, the sun,
camera-target,blend, the motion path and filters (a"<length>"in px forblur()andbloom()), alone or insidecalc(), the math functions and the color functions (hsl(var(--hue) 80% 60%),color-mix()…), which the GPU then computes at every frame.Docs: "Set variables from JavaScript", a page on
setProperty(),getPropertyValue()andremoveProperty()withmount(),mountAsync()and<gss-scene>(whosesceneexists once it firesload), the values each syntax takes, and a live demo where a slider moves a sphere.@propertylinks to it, and lists every property a variable can go in,fogandtransform-originincluded.Animated gradients: a gradient in
colorchanges into another gradient of the same kind in@keyframes, on:hoverand:active(withtransition), its angle, center, stop positions and colors each moving on their own.Through a variable, like a registered
@property:linear-gradient(var(--angle), …)turns when@keyframeschanges--angle.The scene plays an animation:
scene { animation: … }animatesbackground, a color or a gradient, directly or through the scene's variables, on time or withscroll()andview().With
@scene { }andfloor: none, the scene becomes a flat, moving image.noise(), wherever a gradient goes: colors placed by a smooth 3D noise, like SVGfeTurbulence:color: noise(4 3, #1a1d2b, #3a7bff 60%, #ffffff).A scale, octaves,
turbulencefor sharp creases,seed, andatto move it. On an object, the noise is cut in its own space; in the background, it follows the view. It animates like a gradient: animateatand it drifts.Layers of background, like CSS:
background: noise(…), linear-gradient(…), #102040puts its layers over each other, the first on top.Their colors can be transparent (
transparent,#rrggbbaa,rgb(… / 50%),color-mix()under 100%), andbackground-blend-modeblends them with the 16 modes of CSS. Each layer animates like a gradient.mask-imagecuts holes in an object, like a CSS mask: a gradient or anoise()read on the object (from the front, or in 3D fornoise()), with transparent colors.Where it covers less than half, the surface is not there: the eye sees the inside of the object and what is behind it, in the reflections too, and the mouse goes through the holes.
mask-mode: luminancereads its brightness. It animates like a gradient incolor: moving the stops of anoise()dissolves the object.displace(<image>, <map>, <amount>)moves an image by a map, like SVGfeDisplacementMap, wherever a gradient goes (the color of an object or a material, the background and its layers,mask-image): red moves the image to the right and green down, by a share of its size, and anoise()map gives each channel a noise of its own, likefeTurbulence.Stripes moved by a turbulence make marble, a mask gets ragged edges, and animating the map makes the image flow.
opacity, like CSS: an object covers what is behind it as much as its opacity, a number or a percentage.Behind it, the eye sees its back face, what is inside it and what is behind it; on a group, it multiplies into each object. It animates, changes on
:hoverand is set from JavaScript. With shadows, the light goes through it like through stained glass.- Transparent colors on objects:
color: rgb(255 0 0 / 50%),#ff000080,transparent, or a gradient with transparent stops make the object transparent, likeopacity;filter: opacity()too, on objects and groups. Several lights, and colored lights:
@scene { light#bulb; }adds a point of light, placed like an object (translate, its groups, animations, a motion path) and set withcolorand the newintensity; it changes on:hoverthrough its group.The sun takes a color and an intensity (
light: -45deg 54.7deg #ffd27a 0.8),light: noneturns it off, and the scene can animate it.ambienttakes a color:ambient: 0.2 #9db4ff.Shadows:
scene { shadows: soft; }lets the objects cast shadows on the floor and on each other, from the sun and from every light of@scene, with a penumbra (soft) or sharp (hard);noneby default.The holes of
mask-imagelet the light through. The reflections show the objects without shadows.fogon the scene:fog: 6 18fades each object into the background behind it, from 6 to 18 units away from the camera; with a color,fog: #dfe7ef 4 16, the background becomes the fog too.Animatable with the scene's animation and its variables, and readable from
@propertyvariables. Off by default.texture: element(#card): a live image of an HTML element on an object, like CSSelement(), drawn by the browser with the page's CSS and fonts, and shown again each time it changes.The element goes inside
<gss-scene>, or inside the canvas given tomount(). Chromium only for now (HTML-in-Canvas); elsewhere the object keeps its color. A scene with an element is drawn with WebGL2. The docs have anelement()page, and their examples can carry the HTML they show. The playground has anhtmltab for the elements of the scene, and a share link carries them.
Fixed
A value computed by
calc()orsibling-index()that goes out of its range is clamped to it, like CSS, instead of being an error:color-mix(in oklab, #ff5a36, #3a7bff calc(sibling-index() * 40% - 20%))holds the last copies at 100%.The same goes for filter amounts, the x of
cubic-bezier(), a transition duration,animation-iteration-count,blend,ambient,corner-radius, the radii of a cone, the frost ofglass()anddpr. A value written as is outside its range is still an error.
0.0.3
Changed
The screen is no longer mirrored: +x is on the right, like CSS, and
rotate-yandrotate-znow turn like CSSrotateY()androtate()(clockwise).Gradients, conic gradients, textures,
pathandprismshapes and motion paths now appear as written, no longer flipped left to right. The defaultlightbecomes-45deg 54.7deg, so a scene withoutlightis lit as before, from the upper left. Every scene, example and showcase of the site was mirrored to keep its look. A scene written for an earlier version shows mirrored: negate the x oftranslateandcamera-target, the angles ofrotate-yandrotate-z, and the first angle oflightandcamera-angle.
- A misspelled function inside math names the function it was meant to be:
Unknown function slibling-index(): did you mean sibling-index()? - The playground's performance panel is closed by default; the
perfbutton or Alt+P opens it, and that choice is remembered.
Added
conic-gradient()andrepeating-conic-gradient(), like CSS:from <angle>,at <position>, stops placed with angles or percentages; in backgrounds, on objects and in materials.
Motion path, like CSS:
offset-path: path("…")orray(<angle>),offset-distance(a length or a percentage, animatable with@keyframes,:hoverand the scroll) andoffset-rotate(auto,reverse, an angle).The path stands in the object's xy plane like the
pathshape, so a tube and an object following it share the samed.
Scroll-driven animations, like CSS:
animation-timeline: scroll()(the scroll of the page or of the nearest scroll container, any axis) andview()(the scene crossing the screen).The progress of the scroll replaces the time; iterations, direction and easing still apply. In the playground and the docs, a slider over the scene stands in for the scroll.
:active, like CSS: the object pressed with the mouse or a finger, until the button goes up.It goes wherever
:hovergoes (groups, combinators,:has()), is drawn over the hovered state, and takestransition. On WebGL2 and WebGPU; scenes without:activecompile as before.
currentColor: the object's own color wherever a color is expected, like CSS: incolor-mix(),light-dark(), gradient stops, a material (metal(currentColor, 0.2)follows the animated or hovered color) and variables, read with the color of the object that uses them.
:not(<selector list>), like CSS: any selector inside, complex ones,:nth-child()and:has()included (cube:not(#g cube),group:not(:has(sphere))); it weighs like its most specific selector.Resolved at compile time.
Structural pseudo-classes, like CSS:
:nth-child(An+B [of S]),:nth-last-child(),:nth-of-type(),:nth-last-of-type()(odd,even,3,2n+1,-n+3…), and:first-child,:last-child,:only-child,:first-of-type,:last-of-type,:only-of-type.The copies of a
* nare siblings one by one, so:nth-child()counts likesibling-index(): in@scene { cube * 4; sphere; },cube:nth-child(odd)is cubes 1 and 3. Resolved at compile time, at no rendering cost.
Performance panel for WebGPU: FPS, frame/CPU percentiles, resolution and shader/pipeline preparation time, plus asynchronous GPU timestamps spanning picking, scene rendering and post-processing when
timestamp-queryis available.Profiling remains lazy until the panel is opened and works with automatic backend selection.
Dual GLSL/WGSL compilation for scenes, filter passes and media variants.
Native WebGPU runtime with textures, camera controls, animation, hover picking and post-processing;
mountAsync()selects WebGPU with a WebGL2 fallback or forces either backend. Existing synchronous APIs retain WebGL2. The playground includes backend selection and a WGSL tab;<gss-scene backend="auto">opts into WebGPU.
- The editor of the playground and of the reference suggests property names while you type, only those the rule can take (the scene, a shape, a group,
:hover, a face, a@keyframesframe), each with its syntax and description.
Every error of a compile is reported at once, not only the first: after an error, the tokenizer and the parser go on reading (to the end of the declaration, the element or the rule), then, once the text reads, every wrong property, value, variable or object is reported.
The playground and every "Try it" underline each one and write it under its line, the status bar counts them, and the Vite plugin lists each error with its line and column. One error is still thrown as a
GssError; several as aGssErrors, aGssErrorwhoseerrorslists them in the order of the text.
Fixed
A scene with a
filteron an object and a metal, jelly or glass material no longer fails to compile on WebGL2 (it rendered on WebGPU only).Reflections still show the object's filters.
0.0.2
Added
Versioned npm, Vite and CDN installation guides, under Installation → Embedding a scene.
Ship a standalone
lib/embed.jsin npm, with thegss-lang/embedentry point and a pinned jsDelivr URL.- Package version in every site footer; homepage installation cards for npm, Vite and CDN.
- Modern colors:
hwb(), Lab/LCH, OKLab/OKLCH,color(),color-mix(),light-dark()andcontrast-color()(decision 79). - Additional math: inverse trigonometry,
sign(),round(),mod(),rem(),hypot(),log(),exp()andprogress()(decision 78). steps(),step-startandstep-endeasings, including animation and transitions (decision 80).- Deterministic compile-time
random()and conditionalif()values (decision 81). - Linear, radial and repeating gradients on scene backgrounds and objects, including reflections (decisions 81–82).
- Filters on scenes, objects and groups: color adjustments, grain, blur and bloom, with additional rendering passes where needed (decisions 83–84).
- Camera, perfume, watch and orbit sequencer scene files; regression tests for every watch media variant, including WebGL compilation.
Child (
>), adjacent sibling (+) and subsequent sibling (~) selectors, including mixed chains,:hover, relative selectors inside:has(), empty groups and multiplied objects.Nested style rules with
&are still unsupported. (49309f0)@mediarules evaluated by the browser, including color scheme and reduced motion.The compiler prepares variants for up to four distinct queries; the runtime switches variants while preserving the camera.
animation: nonecan disable animation in a matching query. (46ddf76)Animation delay, iteration count, direction and fill mode, plus six animation longhands.
Animations continue to loop by default for compatibility. (b1850fc)
scene { dpr: auto | max | <number>; }to control rendering pixel density.(ec54102)
Documentation copying as Markdown or plain text.
(50b93a6)
Changed
- The playground's performance panel (frame, GPU and CPU time, real pixels, shader build time) ships on the public site, behind the
perfbutton or Alt+P. - Accessible collapsible documentation groups with keyboard controls, visible focus, session-persisted expansion and automatic opening for the current page.
- Documentation sidebar grouped by topic, with alphabetical entry sorting and explicit numeric order overrides; reading order and breadcrumbs follow the same structure.
Pre-render the home page, documentation and showcase so their content is available without JavaScript, and remove their rendering dependencies from client bundles.
(9bb1b83)
Give reference examples descriptive names in the documentation and playground menu.
(bfd5f4e)
Refine benchmark image comparisons to tolerate isolated GPU rounding differences and one-pixel edge shifts within a 0.05% image budget.
(f713c9e)
Performance
Skip whole groups of at least three objects when their bounds cannot improve the nearest distance; apply this only to eligible groups of plain unions.
(83ad25a)
0.0.1
First tagged release of gss-lang, covering development from September 25 to October 1, 2026. (Release commit)
Language and rendering
- Compile a GSS scene into a single WebGL2 fragment shader using signed-distance-field raymarching.
- Declare shapes in
@scene, multiply them with* n, and nest them in groups with inherited transforms. - Style objects with shape, class, ID, universal, list and descendant selectors; resolve specificity, source order and
!important. - Draw cubes, spheres, tori, cylinders, cones, capsules, planes, SVG path tubes and extruded prisms, including SVG arcs and filled contours.
- Translate, rotate and scale objects; combine their distance fields with union, subtraction, intersection and blending.
- Use matte, metal, jelly and glass materials, including reflections, refraction and frost styles.
- Animate translation, rotation, scale and color with
@keyframes, computed in the shader. - Use custom properties and
var(), including inherited variables, variable references, cycle detection and animated variables. - Compute
calc(),min(),max(),clamp(),abs(),sqrt(),pow(),sin(),cos()andtan()at compile time, withsibling-index()andsibling-count()for repeated objects. - Write colors as hex,
rgb(),hsl()or CSS named colors. - Apply image textures, per-face selectors (
::face(),::top,::bottom), pixelated sampling and texture sizing, with up to 16 images per scene. - React to object and group
:hoverthrough a picking pass and a separate hover cascade. - Configure the floor, background, lighting and orbit camera.
Embedding and tools
- Publish compiler and runtime entry points, a runtime-only entry point and a Vite plugin, with TypeScript declarations and an Apache-2.0 license.
- Embed scenes using
mount()or<gss-scene>, with visibility-based sleeping, reduced-motion support, optional camera controls and relative texture URLs. - Provide a CodeMirror playground with examples, shareable URLs, a GLSL tab, located errors and a rendering status bar.
- Export scenes to Shadertoy, mapping textures to
iChannel0–iChannel3and reporting scenes that exceed its four-image limit. - Generate the reference from a shared feature registry, with editable live examples, syntax highlighting and Algolia DocSearch.
- Provide a comment-preserving GSS formatter and a VS Code extension with syntax highlighting and formatting.
- Add the public website, brand assets, showcase, capture generation and embedding examples.
- Add a development profiler and a benchmark that compares rendering and performance across commits using alternating rounds.
- Test documented examples by compiling their generated shaders in Chromium/WebGL2.
Performance and fixes
- Emit only the shader helpers and materials used by a scene.
- Read hover picking results asynchronously to avoid blocking the CPU on the GPU.
- Use bounding spheres to skip unnecessary
pathandprismdistance calculations. - Fix shared search initialization, editor selection visibility and error messages for negative computed radii.
- Keep camera spinning off by default.