← Blog

Conway's Game of Life

Conway's Game of Life is a classic example of how complexity can blossom from just a few straightforward rules. Picture an infinite grid of cells—each one can be "on" (alive) or "off" (dead). Each turn (or "generation"), every cell checks how many neighbors are alive. If it's overcrowded, it dies. If it's too lonely, it also dies. But with exactly three neighbors, a dead cell will spark into life.

This simple set of rules leads to endlessly fascinating behavior—from stable formations and repeating oscillators, to self-propagating "spaceships" that traverse the grid. It's a perfect demonstration of how complexity can arise from simplicity, and it's even Turing complete, meaning it can, in theory, simulate any computation.

How it works

Loading game board...

Pattern Examples

These classic patterns showcase the Game's emergent behavior. The "Glider" demonstrates movement across the grid, the "Pulsar" creates a mesmerizing oscillation, and the "Small Exploder" shows how simple shapes can evolve into complex forms.

The Algorithm

At its heart, the Game of Life runs on just a few elegant functions that determine life and death in our cellular universe:

// The core algorithm that determines life and death
function computeNextGeneration(currentGrid) {
  const newGrid = createEmptyGrid(rows, cols);

  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      const neighbors = countNeighbors(currentGrid, r, c);
      const cellState = currentGrid[r][c];

      // Apply Conway's rules of life
      if (cellState === 1) {
        // Live cell survives if it has 2 or 3 neighbors
        newGrid[r][c] = (neighbors === 2 || neighbors === 3) ? 1 : 0;
      } else {
        // Dead cell springs to life if it has exactly 3 neighbors
        newGrid[r][c] = (neighbors === 3) ? 1 : 0;
      }
    }
  }
  return newGrid;
}
The core algorithm applies Conway's rules to each cell in the grid. For each generation, it checks every cell's state and its neighbor count to determine if it lives, dies, or springs to life.
// Count live neighbors for each cell
function countNeighbors(g, row, col) {
  let count = 0;
  for (let i = 0; i < 8; i++) {
    const nr = row + NEIGHBOR_OFFSETS[i * 2];
    const nc = col + NEIGHBOR_OFFSETS[i * 2 + 1];
    if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
      count += g[nr][nc];
    }
  }
  return count;
}
View Wolfram's Rule 110 →
How it works

Select any area to create clusters of cells, or select and drag to draw living paths. Press Play to watch your creation evolve, or use Step Forward to advance one generation at a time. Use Randomize for chaos, or Clear to start fresh.

The Rules

Each cell follows four simple rules in every generation:

  • Underpopulation: A living cell with fewer than 2 neighbors dies
  • Survival: A living cell with 2 or 3 neighbors survives
  • Overpopulation: A living cell with more than 3 neighbors dies
  • Reproduction: A dead cell with exactly 3 neighbors becomes alive
Controls
  • Play/Pause: Start or stop the simulation
  • Step Forward: Advance one generation at a time
  • Speed: Adjust how quickly generations pass
  • Zoom: Change the grid size (only while paused)
Pattern Examples

Try the preset patterns to see different behaviors:

  • Glider: A pattern that moves diagonally across the grid
  • Pulsar: A large pattern that oscillates with a period of 3 generations
  • Small Exploder: A pattern that expands outward chaotically