$ cd ~/projects/age-of-potatoes
Age of Potatoes
The source behind ageofpotatoes.com, golden-file tests for the deterministic sim included.
- game
- web app

Age of Potatoes
Build a chips-and-soda empire from a weedy roadside lot.
Factorio × Stardew Valley × RimWorld, in your browser.
Free, in your desktop browser, nothing to install. Sign in with an email link and your lot follows you everywhere. Play it at ageofpotatoes.com.
Washer → belt → Slicer → Fryer → Seasoner → Bagger. Five grabber arms, zero hands.
The farmer is supervising.
You start with $50, a watering can, a bag of eight seed potatoes, and a patch of dirt. You end with a humming floor of belts, pipes, and robot arms that grows, fries, seasons, bags, bottles, and ships while you go make a sandwich.
The loop never changes: Tend → Convert → Sell → Improve. What changes over the life of a save is who does each verb. First your hands, then your tools, then your machines. Every system has a manual tier, an assisted tier, and an automated tier, and every automation unlock retires a tool with a small ceremony. Your first watering can ends up bronzed on a plaque, engraved with the number of tiles you personally watered.
Rent's paid through the month. Make chips.
From dirt to dynasty
Day one, by hand. Press 3 and click the dirt to plant a russet, 2 to water it. Harvesting a potato hands the seed back, so a field pays for itself. Everything you do here, you will one day pay a machine to do instead. |
Day fifteen: the plumbing arrives. A pump on the pond, pipe running up to the machines, and sugar cane that regrows from the cut stalk. Along the top, the soda line: cane pressed, boiled down, mixed, carbonated, and bottled. |
Nothing rots. No crop wilts, no good spoils, and dirt is just dirt. It's a sandbox. Prices are stable and posted, and the pressure comes from upkeep instead: machines wear with use (never with time; an idle machine stays pristine), and a sloppy factory bleeds margin. A well-run one prints money and, because every machine's work-cycle sound is quantized to a shared beat grid, it literally grooves.
Three eras, twenty-two discoveries

The research atlas lays the whole climb out in front of you, four lanes wide: Agronomy, Hydraulics, Fabrication & Logistics, and Commerce & Operations. Each era is sealed by a charter: pay the fee and prove you've earned it before the next one opens.
| Era | Name | The vibe | You get |
|---|---|---|---|
| I | The Spud Shack | You, a can, and dirt | Sugar cane, the Washer & Slicer, the Fryer, the Seasoner & Bagger |
| II | The Workshop | Machines, and things that move on their own | Belts & grabber arms, pumps & pipes, irrigation, the soda line, bots, trucks |
| III | The Assembly Line | A floor that runs without you | Express belts, long-reach arms, repair drones |
Build anything, buy everything
The Build menu (B). Three belt tiers, filtered and long-reach arms, junctions, pipe, pumps, sprinklers, and every machine on the line, each with its price on the tag. |
The Stand (G). Seeds, repair kits, kola nuts. The counter is a real place on your lot: buying sends you walking, and the money changes hands when you arrive. The stand sells; it does not haggle. |
Controls
| Key | Does |
|---|---|
| W A S D / click | Walk (click somewhere far away to walk there) |
| 1–0 | Pick a tool from the hotbar |
| B | Build menu |
| T | Research |
| G | The Stand |
| I | The Bag |
| Z | Zones |
| P | Dispatch: carts and truck routes |
| R | Rotate the ghost, or the arm under the cursor |
| C / L | Cycle the crop / the liquid for a pipe run |
| + / − | Zoom |
| Right-click | Tear down |
| Esc | Close whatever's open, or pause |
| F3 | Perf overlay |
Every lot is yours, everywhere

Sign in with an email magic link, pick your farmer, and roll a fresh seed: a lot nobody has stood on before. Every lot is backed up to the cloud under your account and follows you to any browser. A copy also lives locally in IndexedDB, so the game boots instantly and plays straight through a network outage. Personal bests and a global leaderboard are a click away.
Under the hood
I'm Lucas Leite, and I built this because it's fun and I like potatoes and factories. It is a hobby game built with a level of engineering paranoia normally reserved for flight software. The highlights:
- A bit-exact deterministic simulation.
packages/simis pure TypeScript with zero dependencies and no DOM lib, running at a fixed 20 ticks per second.Date,Math.random,async,for...in, and every transcendental (sin,cos,exp,pow,**…) are banned in there by lint rules, because JS engines disagree in the last bits and that desyncs replays. IEEE-754 guarantees+ − × ÷andsqrt. Nothing else gets in. - Integers all the way down. 256 subtiles per tile, money in cents, machine progress
in work-points, fluids in millilitres. There are no floats in sim state, so a refactor
that reorders
(a*b)+ccan't silently invalidate every stored replay. The whole world folds into a single FNV-1a state hash. - Golden replays on every push. Twenty pinned worlds (a seed, a frozen command list,
a tick count, and the state hash they must produce) replay headless in CI. The day
someone introduces non-determinism is the day the build goes red. The longest of them
runs 272,000 ticks. The fixture rules live in
packages/sim/test/fixtures/README.md. - Every mutation is a
Commandat a tick. No debug buttons, no out-of-band writes. Even the cheat console is a command, and it's counted and hashed. - The renderer is a one-way street. Interpolated positions, particle jitter, and lamp
flicker live in
apps/weband are never read back. Audio follows the sim's integer beat grid.AudioContext.currentTimenever touches the simulation. - Zero sound files. Every effect is synthesized at runtime from oscillators and noise, behind a sample-loader-shaped seam so real recordings can drop in later.
- A leaderboard you can only post to by playing. Three Postgres migrations are the
entire backend. Saves sync under Row Level Security, and leaderboard rows are mirrored
out of your save by a database trigger: there are no client write grants at all, and a
save that ever ran a cheat keeps syncing but drops off the public board. See
supabase/README.md. - An AI art pipeline with a hard budget.
yarn artgenerates pixel art through Retro Diffusion (or takes a hand-drawn PNG through the exact same path), locks it to a master palette, and packs the atlas. It fails the build if the belt-item atlas spills onto a second texture page, because PixiJS'sParticleContainerneeds one shared texture.
Stack: Next.js 16 (App Router, Turbopack) · React 19 · PixiJS 8 as a library, no game
framework · TypeScript 6 · Vitest 4 · Supabase (magic-link auth, Postgres, RLS) · IndexedDB
via idb · sharp for the art pipeline · yarn 1 workspaces · deployed on Vercel ·
~118k lines of TypeScript across 92 test files.
Running it locally
Just want to play? You don't need any of this. The live game is at ageofpotatoes.com. The steps below are for running your own copy.
Prerequisites: Node 22+ (CI runs Node 24) and yarn 1.22 (classic).
git clone https://github.com/melucasleite/age-of-potatoes.git
cd age-of-potatoes
yarn install
Accounts are required, so the game needs a Supabase project.
- Copy
apps/web/.env.exampletoapps/web/.env.localand fill inNEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY. Without them,/playshows a "cloud is not configured" screen instead of the game. - Apply the migrations in
supabase/migrations(for a fresh project:supabase link --project-ref <ref> && supabase db push). - Add
http://localhost:3000to the project's redirect allow-list so magic links land back in/play. The rest of the dashboard switches the code depends on are listed insupabase/README.md.
Then start the dev server and open localhost:3000/play:
yarn dev
| Command | Does |
|---|---|
yarn dev |
Next.js dev server on port 3000 |
yarn build |
Production build of apps/web |
yarn typecheck |
tsc --build across the workspace, plus the sim's test project |
yarn test |
Vitest, including the golden determinism replays |
yarn lint |
The lint rules that enforce sim purity |
yarn format |
Prettier (run it after any change) |
yarn goldens:regen |
Re-hash the golden fixtures, only after an intentional sim change |
yarn art gen |
Generate art (--dry-run first, it's free) |
yarn art pack |
Post-process and pack the atlas |
yarn art review |
Write a self-contained contact sheet of every sprite |
CI runs exactly yarn lint, yarn typecheck, yarn build, and yarn test, with no
secrets. The atlas is committed, so the art pipeline (and its RETRODIFFUSION_API_KEY,
documented in the root .env.example) is only needed if you're making new art.
Repository layout
| Path | What lives there |
|---|---|
packages/sim |
The deterministic simulation. Pure TypeScript, zero dependencies, no DOM. |
packages/sim/test |
Unit tests and the twenty golden replay fixtures. |
packages/art-cli |
Asset pipeline: AI generation → post-processing → atlas packing. |
apps/web/app |
Next.js App Router shell: the splash page and /play. |
apps/web/src/game |
PixiJS renderer and input. |
apps/web/src/hud |
Menus, panels, and the rest of the HUD. |
apps/web/src/audio |
Synthesized sound bank and the beat-grid scheduler. |
apps/web/src/storage |
Local-first IndexedDB saves and the cloud handoff. |
apps/web/src/supabase |
Auth, cloud sync, and profiles. |
apps/web/src/reports |
Reports, personal bests, and the leaderboard panel. |
art/ |
Master palette, manifest, and committed generated originals. |
supabase/ |
Migrations and the dashboard checklist for accounts and cloud saves. |
docs/screenshots/ |
The images in this README. |
Contributing
Read AGENTS.md first. It documents the rules that are load-bearing rather than stylistic, and each one is there because breaking it fails silently.
And remember the design gate that outranks everything else in this repo: a fresh-eyes player should grin the moment the sprinkler waters for them, then immediately want a second one. If a change makes the manual farming less pleasant, it's a regression, no matter what it optimizes.
License
MIT © 2026 Lucas Leite
Screenshots were captured headlessly with Playwright against a production build. The busy worlds are the sim's own golden fixtures, replayed and loaded as saves, so every machine in them was placed by a real command on a real tick.
more projectsls ~/projects
~/ai-desloper
Desloper scores how generic an AI-written landing page is against eight principles, with deterministic detectors plus a model judge, then hands you a fix prompt built from the findings you keep.
~/mark85
Iron Man suit firmware: four limb IMUs drive synthesized stereo servo whir and repulsor charge-up.
~/claude-tts
Claude Code narrates its work out loud with offline neural TTS. Piper + FFmpeg, driven by hooks.
~/claude-usage-widget
Your Claude Code limits as Apple-Watch-style activity rings, with a forecast of when you'll hit them.
~/mini-kitchen
Toddler play kitchen with a sink that actually runs. CNC oak plywood, 12 V pump, Python-generated cut sheets.
~/speedster
A focused, dark-mode Rubik's cube timer for iOS: scrambles, rolling averages and progress charts.
