← All projects

Case study · Side project

Market Floor: prices nobody scripted

A browser trading game where every price comes out of a real order book. 62 computer traders with different strategies compete under the same rules as the player, so the market has to hold together on its own.

When 2026Stack TypeScript · Supabase · Vercel

62Computer traders
340+Unit + end-to-end tests
100Games per balance run
<1sServer replay of a full season

01The problem

Most trading games script their price charts. That's easy to build, but players learn the pattern and the market stops feeling alive. I wanted the opposite: a price that is only ever the last trade in an order book, with realistic behaviour (spreads, slippage, trends, panics) coming out of the traders instead of a formula.

That turns a game into a systems problem. The market has to stay liquid, prices have to stay sane over a 250-day season, and every run has to be reproducible, or nobody could trust a leaderboard.

02Architecture

The core decision: the engine is pure TypeScript with zero DOM access, and all of its state is plain JSON. The browser drives Game.step() from an animation loop; a headless script drives the same function in a plain loop; the server runs it too. Saving is JSON.stringify(state).

Market Floor architecture The browser UI and a headless balance script both drive the same deterministic engine. Finished runs are sent as recorded commands to a Supabase edge function, which replays them with the same engine bundle before writing the score to Postgres. Browser UI charts · order ticket Balance harness 100 headless games Engine (pure TS) seeded RNG streams hidden fair value + news order books + matching 62 trader strategies accounts · margin · P&L Edge function replays recorded commands, same engine Postgres verified scores · leagues step() step() bundle if it matches
One engine, three callers: the game, the balancing harness and the server that checks scores.

03A market that holds together

Each commodity has a hidden fair value: a mean-reverting random walk in log space, with seasonality and news shocks. Nobody sees it, including the traders. They estimate it, and the price only moves when they trade.

Order book

Limit and market orders, price-time priority, partial fills, self-trade prevention and depth-based cost estimates. Big market orders walk the book, so the player pays slippage and moves the price.

Market makers

Quote both sides with inventory skew and spreads that widen with volatility and fresh news, which keeps the book two-sided through shocks.

Strategies

Trend followers, contrarians, value investors, panic sellers, whales and noise traders, plus institutions that work big orders over days and a stop-loss crowd whose stops cascade through recent highs and lows.

News

51 headline templates. Some land instantly, some days later, and about one rumor in five is false: it never moves fair value, but traders believe it until the denial reaches them.

04Scores the server can prove

A leaderboard is only as good as its weakest client. Instead of trusting a submitted number, the game sends the run's recorded commands. A Supabase edge function loads the same engine bundle the site serves (checked against its content hash), replays the whole market with those commands and records the score only if the result checks out. A 250-day game replays in well under a second.

This only works because of the early decision to keep the engine pure and deterministic. The same property powers save and load, "ghost" races against your best run and narrated replays.

05Testing and balancing

06Stack

TypeScriptVitelightweight-chartsSupabase · Postgres · Edge FunctionsVitestPlaywrightVercelPWA · offline play

Why it's on this page

It's the same kind of work I do in data platforms, at game scale: many independent producers, a system that must stay consistent under load, and results you can reproduce and verify instead of trusting.