The snow is a heightfield first. A plane, 420 units across, 256 segments, displaced on the CPU, and then a light map is traced over that height so the ridges know where the sun is.
This is one HTML file on Three.js 0.186.1 and WebGPU. The study I started from shaded the surface in GLSL, with its own bloom. This copy keeps the height and the trace, and the surface is a node material. I use AI to write a lot of the TSL. How flat a snowfield is allowed to stay, and that the sparkle dies in the shade, come from 25+ years of JavaScript, game development, modding and level design.
The height
Every sample is domain-warped before the ridges are added. Two low octaves bend the coordinates, and every later feature is evaluated in that bent space, so the drifts curve. A broad fBm sets the mass, ridged noise stacks the spines, a tighter ridge lays the drifts, and a last fBm is the grain. The rim is pulled back down, so the horizon reads flat.
The hash is an integer mix. A sine hash lines up along diagonals on a field this wide, and you can see it.
The light map
One texture, the same grid as the mesh. Red is sun visibility. From each texel the trace walks toward the sun and keeps the steepest slope it meets. If that slope rises above the sun, the texel is in shade, with a soft edge where a ridge only grazes the light. Green is horizon occlusion, eight directions, jittered so the bearings do not leave a star. Both get a blur. The texture row is flipped on the way in, because the plane's v runs the other way from the grid.
Moving the sun waits a moment, then traces a coarse pass, then a fine one. New terrain rebuilds the height, the occlusion and the shadow together. The sliders for polish, melt, relief and sparkle only touch the material.
The surface
The node material reads that texture. Sun visibility and occlusion darken the albedo. Steep ground, where the normal stops facing up, picks up rock. Wind direction comes from a slow noise, and that direction streaks the roughness, which is what the polish slider is driving. Melt drops the roughness further and dulls the glint. Sparkle is a cell noise in the emissive, multiplied by the sun map, so a shaded hollow does not glitter.
Drag right and the field turns with your hand. Phi here is the elevation off the horizon, so x is sine of theta and z is cosine, and theta decreases as the pointer moves right. The camera will not fly through the snow. It stays a little above heightAt.
The frame is that file. While it says it is tracing the sun, the page is still on the CPU. After that, the fps line in the panel is the only readout.
WebGPU only, on Three.js 0.186.1, with no WebGL fallback. If the browser has no WebGPU, the page stops. Open it through a server, because file:// will not load the modules. php -S localhost:8000 or npx serve in the folder is enough.
- Open it on its own
snow.html, the project-
SKILL.md
If you change the grid, change the light map with it. A shadow traced on a different resolution than the mesh will slide off the ridges the moment you orbit.