← All studies MVS Solves  /  Study 01  /  INK
WebGL2 unavailable — this study needs it.
Fig. 01 — advection

Ink knows where it is going.

Dye advected through a divergence-free curl-noise field. Two framebuffers ping-pong every frame: sample where the fluid came from, fade a little, inject under the cursor. The curl of a noise field cannot converge or diverge, so it flows like a fluid without ever solving for pressure.

Drag to bleed
Double-click to clear
RGBA8 · half-res · 4-octave fBm
MVS Solves ↗
Study 01 — advection

The ink runs on a desktop.

A live fluid simulation — dye advected through a divergence-free field, two framebuffers ping-ponging every frame. It wants a cursor to bleed into and a GPU that is not sharing a battery.

Below is how it is built.

Best on a desktop — bring a cursor ↗
Engineering note

Fluid without a pressure solve

The constraint

A real fluid simulation enforces incompressibility with a pressure projection — typically 20–40 Jacobi iterations per frame, each its own render pass. That is a serious frame budget for what is, here, a decorative background.

Why the obvious route fails

Full stable-fluids gives physical accuracy nobody looking at a marketing site can perceive, and spends most of the GPU getting there. On a mid-range phone it simply does not hold 60fps.

What I did instead

The curl of a scalar field is divergence-free by construction — ∇·(∇×F) = 0 identically. Advect dye through curl-noise and you get motion that cannot converge or diverge, so it behaves like an incompressible fluid without ever solving for pressure. Two framebuffers ping-pong: sample where the fluid came from, fade, inject under the cursor.

The trade-off I accepted

This is a visual fluid, not a simulation. There is no obstacle interaction and no true boundary response — you cannot put a wall in it and expect the flow to react correctly. For a background, that is the right trade. For a physics tool, it would be the wrong one.

Velocity field
2 curl samples, 4-octave fBm each
Simulation
Half resolution, RGBA8, ping-pong FBO
Passes per frame
2 — advect, then display
Display
Subtractive; density removes light from paper
Dependencies
None. Raw WebGL2
Degradation
No WebGL2 → static field, layout intact