Full-stack · 2026

FrameShift Arena

Deterministic turn-based PvP game with one serverless function and 2,096 tests

Live

Overview

  • What: a full-stack PvP web game where players build squads of original shape-shifting combat units under a hard power budget and battle other players turn by turn.
  • Hardest problem: keeping battles deterministic and race-free, so the server is the only authority and a replay always matches the live match.
  • How it is validated: 2,096 tests across 104 files, with engine golden-log snapshots; each server test boots its own PGlite database.

The engine is pure and deterministic and is shared by the API and the SPA. The server snapshots both squads onto the match row and stamps the seed and engine version, so old replays survive later rebalances. The repository is private; the live demo is the public artifact.

Architecture

FrameShift Arena action sequence The client sends an expectedVersion and an action to the Hono /api serverless function, which runs UPDATE ... WHERE version = ?. A version mismatch returns 409. On success an event is appended to the event log, and the single-page app folds the log for playback and replay. client · {expectedVersion, action} Hono /api · one serverless function UPDATE … WHERE version = ? 409 · version mismatch event log append SPA folds the log → playback / replay
fig. 01 — frameshift arena action path · optimistic concurrency, replay by folding the event log

Highlights

  • Pure, deterministic, dependency-free TypeScript engine (v3.2.0) in shared/engine, imported by both the API (authoritative resolver) and the SPA (playback).
  • The client's entire vocabulary is two squad ids to start a match and {expectedVersion, action} per turn; the server snapshots both squads onto the match row and stamps seed + engine version, so replays survive future rebalances.
  • Optimistic concurrency: a conditional UPDATE ... WHERE version = ? serializes racers and the loser gets a 409.
  • The battle UI is a fold over the persisted event log: one cursor mechanism drives both live animation and the replay viewer, and no battle math runs in React.
  • Exactly one Vercel serverless function (api/index.ts, a Hono router); Vercel Hobby caps non-framework projects at 12 functions, so the cap is made structurally unhittable.
  • 54-sector world, frozen 3,063-species catalogue, bots, rogue operators, live and asynchronous PvP, matchmaking, spectating, trading, auctions, clans, seasons and achievements.
  • GitHub Actions CI (structural guards, typecheck, lint, tests, build, then migrations); PWA with service worker and offline page; free-tier infrastructure that pauses rather than bills.

Screenshots

FrameShift Arena landing page with the headline Two forms. One budget. Zero mercy., an Enlist Now button and a four-step how-it-works row
Landing page, desktop

What I learned

Putting all battle math in one pure module, with the UI as a fold over the event log, meant live play and replay became the same code path instead of two features to keep in sync. Treating the 12-function Vercel Hobby cap as a design constraint, and enforcing a single /api function with a structural guard script, was better than hoping not to hit it.

Limits

Phase 3 is complete (milestones M19 to M25). The source repository is private, so only the live demo is linked.