~/projects / first-million
First Million
Real-time stock trading game: pick your starting capital and goal, trade against the clock, or race up to 3 rivals in a shared room where everyone trades the same price.
- role
- full stack: shared simulation engine, realtime backend, Vue frontend
- time
- Oct 2026
- type
- personal
- stack
- TypeScript · Vue 3 · Vite · Cloudflare Workers · Durable Objects · Firebase Auth · Zod
01 / problem
A market game needs one price for everyone
A trading game feels fair only when every player in a room trades the same price and the round ends for everyone at the same moment. The price cannot come from each browser, or players would see different markets. Nothing is stored between rounds, and the whole game has to run at zero cost.
02 / solution
The server owns the price, the browser sends intent
Players pick a round length of 3, 5 or 10 minutes, a starting capital from $1,000 to $100,000 and a goal with no upper limit, then buy, sell or close a position in sizes from 10 to 250 units. Speed runs at 1×, 3× or 8×, and the round can be paused. A room of up to four players trades one price from a Durable Object, and a player whose equity falls to zero is eliminated. The browser only sends orders; the server checks them and runs the round.

Setup: starting capital, goal and round length

Margin call: equity falls to zero and the round ends
03 / architecture
How it works
A pnpm monorepo with three packages. shared is a pure TypeScript engine with no platform APIs: the random generator and the elapsed time are passed in, so the same rules run in the browser for single play and in the Worker for rooms. worker runs on Cloudflare Workers, with a Lobby and one Room Durable Object per room, and verifies Firebase ID tokens. web is a Vue 3 app that renders state produced by pure reducers. State lives in memory only, with no storage, KV or D1.
04 / results
Outcome
05 / lessons
What I learned
- Writing the engine as pure functions with the random generator and the clock passed in made the same rules testable with a fixed seed and a fake clock, in both single play and rooms.
- Keeping state in memory only means a restart ends the rooms in progress, but it keeps the model simple and the cost at zero.