A perfect clone of 1993 game SimTower.
See the code
A tick-for-tick clone of SimTower (Yoot Saito / Maxis, 1993), reimplemented as a multiplayer browser game. The simulation matches the original Windows 3.1 binary tick-for-tick — crucially, the sim state machine and elevator dispatch code aims to match exactly.
Live at towers.world.
The original SimTower is a single-player game where you build a 100-story tower out of offices, hotel suites, condos, restaurants, shops, and elevators, and try not to go bankrupt servicing the simulated tenants who move in. tower-together puts that simulation behind a Cloudflare Worker so several people can build the same tower together in real time.
The fidelity goal is strict: given the same input sequence, the reimplementation produces byte-identical state to the original binary on every tick. The trace test in apps/worker/src/sim/trace.test.ts replays gameplay traces captured from the original DOS binary running under emulation and asserts the TypeScript sim produces the same state at every tick.
TowerRoom) per tower. The DO is the authoritative input sequencer; clients submit inputs, the DO orders them into batches, and every client steps the same sim against the same input stream.SIMTOWER.EX_.You need Node 20+ and npm 10. Install once:
npm install
Then run both apps:
npm run dev
That starts the Vite dev server for the client and wrangler dev for the worker via turbo. Open the URL Vite prints.
To run just one side:
npm run dev -- --filter=tower-together-client
npm run dev -- --filter=tower-together-worker
The interesting test is the trace replay:
cd apps/worker
npx vitest run src/sim/trace.test.ts --testTimeout=30000
Each fixture in apps/worker/src/sim/fixtures is a .jsonl file of (tick, input, expected_state) tuples captured from the original binary. The test feeds the inputs into TowerSim and diffs the resulting state against the binary's state on every tick. A divergence on tick N usually means a bug on some earlier tick whose effect only became observable at N — fix the earliest divergence first.
To regenerate fixtures, see simtower/emulator.py (requires the original SIMTOWER.EX_, which is not in this repo).
Other useful targets:
npm run typecheck
node_modules/.bin/biome check .
Run both before committing.
npm run deploy
Builds the client into the worker's dist/, then wrangler deploys the worker. The client is served as static assets from the same worker that hosts the Durable Objects.
MIT. SimTower itself is © Maxis / OPeNBooK / Yoot Saito; this project is a clean-room reimplementation and ships none of the original assets or code.
461 commits
TypeScript
99.9%
A perfect clone of 1993 game SimTower.
See the code
A tick-for-tick clone of SimTower (Yoot Saito / Maxis, 1993), reimplemented as a multiplayer browser game. The simulation matches the original Windows 3.1 binary tick-for-tick — crucially, the sim state machine and elevator dispatch code aims to match exactly.
Live at towers.world.
The original SimTower is a single-player game where you build a 100-story tower out of offices, hotel suites, condos, restaurants, shops, and elevators, and try not to go bankrupt servicing the simulated tenants who move in. tower-together puts that simulation behind a Cloudflare Worker so several people can build the same tower together in real time.
The fidelity goal is strict: given the same input sequence, the reimplementation produces byte-identical state to the original binary on every tick. The trace test in apps/worker/src/sim/trace.test.ts replays gameplay traces captured from the original DOS binary running under emulation and asserts the TypeScript sim produces the same state at every tick.
TowerRoom) per tower. The DO is the authoritative input sequencer; clients submit inputs, the DO orders them into batches, and every client steps the same sim against the same input stream.SIMTOWER.EX_.You need Node 20+ and npm 10. Install once:
npm install
Then run both apps:
npm run dev
That starts the Vite dev server for the client and wrangler dev for the worker via turbo. Open the URL Vite prints.
To run just one side:
npm run dev -- --filter=tower-together-client
npm run dev -- --filter=tower-together-worker
The interesting test is the trace replay:
cd apps/worker
npx vitest run src/sim/trace.test.ts --testTimeout=30000
Each fixture in apps/worker/src/sim/fixtures is a .jsonl file of (tick, input, expected_state) tuples captured from the original binary. The test feeds the inputs into TowerSim and diffs the resulting state against the binary's state on every tick. A divergence on tick N usually means a bug on some earlier tick whose effect only became observable at N — fix the earliest divergence first.
To regenerate fixtures, see simtower/emulator.py (requires the original SIMTOWER.EX_, which is not in this repo).
Other useful targets:
npm run typecheck
node_modules/.bin/biome check .
Run both before committing.
npm run deploy
Builds the client into the worker's dist/, then wrangler deploys the worker. The client is served as static assets from the same worker that hosts the Durable Objects.
MIT. SimTower itself is © Maxis / OPeNBooK / Yoot Saito; this project is a clean-room reimplementation and ships none of the original assets or code.
461 commits
TypeScript
99.9%