Skip to content

~/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
demo.mp4

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

    Setup: starting capital, goal and round length

  • Margin call: equity falls to zero and the round ends

    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.