Game of Life

Conway's zero-player cellular automaton.

Overview

Conway's Game of Life, 1970. Every cell on a grid is alive or dead, every generation each cell counts its eight neighbors, and a rule table decides who exists next tick. Zero players: you set the board, the rules do the rest. The rules fit on an index card; the consequences are Turing-complete.

Like the boids demo, it's plain JavaScript on one canvas.

Demo

gen 0 · pop 0

[ click to toggle, drag to paint. the edges wrap. the phosphor is slow. ]

Things to try: the r-pentomino boils for a thousand-odd generations from five cells. The glider gun, on a wrapping grid, eventually shoots itself in the back. A long straight line collapses into blinkers. Painting into a running soup is encouraged.

The Rules

Each cell counts its eight neighbors:

  • Loneliness — a live cell with fewer than two neighbors dies.
  • Contentment — a live cell with two or three neighbors carries on.
  • Overcrowding — a live cell with four or more neighbors dies.
  • Birth — a dead cell with exactly three neighbors switches on.

That's B3/S23 — born on 3, survives on 2 or 3. Minus the drawing, the demo above is:

life.js JavaScript
// two grids — count neighbors on one, write the verdict to the other
function step() {
	for (let y = 0; y < rows; y++) {
		const up  = ((y + rows - 1) % rows) * cols;  // % = the edges wrap
		const mid = y * cols;
		const dn  = ((y + 1) % rows) * cols;
		for (let x = 0; x < cols; x++) {
			const xl = (x + cols - 1) % cols, xr = (x + 1) % cols;
			const n = cur[up + xl]  + cur[up + x]  + cur[up + xr]
			        + cur[mid + xl]                + cur[mid + xr]
			        + cur[dn + xl]  + cur[dn + x]  + cur[dn + xr];
			// the entire game is this line:
			nxt[mid + x] = (n === 3 || (n === 2 && cur[mid + x])) ? 1 : 0;
		}
	}
	[cur, nxt] = [nxt, cur];  // swap buffers — never update in place
}

The one real trap is the last line: you need two grids. Update in place and half the neighbors you count are from the next generation — the symptom is gliders that smear apart.

Field Guide

Every soup settles into the same zoo:

  • Still lifes — block, beehive, loaf: every cell at two or three neighbors, forever. Most soup ends here.
  • Oscillators — the blinker (period 2) is most of the rest; the pulsar in the menu is period 3.
  • Spaceships — the glider re-forms one diagonal cell over every four generations. Hacker heraldry.
  • Methuselahs — the r-pentomino (5 cells) boils for ~1,100 generations; the acorn (7 cells) over 5,200. The first observed gliders escaped an r-pentomino.
  • Guns — Gosper's glider gun won Conway's $50 prize for unbounded growth: period 30, one glider per cycle, forever.

Implementation Notes

If you build one:

  • Two buffers, swap. Never update in place.
  • One flat Uint8Array, row-major. No Cell objects; the step loop never allocates.
  • Hoist the wrap. Compute the wrapped row offsets once per row; or pad the grid with a copied border and skip modulo entirely.
  • The phosphor is one extra buffer. Death writes 1.0, each generation multiplies by 0.72, drawn in four bucketed alphas so fillStyle changes four times, not four thousand.
  • Only the ternary is Life. HighLife (B36/S23) has a replicator, Seeds (B2/S) detonates, Day & Night (B3678/S34678) is dead/alive symmetric — same loop, different line. The rabbit hole is Gosper's Hashlife.