Helix Noise · volumetric smoke · WebGL2 raymarch
field.bake3D()A 48³ voxel smoke volume advected through a Helix Noise field and rendered by
volume raymarching with self-shadowing. The velocity comes straight from the library:
field.bake3D(48) returns an RGBA grid (rgb = velocity), the three smoke channels advect
semi-Lagrangian each frame, upload as a 3-D texture, and a fragment shader marches it. Push
helicity to ±1 and the plumes corkscrew with a single handedness — real 3-D chirality in smoke.
Drag to orbit, scroll to zoom; toggle draw to pour smoke with the pointer.
Every knob change calls field.set({ slope, helicity, coherence, seed }) and then
field.bake3D(48) — one RGBA Float32Array whose rgb is the divergence-free
velocity at each voxel. The three smoke channels advect semi-Lagrangian on the CPU against that grid,
upload as an RGBA8 3-D texture, and a WebGL2 fragment shader raymarches it: 72 steps, front-to-back
compositing, one shadow tap per step for the self-shading that reads as smoke rather than fog. Nothing
here reaches into the field internals — it is all the public API. The colour gradient row swaps a
256-px 1-D LUT (a second texture) into the shader, tinting each sample by height and density — the same
trick a VFX artist wires as a smoke ramp.
48³ is demo resolution — production is 128³–512³ baked to OpenVDB with the same call. The velocity field is frozen per seed (the smoke moves through it; re-baking each frame is cheap enough for slow morphs but off here). Semi-Lagrangian sampling adds numerical diffusion — visible as the smoke's softness. Requires WebGL2; the canvas says so if it's missing.