Top Shot Reloaded — 1v1 Speedball Duel
A phone-first paintball duel built solo from prototype to beta: a deterministic 120 Hz headless simulation, a WebGL renderer, a print-inspired design system, lockstep multiplayer over a Cloudflare relay, and native shells for iOS, Android, and desktop.

Overview
Top Shot Reloaded is a one-on-one top-down speedball duel: two players, two mirrored inflatable bunkers, 45 seconds on the clock, and the question of when to step out and shoot. I have been building it solo since June 2026 as designer, engineer, and the person who writes the release notes — TypeScript and Vite on the web, wrapped with Capacitor for iOS and Android and Tauri 2 for macOS and Windows.

The core idea: position is the trigger
No ammo count, no fire rate to manage. Every bunker has staged spots — left lane, right lane, forward over the top, back in deep cover — and each spot has two states: tucked and silent, or stepped out with the marker running itself. Holding FIRE steps you out; releasing ducks you back. Repositioning costs a beat and stepping out is a commitment, so every decision is a spend rather than a twitch. Paint behaves like paint: spread cones, drag, gravity drop, bounces off the bags, and trades — a ball already in the air can still take out the player who just shot you.

What I built
- A headless, deterministic simulation on a fixed 1/120 s step with a seeded, split random stream, a world hash, and input-log replay — the sim never touches the DOM, so the same match can be re-run and verified anywhere
- A WebGL render pipeline (three.js) over that sim, with a canvas2d fallback: characters baked from a rigged low-poly mesh, real bunker meshes from the NXL/PSP kit, paint splats, verdict wipes, and a strict one-way core → sim → render dependency firewall
- An AI rival that decides every 0.3–1.2 s to peek, suppress, tuck, or hold, with a humanlike aim delay — five named rivals with distinct habits
- Hold-and-steer touch controls: a position pad, an aim pad, and a FIRE button, magnetised so a thumb stays in its zone until it clearly commits to another
- Career stats, run records, and a replay-verifiable leaderboard spine, persisted through a platform layer so saves survive on web, iOS, Android, and desktop
- Lockstep netcode: a WebSocket relay on a Cloudflare Worker with Durable Objects, a wire protocol, a latency ladder, and a mirrored guest view, so two real engines play one relayed match with identical worlds
- Around 1,760 tests across 97 files, including freeze guards that fail CI if a frozen visual surface changes and vocabulary tests that pin every player-facing string
A design system with two voices
The visual identity, "Match Data", is print-inspired and value-driven: a warm putty-and-ink neutral ladder, one amber SIGNAL accent, and a per-match neon paint that is the only loud colour on screen. It speaks in two registers. STATEMENT is in-match: Anton slams, tilted verdict wipes, ref calls like GAME ON!, HIT!, and TRADE!. PAPER is everywhere else: calm print furniture, tracked caps, hard offset shadows, zero exclamation marks. Identity comes from structure — ink frames, radius zero, down-right shadows — not from colour.
The system ships as a tracked bundle in the repo: tokens, component specs, motion tables, and a ratified vocabulary that resolved three generations of contradictory copy into one table, each row pinned by a test.

Proving determinism before building on it
Multiplayer, replays, and the leaderboard all rest on one claim: two simulations fed the same inputs produce the same world, bit for bit. Rather than argue it, I measured it — 200 seeds × 3,000 ticks across two independent worlds joined only by a modelled link, at clean, 60 ms, and 200 ms round-trip latencies and at frame rates from 60 to 240 fps, with zero divergence. Then two real engines played a complete race to five through the live relay with identical worlds and phases throughout. An adversarial audit of the mirrored guest view raised 26 findings and confirmed 18, including three defects that hash comparison could never catch, because both clients were identically wrong.
Bunkers that block like bunkers
The field draws from the real inflatable shapes players snap-shoot behind — doritos, cans, cakes, temples, the X, the Rollie. The latest rules revision replaced every bunker's rectangular hitbox with its actual outline. Paint threaded past a bag's corner used to die on empty air; now it flies through, and ricochets come off the true curve. Each rules change bumps a versioned config that carries the measurements behind it, and it invalidates old replays on purpose, because the collision geometry is part of the rules.
Status
In private beta. Practice against the AI rival works offline, private matches run over the relay, and matchmaking is not built yet. The iOS shell builds through Capacitor and the desktop shells through Tauri 2. Next up: the on-device performance pass, the server side of the leaderboard, and store submission.
The game is in active development. If you'd like to playtest the beta on iOS, Android, or desktop, get in touch and I'll send you a build.

