Helix Noise · GPU smoke · obstacle · curl of a baked potential

Smoke around an obstacle — the whole sim on the GPU

The velocity is never baked directly. Instead field.withBoundary(sdf) wraps the field against an obstacle, bounded.bakePotential3D(64) stores the vector potential A (rgb) and the signed distance (alpha) in one RGBA16F volume, and the advection shader reconstructs the velocity on the fly as u = ∇×A — a curl of the trilinear samples. Because it is a curl, the interpolated velocity is discretely divergence-free (a directly-baked velocity leaks divergence and makes smoke pool or vanish); the same alpha channel carves the solid out of the smoke and shades its surface in the raymarcher. Dye advects entirely in 3-D textures — no CPU per-frame work. Drag to orbit, scroll to zoom, toggle draw to inject smoke.

building field…
drag to rotate · scroll / pinch to zoom
Helicity+0.70
left-handed −1right-handed +1
The wake corkscrews with a single handedness as the flow wraps the obstacle.
Spectral slope1.9
fine · raggedlarge · silky
Steep = a few silky folds; shallow = ragged multi-scale billows.
Phase coherence45%
diffuseorganised
Same spectrum, phases random → structured: haze becomes distinct rolling cells.
Obstacle
Colour
speed1.0×
rise0.45
emit1.0×
zoom1.0×
left emitter right emitter drawn smoke obstacle (from the SDF channel)

Why the potential, not the velocity

Trilinearly interpolating a baked velocity destroys its divergence-free property — the reconstructed field gains O(voxel²) divergence, which shows up as smoke piling into some cells and thinning out of others. Baking the vector potential and taking a finite-difference curl in the shader keeps the velocity discretely divergence-free to machine precision, because the discrete curl of an interpolant has exactly zero discrete divergence. The bakePotential3D() call hands you rgb = A and alpha = the obstacle's signed distance, so one texture drives both the flow and the solid.

Honest limits

64³ is demo resolution and the field is frozen per seed (smoke moves through it; the obstacle is respected kinematically — free-slip, no wake shedding or pressure feedback, because this is an authoring field, not a solver). Semi-Lagrangian advection adds numerical diffusion — the smoke's softness. Needs WebGL2 + EXT_color_buffer_float; the canvas says so if either is missing.