Draw My Canvas / studio

Game of Life

John Conway’s Game of Life on a grid that wraps at its edges. A third of the cells start alive at random, and two rules decide everything after that. Newborn cells are coral, survivors are cobalt, and every death leaves a ghost that fades over eight generations. Watch the noise cool into blocks, blinkers and gliders; when the grid starts repeating itself, it is wiped and seeded again.

Plate 31b3-s23

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. For a big file that is sharp, use Save as wallpaper: it draws the plate again from scratch at exactly 1179×2556 or 1290×2796 (phones), 1920×1080, 2560×1440 or 3840×2160 (4K), so every line is rasterised at that size and the shape is the screen’s, never stretched. Because the plate restarts, a wallpaper is a fresh run of it rather than the exact frame on screen; it is given as long as the stage has been running, up to 20 seconds, to develop. 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. Stills are PNG only — no JPEG, no WebP. On browsers that can record video, Record a clip saves 5, 10 or 20 seconds of the running animation as MP4 or WebM (whichever this browser can encode) at the same width, with the mark in every frame. A clip is not a seamless loop, it has no audio, and a width bigger than the stage is resampled rather than sharper — only the wallpaper is redrawn at its size. Where a browser cannot record video the button never appears and a PNG is the only export. And nothing is uploaded: the frame or clip 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

The Game of Life is a grid of square cells, each either alive or dead, updated in generations. It was devised by the mathematician John Horton Conway and first published in Martin Gardner’s “Mathematical Games” column in the October 1970 issue of Scientific American. Nobody plays it: you set up a starting pattern and the rules do the rest.

Here the starting pattern is a random soup: every cell is alive with probability 35 %. The grid wraps round, so a cell on the right edge has neighbours on the left edge and one on the bottom has neighbours on the top; there is no border for patterns to crash into. Cells are at least 6 CSS pixels across, so the stage on this page is about 113×64 cells of 8 px on a 1280-pixel-wide window and 58×44 cells of 6 px on a phone; opened fullscreen at 1280 it is about 116×65 cells of 11 px. The grid advances ten generations a second.

For the first few seconds the field boils. Then most of it dies, and what is left sorts itself into small objects that either never change or flip back and forth for ever, with a few gliders crawling between them. Clicking the animation throws a fresh soup.

The two rules: B3/S23

Every cell has eight neighbours, four along its sides and four at its corners. Each generation, all cells are updated at once:

  • Birth. A dead cell with exactly three live neighbours becomes alive.
  • Survival. A live cell with two or three live neighbours stays alive.
  • Death. Every other cell is dead next generation: a live cell with one neighbour or none dies of isolation, one with four or more of overcrowding.

Written as a rulestring that is B3/S23: born with 3, survives with 2 or 3. The notation matters because it names a whole family of rules; HighLife, for example, is B36/S23, which differs only in that a dead cell with six neighbours is also born. The rules above are those in Gardner’s 1970 column (a copy hosted by Stanford), and the rulestring is the one given in Wikipedia’s article on the game.

What the colours mean

Gardner told his readers to play the game with counters of two colours, because “births and deaths occur simultaneously” and a newborn counter must not be counted as a neighbour until the whole generation is done. This plate keeps that distinction and adds a third state:

  • Coral is a cell born this generation.
  • Cobalt is a cell that was alive last generation and survived.
  • Faint blue is a ghost: a cell that has died in the last eight generations, fading a step each generation.

So a still life, such as the 2×2 block, is solid cobalt. A blinker, three cells in a row that turn through 90° every generation, shows a cobalt centre, two coral ends and two ghosts where the ends just were. A glider, five cells that rebuild themselves one cell diagonally along every four generations, leaves a short blue wake pointing back the way it came. Any single frame is therefore a map of where the grid was busy, not only of where it is alive.

When a run ends

After every generation the whole grid is reduced to a pair of hash numbers. Because the rules contain no chance, if the grid ever equals itself from p generations earlier, it will repeat with period p for ever. When the last six generations all match with the same period of six or less, the run is over: the field is held for three seconds, fades out, and a new soup fades in. The empty grid counts too, with period one.

Measured with the plate’s own code on 60 seeded soups on the 113×64 grid a 1280-pixel window gets here: 34 settled, at a median of about generation 1,014 (roughly 100 seconds), the earliest at 629 and the latest at 1,496. The other 26 had not settled by generation 1,500 and were ended there; a glider circling the wrapped grid, for one, never repeats inside six generations. Of the 35 % of cells alive at the start, a median of about 3.2 % were alive at the end. On the phone-sized 58×44 grid 53 of 60 settled, at a median of generation 654: a smaller world runs out of room sooner.

The two named patterns

One run in four skips the soup and starts from a single famous pattern in empty space, alternating between these two. For these runs the edge of the picture stops wrapping and instead swallows anything that reaches it, so gliders fly off the screen.

The R-pentomino is five cells (drawn as the Life Lexicon draws it, O for a live cell):

.OO
OO.
.O.

It looks like it should fizzle out; instead it runs for a very long time. In 1970 Gardner reported that its fate was “not yet known” and that Conway had tracked it for 460 moves. The Life Lexicon gives the answer found since: it does not stabilise until generation 1103, by which time it has a population of 116. Wikipedia’s article on methuselahs, small patterns that take a long time to settle, gives the same two numbers. This plate’s test suite checks both against its own step function on a 1024×1024 wrapped grid, wide enough that no glider comes back round: the population is 116 at generation 1103 and never changes again, and 1103 is the last generation at which it changes. On screen you will not see those numbers: the picture is too small, and the swallowing edge eats part of the debris as well as the gliders.

Gosper’s glider gun is 36 cells that never settle and never grow in place, but fire a glider off down and to the right at a steady rate:

........................O...........
......................O.O...........
............OO......OO............OO
...........O...O....OO............OO
OO........O.....O...OO..............
OO........O...O.OO....O.O...........
..........O.....O.......O...........
...........O...O....................
............OO......................

The Life Lexicon calls it the first known gun, found by Bill Gosper in November 1970. Conway had offered fifty dollars in the original column to whoever could prove or disprove that no pattern grows for ever, and a gun that keeps adding gliders settles it. According to Wikipedia’s article, the gun makes its first glider on the 15th generation and another every 30th generation from then on. That is the other number the test suite holds the plate to: on a 256×256 grid the gun’s population rises by exactly 5 cells, one glider, every 30 generations, ten windows in a row. A gun run lasts 600 generations, one minute, then fades.

What this is not, and the two plates it sits nearest

This is a composed animation of one rule, not a Life editor: you cannot draw your own cells, load a pattern or change the rule. Two plates in the gallery are close relatives, and neither runs B3/S23.

  • Rule Thirty Cascade is a one-dimensional automaton: a single row of cells, each looking at itself and its two neighbours, with each new generation drawn as the next row down. Life is two-dimensional and every generation replaces the last.
  • Sandpile Avalanche is also a grid of cells updated by a local rule, but a cell holds a count of grains rather than being alive or dead, and any cell holding four topples one grain into each of its four side neighbours. Grains are moved, never created, except where new ones are dropped; Life creates and destroys cells freely.

How it is drawn

Each generation counts every cell’s eight neighbours, applies the rule as two bit masks (bit 3 for birth, bits 2 and 3 for survival), and records who was born, who died and the grid’s two hashes. Only the box around the live cells and their ghosts, plus one cell, is visited, which lets the test suite run the R-pentomino on a million-cell grid in under three seconds. A soup is run for 20 generations before it is first shown, because the first two seconds of a 35 % soup are uniform static. The picture is repainted every frame so that fades are smooth, but the cells change only once per tenth of a second of real time, whatever the frame rate.

Not one colour is named in the drawing code. The background and the ink slots are handed in from the shared palette, which is why the gallery tile, this page and a copy of the embed on someone else’s page follow the same light and dark settings. Under prefers-reduced-motion no animation loop runs: a fixed soup is computed 150 generations ahead and drawn once, with its births and ghosts, so the still shows the rules at work rather than raw noise.

Honest limits

The grid is small and wraps. The game is defined on an infinite plane. On a 113×64 torus the soup’s debris meets itself round the back, gliders collide with their own origins, and runs settle differently from how the same cells would on an unbounded grid.

The settle test only knows periods up to six. A field holding an oscillator with a longer period, or a glider that never meets anything, is not recognised as settled and is ended at generation 1,500 instead. Two different grids could in principle share both hashes; requiring six matching generations in a row makes that practically impossible, not impossible.

The named runs are demonstrations, not measurements. The R-pentomino’s 1103 and 116 are checked in the test suite on a large grid, not on your screen, and the gun is cut off after a minute.

Nothing here is adjustable. The density, the speed and the cycle are constants in one file. This gallery ships finished compositions rather than controls.

Draw your own

The rule above is short enough to run yourself. The snippet below is this plate’s Game of Life in 29 lines of HTML and plain JavaScript with no library, and with the plate’s own constants: the rule as the same two bit masks (birth on bit 3, survival on bits 2 and 3, which is B3/S23), a 35 % soup, the same wrap-around edge, the same 20-generation warm-up, ghosts that last eight generations, cells sized to about 110 across but never under 6 or over 12 pixels, and ten generations a second. What it leaves out is everything that exists for the gallery rather than for the game: the settle test that ends a run and throws a new soup (this one runs until you reload), the R-pentomino and glider-gun runs, the fades, click-to-reseed, the seeded random generator and sharp drawing on high-density screens (it draws in CSS pixels).

  • Grid. It fills the window: a 1280 × 720 window gets 11 px cells and a 116 × 65 grid (7,540 cells), a 390 × 844 phone gets the 6 px minimum and 65 × 140. Each cell is drawn 1 px short on each axis (16% of the cell, rounded, at least 1), so live cells read as separate tiles. The grid wraps: the % H and % W in the neighbour count make the right edge a neighbour of the left.
  • Rule. Each cell counts its eight neighbours k; a live cell lives on if bit k of S is set, a dead one is born if bit k of B is. Measured on this exact code over 30 soups on the 116 × 65 grid: of the 35 % alive at the start, a median 17.7 % are alive after the warm-up, 8.1 % at generation 150, 3.7 % at generation 1,000 and 3.3 % at 2,000, by which time what is left is mostly blocks, blinkers and the odd glider.
  • Speed. One generation every 100 ms, so ten a second, and the canvas is redrawn once per generation. Counting every neighbour of 7,540 cells takes about 0.14 ms in Node on the machine that built this page, so the speed is a choice, not a limit: set MS to 16 and it runs at roughly sixty.
  • Colour. A survivor is cobalt, a cell born this generation is coral, and a cell that has just died leaves a ghost in the deeper cobalt whose opacity falls with the square of the generations it has left, from 0.3 (0.22 on the light page) to nothing in eight. Those four colours are this site’s palette, light or dark to match your device. Reduced-motion visitors get one still frame, 150 generations in, like the plate’s, though from a fresh random soup rather than the plate’s fixed seed.

Three edits on line 7 show how narrow the rule is. Add | (1 << 4) to S, so a cell also survives with four neighbours, and the soup congeals: about half the grid is alive at generation 150 and still at 1,000, a frozen maze. Take (1 << 2) out instead, so only three neighbours keep a cell alive, and by generation 150 about 0.1 % of cells are left. Change B to (1 << 3) | (1 << 6) for HighLife and a soup ends up about as sparse as Life’s (3.4 % alive at generation 1,000, against 3.7 %): one rule change can matter enormously or barely at all. For a check that the snippet is the real game, a glider placed in it rebuilds its own five cells one square diagonally along every four generations, as the Wikipedia article on the glider says; this page’s test suite checks that, and the rule for every one of the 512 possible neighbourhoods, against the code below. The rule, Conway’s 1970 date and Gardner’s October 1970 Scientific American column are as given in Wikipedia’s article on the game. Save the snippet as an .html file and open it.

<canvas id="life" style="display:block"></canvas>
<script>
const dark = matchMedia('(prefers-color-scheme: dark)').matches;  // follow the OS theme
const [ground, cobalt, deep, coral] = dark ? ['11,13,18', '150,180,255', '120,158,255', '255,120,84']
                                           : ['231,226,213', '40,72,205', '52,88,214', '190,68,28'];
const LOOK = dark ? [0.92, 1, 0.3] : [0.9, 1, 0.22];  // opacity of survivor, newborn, freshest ghost
document.body.style.cssText = 'margin:0; background:rgb(' + ground + ')';
const B = 1 << 3, S = (1 << 2) | (1 << 3), DENSITY = 0.35, GHOST = 8, MS = 100, WARM = 20, STILL = 130;  // B3/S23
const CELL = Math.max(6, Math.min(12, Math.floor(innerWidth / 110))), GAP = Math.max(1, Math.round(CELL * 0.16));  // ~110 across
const W = Math.floor(innerWidth / CELL), H = Math.floor(innerHeight / CELL);
let c = new Uint8Array(W * H).map(() => Math.random() < DENSITY ? 1 : 0), nx = new Uint8Array(W * H);  // a 35% soup
const born = new Uint8Array(W * H), gh = new Uint8Array(W * H);  // newborn flags, ghost countdowns
function step() {  // every cell at once; the grid wraps round, so there is no edge
  for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) { let k = 0;
    for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) if (dx || dy) k += c[(y + dy + H) % H * W + (x + dx + W) % W];
    const i = y * W + x, a = c[i], live = a ? (S >> k) & 1 : (B >> k) & 1;  // bit k of S: survives with k
    nx[i] = live; born[i] = live & (a ^ 1); gh[i] = live ? 0 : a ? GHOST : Math.max(0, gh[i] - 1); }
  [c, nx] = [nx, c];
}
const cv = document.getElementById('life'), ctx = cv.getContext('2d'); cv.width = W * CELL; cv.height = H * CELL;
function draw() { ctx.fillStyle = 'rgb(' + ground + ')'; ctx.fillRect(0, 0, cv.width, cv.height);
  for (let i = 0; i < W * H; i++) { if (!c[i] && !gh[i]) continue;
    ctx.fillStyle = !c[i] ? 'rgba(' + deep + ',' + LOOK[2] * (gh[i] / GHOST) ** 2 + ')'  // ghost of a cell that died
      : born[i] ? 'rgba(' + coral + ',' + LOOK[1] + ')' : 'rgba(' + cobalt + ',' + LOOK[0] + ')';  // newborn, survivor
    ctx.fillRect(i % W * CELL + (GAP >> 1), (i / W | 0) * CELL + (GAP >> 1), CELL - GAP, CELL - GAP); } }
const still = matchMedia('(prefers-reduced-motion: reduce)').matches;  // still: 150 generations in, drawn once
for (let i = 0; i < WARM + (still ? STILL : 0); i++) step();
draw(); if (!still) setInterval(() => { step(); draw(); }, MS);  // 10 generations a second
</script>
Paste it into an empty .html file.

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

More from the gallery

All 32 animations in the gallery →