Field notes on a pocket universe
How a two-line rule becomes weather when you hand every pixel to the GPU — and how weather becomes a painting.
In 2021 I built, by hand, a website called Doors of Delirium. It fills the screen with slow, colorful weather — rings of color crawling across a dark field, threads of it drawing spirals and roses and forking lightning. None of it is video, and none of it is noise. Underneath is John Conway's Game of Life: one rule about counting neighbours, applied to every cell of a grid, over and over. This piece is about the distance between that rule and what's on the screen.
The plan: first the rule itself, on a grid small enough to watch single cells live and die. Then the machine — why the obvious program stops keeping up, and how a graphics card runs the same rule on a few million cells at once. Then the dressing that turns a simulation into the site: trails, palettes, and the drawing machines that seed the grid.
Count your neighbours, live or die by the count
The universe is a grid. Each cell is either alive or dead. Time passes in ticks, and at every tick, every cell looks at its eight neighbours — the cells touching it, including diagonals — and counts how many are alive. Then the rule:
A dead cell with exactly three live neighbours becomes alive. A live cell with two or three live neighbours stays alive. Every other cell is dead on the next tick. Too few neighbours and a cell starves; too many and it's crowded out. That's all the physics there is.
There are no moves. You choose a starting arrangement, and from there the board plays itself. The standard notation for the rule is B3/S23 — born on 3, survives on 2 or 3 — and the notation matters, because it says the whole rule is two small sets of neighbour counts. Two small sets fit in two small integers. We'll come back to that in §6.
One more detail, easy to miss: all cells update simultaneously. The whole next generation is computed from the whole current one before any cell changes. No cell ever sees a half-updated board. Hold on to this one too — it's the reason the fast version of this program needs two copies of the universe.
The five cells on the board below are a famous arrangement called the R-pentomino. Step it by hand a few times, then let it run.
The fates toggle is worth a moment. It marks, on the current board, everything the next tick will change: sienna for cells about to die, gold rings where births are about to happen. Stepping is just those marks cashing in, all at once. When a pattern stops changing, the marks disappear — that is what stable means here.
Still lifes, oscillators, and things that travel
Run the rule for a while and you stop tracking cells and start recognizing shapes — the same few arrangements, turning up again and again. The classic bestiary sorts them into three families.
Still lifes are fixed points: arrangements where every live cell has two or three live neighbours and no dead cell has exactly three. The rule looks at a still life and changes nothing, forever. The block — four cells in a square — is the smallest; the beehive and the loaf are the ones you'll see everywhere once you start looking.
Oscillators cycle. The blinker — three cells in a row — flips between horizontal and vertical every tick, period two. The toad is another period-two flicker. The pulsar is the big one: forty-eight cells that expand and contract on a three-tick cycle.
Spaceships are the third family, and the strangest. A glider is five cells that, after four ticks, re-form exactly — one cell diagonally displaced. No cell moved; cells only lived and died in place. The pattern moved, and it will keep going until it hits something. The lightweight spaceship (LWSS) does the same trick horizontally.
It goes much further than this. Glider streams can be collided in ways that behave like logic gates, and from logic gates, with enough patience, people have built memory, arithmetic, and entire computers inside the grid — the Game of Life can compute anything a computer can. I'm not going to walk through that construction; the history section points at it. The fact I want from it is smaller: the rule contains none of this. Nothing in B3/S23 mentions a glider.
Seed it at random, watch it settle
So far the board has been arranged by hand. The other way is to seed it at random — light up a quarter of the cells and let the rule take over. The first generations are chaotic, big fronts of birth and death sweeping the grid. Then the activity burns down, and what's left is the bestiary again, uninvited: blocks, beehives, blinkers, and now and then a glider escaping the wreck on a diagonal. The standing term for the leftover debris is ash.
This is what people mean by emergence, and the soup is where the word stops being abstract. The rule knows only neighbour counts. Nobody put the bestiary in; it is what the rule's fixed points and cycles happen to look like.
Watch the population readout as the soup settles: a steep early drop, then a long tail while the last unstable regions work themselves out. On a grid this size, the whole thing is over in about a minute. The natural next move is to ask what the same process looks like on a few million cells — and the obvious program for that has a problem.
The obvious program, measured honestly
The program running every exhibit so far is the one you would write first: two arrays, a loop over every cell, count the eight neighbours, apply the masks, swap the arrays. Nothing clever. Its cost is easy to state — a fixed amount of work per cell, times the number of cells, every generation.
Notice what that cost does not depend on: how many cells are alive. The loop visits every cell to find out whether anything happened there. An empty board costs the same as a boiling one. The program pays for silence.
The budget it has to fit inside is a frame: at sixty frames a second, about 16.7 milliseconds. The exhibit below runs the same stepper at four sizes and times it — the step alone, drawing excluded, averaged over the last forty generations. I'm not going to quote numbers; the readout is measuring your machine.
Whatever your numbers are, their shape is the point: quadruple the cells, roughly quadruple the time. The site wants one cell per pixel of a full screen — a few million cells — at sixty generations a second, and this loop was struggling at a fraction of that. There are much cleverer CPU programs (the history section points at HashLife, which is startling), but they trade the simplicity away. The interesting move is the one that keeps the program dumb and changes where it runs.
The grid is an image that computes itself
Change how you look at the board. A grid of cells, each holding a small value, laid out in rows — that is an image. A cell is a pixel. Alive is one color, dead is another. This is not a metaphor; it is a storage decision, and it is the whole move.
Because there is one machine in the computer built for exactly this shape of work: the GPU runs a small program at every pixel of an image, all of them effectively at once. That program is called a fragment shader. Normally it computes lighting or blur; there is nothing stopping it from computing the rule. Each pixel's shader reads its eight neighbours from the image and writes the pixel's next state. One shader pass — however many pixels — is one generation.
One catch, and §1 already handed us the answer. The shader cannot write into the image it is reading — and simultaneity says it shouldn't want to: the whole next generation must be computed from the whole current one. So there are two images. Read from A, write into B. Next generation, read from B, write into A. The CPU program was quietly doing the same thing with its two arrays. Graphics programmers call it ping-ponging.
The swap costs nothing. No cells move, no memory is copied; two labels trade places. The universe alternates between two copies of itself, and at any moment one of them is the truth and the other is scratch paper.
Eight lookups, two masks, every pixel at once
Here is the heart of the site's simulation shader — the program that runs at every pixel, once per generation. Reading a neighbour is a texelFetch: fetch the texture value at a coordinate, offset by one of the eight directions.
int numNeighboursAlive = isAlive(coordinate + ivec2(-1, -1)) + isAlive(coordinate + ivec2( 0, -1)) + isAlive(coordinate + ivec2( 1, -1)) + isAlive(coordinate + ivec2(-1, 0)) + isAlive(coordinate + ivec2( 1, 0)) + isAlive(coordinate + ivec2(-1, 1)) + isAlive(coordinate + ivec2( 0, 1)) + isAlive(coordinate + ivec2( 1, 1)); bool keepAlive = (uSurviveMask & (1 << numNeighboursAlive)) != 0; bool born = (uBirthMask & (1 << numNeighboursAlive)) != 0;
The two masks are §1's promise kept. A rule like B3/S23 is two sets of neighbour counts, and each set is stored as one small integer with one bit per count: birth on three is 1 << 3, which is 8; survive on two or three is 12. Testing the rule is a shift and an AND — and swapping in an entirely different universe means changing two uniforms. The site ships four: Conway's B3/S23, High Life (B36/S23), Maze (B3/S12345), and Day & Night (B3678/S34678).
The plate below is the whole machine — this shader plus everything the next two sections unpack, running a complete scene from the site: a rolled palette, an afterglow, drawing machines seeding the grid, a slowly drifting camera. One cell per pixel; the cell count and the generations per second are in the readout. The rule toolrow does exactly what §1 promised: it swaps the two mask integers, mid-flight, without touching anything else. Watch Maze freeze the current scene into corridors.
At this size the character of the thing changes. You stop following any cell, or any glider, and start seeing fronts and textures — regions of churn with edges that advance and stall. This is the scale the site lives at, and it is where Life starts to look like weather. The rule is now fully accounted for. What is not yet accounted for is everything else you just saw: where the color comes from, why the dead leave trails, and what is drawing those spirals. None of it touches the simulation shader.
A channel for the living, a channel for the dead
The state texture is an image, and an image has channels. The simulation only ever uses one: a cell is alive if its red channel is high, and the eight texelFetches in §6 read nothing else. That leaves the green channel free, and the site spends it on memory. While a cell is alive, the shader writes its green channel high too. When the cell dies, green is not zeroed — it is reduced by a fixed amount, uDecay, every generation, until it fades out. Green answers one question: how recently was this cell alive.
The memory never feeds back. The rule doesn't read it, so the trails are ornament in the strictest possible sense — remove them and the simulation is bit-for-bit identical. The render pass simply treats a cell's brightness as whichever is stronger, the living red or the fading green.
The decay value sets the length of the wake, and the site names its presets: God mode (0.05 — about twenty generations of trail), Life on Mars (0.01), Comet (0.005 — wakes two hundred generations long, where everything smears into tails), and Alive in a simulation (1.0 — no memory at all, the setting §6's arithmetic section quietly ran under).
Color is the render pass's other job, and the trick is that hue has nothing to do with the cells. Brightness comes from the state; color comes from where the cell is. The distance from the grid's centre indexes into a palette stored as a texture one pixel tall, and a slowly drifting offset — the pulse — pushes that index over time, so rings of color crawl inward or outward through the population. The palettes themselves are a handful of anchor colors expanded into a smooth ramp by interpolating in OKLab, a color space where the midpoint of two colors looks like the midpoint; interpolate naively in RGB and the ramp sags dark and muddy between saturated anchors.
The drawing machines and the kaleidoscope
One question is left: where do the scenes come from? The simulation doesn't seed itself. There is a third texture — the spawn buffer, one byte per cell, uploaded fresh every frame — and any nonzero byte in it forces that cell alive. Everything that enters the site's universe enters through this buffer, including your finger when you draw on the plates above.
What writes into it is a set of drawing machines. Each scene rolls one family of particles that spend tens of seconds tracing paths into the buffer: splines threaded through random control points, rhodonea roses, phyllotaxis — seeds placed at the golden angle, the sunflower's arrangement — damped Lissajous harmonographs, walkers that wander outward and fork like lightning, streamlines carried along a flow field. The names are honest. They are pen plotters, and the Game of Life is the paper that fights back.
One implementation detail is worth naming, because every version of this system hits it: a fast particle crosses many cells between two frames, and plotting only its current cell draws a dotted line. The cure is to draw the segment from where it was last frame — ribbons instead of dots. The site caps the segment length, so a particle that teleports (a delayed start, a mode flip) spawns a point instead of a streak.
Then the kaleidoscope: every mark a machine makes can be repeated around the grid's centre — rotated copies at two, four, up to eight axes, sometimes mirrored. One wandering spline becomes a rose window. Your own strokes get the same treatment, which is why drawing on the §6 plate feels like more hands than yours.
The exhibit below runs the machines with the simulation switched off — pen on paper, nothing fighting back. These are the same objects, from the same code, that seeded every plate above. Pick a family; the symmetry slider applies to new marks as they land.
Last, the camera. The drifting rotation and the slow zoom on the site never touch the simulation either — they are a coordinate transform applied where the render shader samples the state texture, which is why §6's board sometimes sits tilted in its frame. The universe holds still; the window onto it moves.
That is the entire machine. A rule stored in two integers, run by a shader over a pair of ping-ponging textures; a memory channel for the dead; color by distance from the centre through an OKLab ramp; pen plotters seeding it all through a spawn buffer, their strokes multiplied by a kaleidoscope; and a camera that is only ever a change of coordinates.
So, to close: the whole console. Every knob the piece has introduced, on one live universe — the rule, the decay, the palette, the pulse, the drawing machine, the symmetry. The site rolls all of these dice itself, once per scene. Here they are yours.
The site is this console running unattended, rolling its own dice once per scene — go let it. And scroll back to the top with all of it in hand: the wall of universes up there should read differently now.
Where this game came from
John Conway devised the Game of Life at Cambridge in the late 1960s, and it reached the public in October 1970, in Martin Gardner's "Mathematical Games" column in Scientific American. The column offered a wager: Conway conjectured that no pattern could grow without bound, and put fifty dollars on anyone proving or disproving it before the end of the year.
The money was gone within weeks. In November 1970, Bill Gosper and his collaborators at the MIT Artificial Intelligence Laboratory found the glider gun: thirty-six cells that fire a fresh glider every thirty generations, forever — the first pattern with unbounded growth. Gun-built glider streams became signals, collisions became logic, and the universality construction — the Game of Life computing anything a computer can — was sketched in Winning Ways in 1982.
The B/S notation generalizes the rule, and §6's other three universes each have a history. High Life (B36/S23) was devised by Nathan Thompson in 1994, and is loved for its replicator — a small pattern that copies itself. Day & Night (B3678/S34678) is Thompson again, in 1997, studied in depth by David I. Bell; its name comes from a symmetry, in which inverting every cell of a pattern inverts its entire future. Maze (B3/S12345) freezes growth into corridors; I have not found a firm attribution for it.
Two honest footnotes to §4's wall. First, the clever CPU program exists: HashLife — Gosper again — uses quadtrees and memoization to leap regular patterns forward by astronomical numbers of generations ("Exploiting Regularities in Large Cellular Spaces," 1984). It trades the dumb loop's generality for staggering speed on structured patterns. Second, running cellular automata in fragment shaders is old folklore by now; the site's version is about the plainest possible form of it, which is why it fit in an article.
Doors of Delirium itself was built by hand in 2021. Everything in this piece runs on the site's own code rather than a reimplementation — the plates above are the machine itself, not a diagram of it.
References
- Gardner, M. (1970). Mathematical Games: The fantastic combinations of John Conway's new solitaire game "life". Scientific American, 223(4), 120–123.
- Berlekamp, E.R., Conway, J.H., & Guy, R.K. (1982). Winning Ways for Your Mathematical Plays. Academic Press.
- Gosper, R.W. (1984). Exploiting Regularities in Large Cellular Spaces. Physica D, 10(1–2), 75–80.
- LifeWiki (conwaylife.com) — the standing encyclopedia of Life patterns, rules, and their discoverers.