Conway's Game of Life — emergentes Leben in 60 Zeilen
Vier einfache Regeln. Eine 2D-Matrix. Daraus entstehen Gleiter, Oszillatoren, stabile Strukturen — und der Hintergrund von letrobots.de. Ein KI-generiertes Experiment in zellulärer Automatentheorie.
Was ist Conway's Game of Life?
Das Game of Life wurde 1970 vom britischen Mathematiker John Horton Conway entwickelt. Es ist kein Spiel im klassischen Sinne — es gibt keinen Spieler, keine Eingabe, kein Ziel. Stattdessen: ein zellulärer Automat, der sich nach festen Regeln selbst entfaltet.
Das System besteht aus einem zweidimensionalen Gitter von Zellen. Jede Zelle ist entweder lebendig oder tot. In jedem Zeitschritt wird der Zustand aller Zellen gleichzeitig nach denselben vier Regeln aktualisiert:
Einsamkeit
Eine lebende Zelle mit weniger als 2 Nachbarn stirbt.
Überleben
Eine lebende Zelle mit 2 oder 3 Nachbarn lebt weiter.
Überbevölkerung
Eine lebende Zelle mit mehr als 3 Nachbarn stirbt.
Geburt
Eine tote Zelle mit genau 3 Nachbarn wird lebendig.
Das Gitter
Das Gitter wird als Array von Uint8Array-Zeilen gespeichert —
speichereffizient, schnell zu iterieren. Die Kanten sind toroidal verbunden:
die rechte Seite ist mit der linken verbunden, oben mit unten.
Zellen am Rand haben dadurch immer acht vollständige Nachbarn.
Die Startbelegung ist zufällig — jede Zelle hat eine 25%-Chance, lebendig zu beginnen. Das erzeugt genug Aktivität ohne sofortiges Ausdünnen.
function randomize() {
return Array.from({ length: ROWS }, () =>
Uint8Array.from({ length: COLS }, () =>
Math.random() < 0.25 ? 1 : 0
)
);
}
Der Kern: die step()-Funktion
Jeder Simulationsschritt zählt für jede Zelle die lebenden Nachbarn und wendet die vier Regeln an. Dabei wird immer in ein neues Array geschrieben — alle Zellen werden gleichzeitig aktualisiert, nicht sequenziell.
function step() {
const g = grid;
const next = Array.from({ length: ROWS }, () => new Uint8Array(COLS));
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
let neighbors = 0;
// 8 Nachbarn, toroidal (Kanten verbinden sich)
for (let dr = -1; dr <= 1; dr++)
for (let dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
neighbors += g[(r + dr + ROWS) % ROWS][(c + dc + COLS) % COLS];
}
// Regeln
next[r][c] = g[r][c]
? (neighbors === 2 || neighbors === 3 ? 1 : 0) // lebt: 2–3 → überleben
: (neighbors === 3 ? 1 : 0); // tot: 3 → Geburt
}
}
grid = next;
}
Canvas & Performance
Die Simulation läuft im Browser via requestAnimationFrame.
Ohne Bremse wären das 60 Frames pro Sekunde — zu viel für eine Hintergrundanimation.
Mit einem einfachen Frame-Skip wird die Simulation nur alle 6 Frames aktualisiert,
was ~10 fps ergibt und die CPU-Last auf ein Sechstel reduziert.
let frame = 0;
function loop() {
if (++frame % 6 === 0) {
step();
draw();
}
requestAnimationFrame(loop);
}
Das Canvas passt sich per resize-Event an jede Fenstergröße an —
COLS und ROWS werden dynamisch aus der Viewport-Größe berechnet.
Als Hintergrund einbetten
Drei CSS-Zeilen machen aus der Simulation einen lebendigen Seitenhintergrund:
canvas {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
opacity: 0.18; /* subtil, nicht ablenkend */
pointer-events: none; /* Klicks gehen durch */
z-index: 0; /* hinter allem anderen */
}
Der eigentliche Content liegt in einem div mit z-index: 1 darüber.
Die Hintergrundkarten verwenden background: #1a1a1acc (leicht transparent),
damit das Gitter durch schimmert.