Iris
A meditative browser painting instrument: Stable Fluids in WGSL, Kubelka–Munk pigment mixing, and share links that carry a seed and gesture instead of an image.
A sheet of cotton cardstock, and a breath every 15s that lays down pigment whether or not you do. You work it with one pointer.
The paint is finite. When the wells run dry the flow decays, the image sets the way drying paint sets, and what is left is yours.
What it does






What it makes
Each from its own session and palette.







The solver
Stable Fluids, written as WGSL compute passes rather than assembled in a tool:
velocity advect → forces → divergence → jacobi → projectpigment advect → material → resolve → film → dry → bake
Four pigment concentrations advect through the same velocity field and are resolved with Kubelka–Munk, so overlapping pigments subtract the way paint does instead of averaging the way pixels do. Blue over yellow makes green.

Texture acts on the field as it sets. Granulation settles heavy pigment into the paper's tooth, blanching thins a drying wash, the rim darkens as an edge dries.

The instability is deliberate. Saffman–Taylor viscous fingering is what makes an edge break into fronds instead of spreading as a disc.
What a share link carries
A link carries the session seed and the gesture recorded at 10 Hz, not an image.
The seed reproduces the piece's own chance: every pulse's noise field, every
breath's rotation; everything stochastic routes through one seeded generator,
because one stray Math.random makes the recording a lie. The recording
reproduces yours, interpolated up to frame rate on playback.
What comes back is not the same image and is not meant to be. GPU float is not bit-identical across vendors and the fingering amplifies any divergence, so a link regrows a painting from the same conditions rather than copying one.

Status
498 unit tests and 55 browser specs driving a real GPU, covering the pigment ledger, the drying curve, the replay clock, the seeded generator. Nothing reaches the piece until a test shows it making the effect it was supposed to make.
One test runs the same capture twice, once as fast as the machine will give frames and once with 0.25s thrown away between every one, and requires the two to come out identical. This is what lets the piece be filmed frame by frame on any machine.
- Role
- Creator. Solver, pigment model, session and replay, interface
- Built with
- WebGPU · WGSL compute · TypeScript · Web Audio · Vite
- From
- A TouchDesigner fluid study, rewritten to run in a browser
- Source
- Private for now.