6.5 KiB
Conway Life Demo Implementation Plan
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Build a direct-open Conway's Game of Life demo page with a modern visualization style, preset patterns, canvas-based simulation, and a clean teaching-plus-exploration flow.
Architecture: Keep the page build-free and local-file friendly: semantic markup in index.html, visual styling in styles.css, and a single browser script in app.js. Put Conway engine logic and state helpers in testable pure functions exported from app.js, while the DOM/controller layer wires those functions to the canvas UI.
Tech Stack: HTML5, CSS3, vanilla JavaScript, Canvas API, Node.js built-in test runner (node --test)
Note: this workspace is not a git repository, so commit steps are intentionally omitted.
File Structure
- Create:
index.html- page structure, content sections, script/style includes - Create:
styles.css- theme variables, layout, cards, control panel, responsive styling, animation polish - Create:
app.js- Conway engine, preset definitions, state helpers, canvas rendering, UI events - Create:
tests/life-demo.test.js- Node tests for pure simulation and state logic
Chunk 1: Build and test the Conway engine
Task 1: Create the failing engine tests
Files:
-
Create:
tests/life-demo.test.js -
Test:
tests/life-demo.test.js -
Step 1: Write the failing test
const test = require('node:test');
const assert = require('node:assert/strict');
const {
createEmptyGrid,
stepGrid,
stampPattern,
PATTERNS,
} = require('../app.js');
test('blinker rotates after one generation', () => {
const grid = createEmptyGrid(5, 5);
grid[2][1] = 1;
grid[2][2] = 1;
grid[2][3] = 1;
const next = stepGrid(grid);
assert.deepEqual(next[1], [0, 0, 1, 0, 0]);
});
- Step 2: Run test to verify it fails
Run: node --test tests/life-demo.test.js
Expected: FAIL because app.js or the exported functions do not exist yet.
- Step 3: Write minimal implementation
function createEmptyGrid(rows, cols) {
return Array.from({ length: rows }, () => Array(cols).fill(0));
}
Implement the smallest export set needed to pass:
-
createEmptyGrid(rows, cols) -
countLiveNeighbors(grid, row, col) -
stepGrid(grid) -
stampPattern(grid, pattern, offsetRow, offsetCol) -
PATTERNSforglider,pulsar, andgosperGliderGun -
Step 4: Run test to verify it passes
Run: node --test tests/life-demo.test.js
Expected: PASS for the engine tests.
Chunk 2: Add state helpers and page shell
Task 2: Add failing tests for state helpers
Files:
-
Modify:
tests/life-demo.test.js -
Modify:
app.js -
Create:
index.html -
Create:
styles.css -
Step 1: Write the failing test
Add tests for:
test('createInitialState seeds the default preset and starts paused', () => {
const state = createInitialState({
rows: 20,
cols: 20,
defaultPattern: 'pulsar',
});
assert.equal(state.running, false);
assert.equal(state.selectedPattern, 'pulsar');
assert.equal(state.generation, 0);
assert.ok(state.liveCount > 0);
});
- Step 2: Run test to verify it fails
Run: node --test tests/life-demo.test.js
Expected: FAIL because createInitialState is not implemented yet.
- Step 3: Write minimal implementation
Implement pure helpers:
cloneGrid(grid)countLiveCells(grid)createInitialState({ rows, cols, defaultPattern })randomizeGrid(grid, probability)toggleCell(grid, row, col, forcedValue)
At the same time, scaffold:
-
index.htmlwith Hero, Lab, Rules, and Presets sections -
styles.csswith the modern visualization theme variables and responsive grid -
Step 4: Run test to verify it passes
Run: node --test tests/life-demo.test.js
Expected: PASS for the new state-helper tests.
Chunk 3: Wire controls, canvas rendering, and preset switching
Task 3: Add failing tests for UI-facing state transitions
Files:
-
Modify:
tests/life-demo.test.js -
Modify:
app.js -
Modify:
index.html -
Modify:
styles.css -
Step 1: Write the failing test
Add tests for:
test('applyPreset replaces the grid, pauses playback, and resets generation', () => {
const state = {
...createInitialState({ rows: 25, cols: 25, defaultPattern: 'glider' }),
running: true,
generation: 12,
};
const next = applyPreset(state, 'gosperGliderGun');
assert.equal(next.running, false);
assert.equal(next.generation, 0);
assert.equal(next.selectedPattern, 'gosperGliderGun');
assert.ok(next.liveCount > state.liveCount);
});
- Step 2: Run test to verify it fails
Run: node --test tests/life-demo.test.js
Expected: FAIL because applyPreset is not implemented yet.
- Step 3: Write minimal implementation
Implement:
applyPreset(state, patternName)advanceState(state)setSpeed(state, speed)getSpeedLabel(speed)
Then wire browser behavior:
-
draw the grid on a
canvas -
support play/pause, step, clear, randomize, reset preset, and speed controls
-
support click and drag painting on the canvas
-
update status text, generation count, live-count, and active preset styling
-
Step 4: Run test to verify it passes
Run: node --test tests/life-demo.test.js
Expected: PASS for the state-transition tests.
Chunk 4: Polish visual details and run end-to-end verification
Task 4: Finish presentation details and verify manually
Files:
-
Modify:
index.html -
Modify:
styles.css -
Modify:
app.js -
Test:
tests/life-demo.test.js -
Step 1: Refine the visuals
Complete:
-
luminous background gradients and grid accents
-
polished cards and control panel surfaces
-
hover/focus states
-
responsive stacking for smaller screens
-
subtle canvas/section entrance motion
-
Step 2: Run automated tests
Run: node --test tests/life-demo.test.js
Expected: PASS with zero failures.
- Step 3: Run manual verification
Open: index.html
Verify:
-
page opens directly without a dev server
-
pulsar loads by default and the simulation starts paused
-
controls behave correctly
-
each preset loads and pauses correctly
-
editing by click/drag works
-
desktop and narrow layouts both remain usable
-
Step 4: Record any gaps
If a browser-only issue appears, fix it and rerun:
node --test tests/life-demo.test.js- manual browser verification of
index.html