Code — how this page is built

Under the hood

The actual source of the components on this page, read straight from the repo. Copy it, read it, or open it on GitHub.


Conway's rules over a toroidal Uint8Array grid, drawn to canvas and stepped on a timer. Click toggles cells; randomise/clear/step/pause controls.

'use client';

import * as React from 'react';

import { Win98Button } from '@/components/ui/win-98-button';

const COLS = 70;
const ROWS = 46;
const CELL = 8;

function emptyGrid() {
  return new Uint8Array(COLS * ROWS);
}
function randomGrid() {
  const g = emptyGrid();
  for (let i = 0; i < g.length; i++) g[i] = Math.random() < 0.28 ? 1 : 0;
  return g;
}

export function LifeGame() {
  const canvasRef = React.useRef<HTMLCanvasElement>(null);
  const grid = React.useRef<Uint8Array>(randomGrid());
  const [running, setRunning] = React.useState(true);
  const [gen, setGen] = React.useState(0);

  const draw = React.useCallback(() => {
    const ctx = canvasRef.current?.getContext('2d');
    if (!ctx) return;
    ctx.fillStyle = '#f3eee1';
    ctx.fillRect(0, 0, COLS * CELL, ROWS * CELL);
    ctx.fillStyle = '#14110b';
    for (let y = 0; y < ROWS; y++) {
      for (let x = 0; x < COLS; x++) {
        if (grid.current[y * COLS + x]) ctx.fillRect(x * CELL + 0.5, y * CELL + 0.5, CELL - 1, CELL - 1);
      }
    }
  }, []);

  const step = React.useCallback(() => {
    const cur = grid.current;
    const next = emptyGrid();
    for (let y = 0; y < ROWS; y++) {
      for (let x = 0; x < COLS; x++) {
        let n = 0;
        for (let dy = -1; dy <= 1; dy++)
          for (let dx = -1; dx <= 1; dx++) {
            if (!dx && !dy) continue;
            const nx = (x + dx + COLS) % COLS;
            const ny = (y + dy + ROWS) % ROWS;
            n += cur[ny * COLS + nx];
          }
        const alive = cur[y * COLS + x];
        next[y * COLS + x] = alive ? (n === 2 || n === 3 ? 1 : 0) : n === 3 ? 1 : 0;
      }
    }
    grid.current = next;
    setGen((g) => g + 1);
    draw();
  }, [draw]);

  React.useEffect(() => {
    draw();
  }, [draw]);

  React.useEffect(() => {
    if (!running) return;
    const id = window.setInterval(step, 90);
    return () => window.clearInterval(id);
  }, [running, step]);

  const toggleAt = (e: React.MouseEvent) => {
    const rect = canvasRef.current!.getBoundingClientRect();
    const x = Math.floor(((e.clientX - rect.left) / rect.width) * COLS);
    const y = Math.floor(((e.clientY - rect.top) / rect.height) * ROWS);
    if (x < 0 || x >= COLS || y < 0 || y >= ROWS) return;
    grid.current[y * COLS + x] ^= 1;
    draw();
  };

  return (
    <div className="flex w-full max-w-[760px] flex-col items-center gap-5">
      <canvas
        ref={canvasRef}
        width={COLS * CELL}
        height={ROWS * CELL}
        onClick={toggleAt}
        className="w-full max-w-full cursor-crosshair rounded-md border border-foreground/20"
      />
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Win98Button onClick={() => setRunning((r) => !r)} className="h-9 min-w-24 px-5">
          {running ? 'Pause' : 'Play'}
        </Win98Button>
        <Win98Button onClick={step} disabled={running}>Step</Win98Button>
        <Win98Button onClick={() => { grid.current = randomGrid(); setGen(0); draw(); }}>Random</Win98Button>
        <Win98Button onClick={() => { grid.current = emptyGrid(); setGen(0); draw(); }}>Clear</Win98Button>
      </div>
      <div className="font-mono text-[11px] uppercase tracking-[0.12em] opacity-50">
        generation {gen} · click to toggle cells
      </div>
    </div>
  );
}
↖ kaspirius