Draw My Canvas / studio

Turing Bloom

Two chemicals share a square. One is fed in, the other eats it and makes more of itself. Nothing tells them to grow a reef.

Plate 17reaction-diffusion

Waiting for the animation above to load…

What lands in the file, and what the width does

One click writes whatever the animation is drawing at that moment to a PNG, with the drawmycanvas.com mark drawn into the picture rather than laid over it. Leave the width box empty and you get the stage exactly as your browser rasterised it — your window’s width times its device pixel ratio, which is about 1,600 px across from a 1280‑px window on a HiDPI laptop and about 353 px from a 390‑px phone.

Type a width instead, or take a preset, and the frame is redrawn into a canvas that wide: the height follows the stage’s own shape and the mark scales with it. The stage is 16:9 on a wide window and 4:3 below 560 px, so a width of 1200 saves 1200×675 on a laptop and 1200×900 on a phone. 1200 px is the width Open Graph and X link cards are cut from — the canonical card is 1200×630, and a card crops the extra height rather than letterboxing it.

Honest limits. Asking for more pixels than the stage was drawn at resamples pixels that were never drawn: a 1920‑px file exported from a 353‑px phone stage is bigger, not sharper. A very large width is a real memory allocation and a browser is allowed to refuse it; when that happens the line above says so plainly and nothing else on the page changes. PNG only — no JPEG, no WebP, no animated export. And nothing is uploaded: the picture is assembled in your own browser, so no frame of this plate ever reaches us.

Live on an HTML canvas · vanilla JavaScript · no dependencies Open fullscreen

About this piece

This is the Gray-Scott model, a two-chemical reaction run on a 180×180 grid. Chemical A is fed into every cell from outside; chemical B is drained out of every cell. The only thing that happens between them is one autocatalytic reaction — A + 2B → 3B — so B consumes A and turns it into more B. Written out, with lap() the local Laplacian:

dA/dt = DA lap(A) − A·B² + F (1 − A)
dB/dt = DB lap(B) + A·B² − (K + F) B

That is the entire program. There is no rule anywhere that says “branch”, no list of shapes, no randomness after the first frame. The reef is what those two lines do when you iterate them 360 times a second.

Why two diffusion speeds is the whole trick

Alan Turing wrote this idea down in 1952, in The Chemical Basis of Morphogenesis, his last published paper. His claim was counter-intuitive enough that it took decades to be taken seriously: a chemical mixture that is perfectly stable when you stir it can be made unstable by adding diffusion, and the instability has a preferred size. Spread it out and it does not smooth over — it separates into spots and stripes at a wavelength set by the rate constants.

The mechanism has a name now: local activation, long-range inhibition. B is the activator; it makes more of itself wherever it already is. A is what B needs, and A diffuses faster — here exactly twice as fast, DA = 1.0 against DB = 0.5. So a patch of B grows, and while it grows it drains A out of the ring around it faster than B can spread into that ring. The patch is fenced in by its own hunger. Set DA = DB and the whole picture dissolves into a flat wash within a few hundred steps; the ratio, not the reaction, is what makes a pattern.

The (F, K) plane, and why this corner

Two numbers select the behaviour: the feed rate F and the kill rate K. John Pearson mapped that plane in 1993 and found more than a dozen qualitatively different regimes packed into a region roughly 0.01 wide. Three of them are worth naming, because the code here is identical for all three and only the constants differ:

  • Coral, F = 0.037 / K = 0.06 — what this plate runs. Fronts advance, tips split in two, and a branch stalls when it runs into the depleted zone around its neighbour. The result keeps rearranging for as long as you leave it open.
  • Mitosis, F = 0.0367 / K = 0.0649 — a shift of about one part in a thousand. Blobs stay separate, grow to a fixed size, and divide.
  • Maze, F = 0.029 / K = 0.057 — stripes of a single width that lock into a labyrinth and then stop moving.

Most of the plane is dead. Push K a little too high and B is drained faster than the reaction can replace it, so every seed fades and the field goes uniformly A = 1 — a blank plate, not a subtle one. That is a real cliff, not a gradient: at F = 0.037 the pattern survives to roughly K = 0.065 and is gone by 0.07.

How this plate is built

Two pairs of Float32Arrays of 32,400 cells each, swapped every step so no cell ever reads a half-updated neighbour. The Laplacian is the standard nine-point stencil — 0.2 on each edge neighbour, 0.05 on each corner, −1 at the centre. Those weights sum to zero, so a flat field never drifts, and they are symmetric, so the equations have no preferred direction on the grid. Integration is forward Euler at dt = 1, which for DA = 1.0 sits exactly at the stability limit of that stencil; a larger diffusion constant at this step size would blow up rather than draw.

Both axes wrap. A reflecting edge would pin an advancing front against the wall and grow a visible frame around the picture; on a torus the reef runs off one side and back in on the other.

Seeding is the part that took the most care. Gray-Scott from a single central drop shows an almost-empty plate for several seconds — a blank first paint, a blank screenshot, a blank share card. So the field is seeded with 36 small blobs on a jittered 6×6 lattice covering the whole square, each 2 to 4 cells in radius, about 4% of the field in total; and then it is advanced 360 steps before the first frame is ever drawn. By then the fronts have met and the picture is a picture. The jitter comes from an integer hash rather than Math.random(), so the bloom you see on load is the same one everybody else sees. Click the canvas and it grows a different one, deterministically — run 3 is the same reef on your machine as on mine.

Colour is not painted, it is read. Each frame writes the concentration of B into a 180×180 ImageData through a four-stop ramp taken live from this site’s palette: page background where nothing grew, then the deep cobalt, the body cobalt, and the coral accent only on the brightest crests. B settles into about [0, 0.34] for this (F, K), so the ramp is stretched over that window instead of over [0, 1] — otherwise the whole reef would sit in the bottom third of the gradient and read as one flat wash. Switch this page between light and dark and the same field repaints as ink on paper.

What to look for

Watch a single tip. It advances, thins, and splits into two — that is the Turing wavelength asserting itself: a front wider than the preferred spacing cannot stay a single front. Then watch what happens when two branches approach each other. They do not merge and they do not cross; they stop, a cell or two apart, held off by the trough of used-up A between them. Every gap in this picture is that trough.

Give it a minute and look again. Unlike a maze or a mitosis field, the coral setting never fully settles — branches are still being pushed around by their neighbours long after the frame looks finished.

Honest limits. This is a simulation of an idealised chemistry, not of any real organism; the resemblance to coral or to a leopard’s coat is a family resemblance between mechanisms, not a model of either. The grid is genuinely 180 cells across whatever your screen is, so on a wide stage each cell is about 7 pixels and the smoothness you see is the browser’s bilinear upscale, not extra detail — zoom in and you will find the lattice. The square field is scaled to cover a rectangular stage rather than stretched, which keeps the shapes round at the cost of cropping the top and bottom of the reef on a wide window. And it is arithmetic, not chemistry: forward Euler at dt = 1 is a coarse integrator, so the exact pixel values drift from a finer solve even though the pattern does not. Reduced-motion visitors get a settled still frame — the field warmed 1,260 steps and drawn once — because a frozen young field would be an honest picture of nothing much.

Curious how the loop and canvas fit together? Read how it works →

More from the gallery

All 22 animations in the gallery →