live - 60fps 1920 x 1080
<!DOCTYPE html>
<html>
<head>
<title>Game of Life</title>
<style>
:root { --bg-color: #05070d; --cell-new: #34d399; --cell-old: #164e63; }
* { margin: 0; padding: 0; }
body { height: 100vh; background: var(--bg-color); overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const cv = document.getElementById('c'), ctx = cv.getContext('2d');
const css = getComputedStyle(document.documentElement);
const bg = (css.getPropertyValue('--bg-color') || '#05070d').trim();
function hex(h){h=(h||'').trim().replace('#','');if(h.length===3)h=h.split('').map(c=>c+c).join('');const n=parseInt(h,16);return[(n>>16)&255,(n>>8)&255,n&255];}
const cNew = hex(css.getPropertyValue('--cell-new') || '#34d399');
const cOld = hex(css.getPropertyValue('--cell-old') || '#164e63');
const CELL = 9;
let cols, rows, cur, nxt, age, w, h;
function seed() { for (let i = 0; i < cols * rows; i++) { cur[i] = Math.random() < 0.16 ? 1 : 0; age[i] = 0; } }
function build() {
w = cv.width = innerWidth; h = cv.height = innerHeight;
cols = Math.ceil(w / CELL); rows = Math.ceil(h / CELL);
cur = new Uint8Array(cols * rows); nxt = new Uint8Array(cols * rows); age = new Uint16Array(cols * rows);
seed();
}
addEventListener('resize', build); build();
function tick() {
let alive = 0;
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 xx = (x + dx + cols) % cols, yy = (y + dy + rows) % rows;
n += cur[yy * cols + xx];
}
const i = y * cols + x, a = cur[i];
const live = a ? (n === 2 || n === 3) : (n === 3);
nxt[i] = live ? 1 : 0;
if (live) { age[i] = a ? Math.min(255, age[i] + 1) : 0; alive++; }
}
[cur, nxt] = [nxt, cur];
if (alive < cols * rows * 0.02) { // reseed a soup when it fizzles out
for (let k = 0; k < cols * rows * 0.06; k++) cur[(Math.random() * cols * rows) | 0] = 1;
}
}
function draw() {
ctx.fillStyle = bg; ctx.fillRect(0, 0, w, h);
for (let y = 0; y < rows; y++) for (let x = 0; x < cols; x++) {
const i = y * cols + x; if (!cur[i]) continue;
const t = Math.min(1, age[i] / 40);
ctx.fillStyle = `rgb(${cNew[0]+(cOld[0]-cNew[0])*t|0},${cNew[1]+(cOld[1]-cNew[1])*t|0},${cNew[2]+(cOld[2]-cNew[2])*t|0})`;
ctx.fillRect(x * CELL + 1, y * CELL + 1, CELL - 2, CELL - 2);
}
}
let acc = 0;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) { for (let i = 0; i < 12; i++) tick(); draw(); }
else (function loop() { acc++; if (acc % 4 === 0) tick(); draw(); requestAnimationFrame(loop); })();
</script>
</body>
</html> About this animation
Conway's Game of Life plays out forever — gliders, oscillators, and colonies bloom and die across a glowing cellular grid.