A lane-defence game in the spirit of Plants vs Zombies, with one twist: you play the food. You place edible defenders on a kitchen countertop and hold off waves of pests.
Solo project. Design, code, UI specification and balance by Daniil Muratov, 2026.
The game is built from a written design document, and the document is the source of truth: when the game changes, the document changes with it.
| Document | What is in it |
|---|---|
docs/design-pack/SPEC.md |
The full UI specification: layout, states, sizes, behaviour |
docs/design-pack/01-battle-hud.md |
Battle HUD |
docs/design-pack/02-level-select.md |
Level select |
docs/design-pack/03-shop.md |
Shop |
docs/design-pack/04-almanac.md |
Almanac |
docs/design-pack/05-loadout.md |
Loadout |
docs/design-pack/09-tokens.md |
Design tokens: colour, type, spacing |
docs/adr/0001-design-pack-is-canon.md |
Why the design pack, not the code, is canon |
A few decisions the document records, as examples of how the design is argued rather than asserted:
- The board is described in lanes and depth, not rows and columns. 5 lanes, 16 cells of depth. The same board and the same balance then work in landscape and in portrait, and the engine rebuilds the layout when the screen shape changes.
- The mousetrap clears the whole lane, not one pest. It used to snap shut on the single pest that touched it. A lane is rarely lost to one pest, it is lost to the ones walking through the gap behind it, so the safety net now drives the length of its lane. Three rules come with that: a burrowed pest is missed, nothing killed by the trap pays a coin, and nothing killed by the trap leaves a brood.
- Nothing is paid for losing. The trap fires because the lane was already lost, so it earns nothing.
TypeScript, Phaser 3, Vite, Vitest, Capacitor for the Android build.
src/core/— simulation: grid, waves, economy, combat. No rendering, no Phaser.src/view/— Phaser scenes, sprite manifest, procedural placeholders for missing art.src/data/— content and level data.test/— 24 test files covering board, economy and combat rules.
Keeping the simulation free of the renderer is what makes the rules testable: the balance tests run in Node, without a browser and without Phaser.
npm install
npm run dev # http://localhost:5173
npm test # Vitest
npm run build # type-check and production bundle
npm run android # build, sync and open the Capacitor Android projectArt is loaded from public/assets/sprites/. If a sheet is missing, BootScene draws a
procedural placeholder of the same size and frame count and logs what it could not find,
so the game always runs.
Playable vertical slice: the battle screen, the economy, the wave loop and the loss condition all work. Screens outside the slice (shop, almanac, loadout) are specified in the design pack and not yet implemented.