Ready To Race
Unlimited
A Browser Multiplayer Car-Racing Arena - Built with Next.js & Vibe Coding
Ready To Race Unlimited is a fully playable, real-time car-racing arena that lives entirely in the browser - odds, betting, a 60fps race simulation and a neon e-sports skin, with no backend at all. It's also an experiment in vibe coding: building a complete game by steering an AI conversationally, prompt by prompt, system by system.
The Build Challenge
A racing game looks simple until you build one. It needs a race that's exciting but fair, betting math that actually pays out correctly, animation that stays smooth on any machine, a grid full of opponents, sound, and persistence - and here, all of it had to run client-side with zero server. The twist: the whole thing was built through vibe coding, so the real challenge was decomposing a game into prompts an AI could implement cleanly, then reviewing and steering the output.
"The hard part of a betting race isn't the visuals - it's making outcomes feel random and dramatic, yet provably fair and reproducible."
Technical Problem
Simulate a believable race with live lead-changes, drive an odds-based betting economy, and animate it all at 60fps - entirely in the browser, with no backend to authoritatively run the race.
The Goal
A self-contained Next.js game: deterministic seeded races, six cars with real stats, working bets and payouts, bot opponents, audio and a polished neon e-sports UI with swappable palettes.
The Constraint
Build it by vibe coding - describing each system to an AI and iterating - which demands a clear mental model of the architecture so the generated code stays consistent and debuggable.
Tech Stack & Approach
The whole game is a single Next.js (App Router) application bundled with Turbopack and deployed to Netlify. There's no database and no game server - every car, bet, race and saved stat lives in React state and the browser's localStorage. State is driven by hooks and reducers (the UI alone runs dozens of useState and useReducer stores), and the race itself is a hand-rolled simulation on requestAnimationFrame.
Naive Build vs. How It's Engineered
- Move cars with
Math.random()each frame - janky and unfair - Tie game speed to frame rate, so fast machines race faster
- Hard-code one look and recompute payouts ad-hoc per bet
- A seeded PRNG so a race is deterministic and replayable
- A fixed-timestep loop - identical physics at any FPS
- Token-driven palettes and a single odds → payout formula
The Stack, Layer by Layer
"A single client-side Next app - no server, no database. The browser is the whole game."
"A hand-written race loop - velocity from stats, jitter for drama, finish detection per tick."
"Swappable neon palettes via CSS custom properties, plus countdown & ambient sound."
Core Systems at a Glance
Architecture & Game Loop
The Phase State Machine
The entire game is driven by one screen-phase state machine. Each phase owns its own UI and logic, and transitions are explicit - which keeps a complex game readable and made it easy to build phase-by-phase while vibe coding.
The race runs on a fixed-timestep loop: a requestAnimationFrame callback accumulates real elapsed time and advances the simulation in fixed 16ms steps (capped at four catch-up steps per frame). Physics is therefore identical whether the device runs at 30, 60 or 144Hz - and a pause flag simply re-queues the frame without advancing time.
Core Modules
Race Engine
Seeded RNG · Fixed-step loop · Finish detect
Betting & Odds
Wagers · Payout math · Live wager feed
Bots & Rooms
AI opponents · Public / private rooms
FX & Persistence
Audio · Palettes · localStorage
Game Systems & Visual Language
Every car is a small data object - name, driver, colour, odds, recent form and two stats: speed and handling. The race converts those stats into a base velocity, then adds controlled randomness so no two runs are identical. Because the randomness flows from a single seed, the same seed always produces the same race - the basis of a fair, reproducible result.
// each car advances per fixed 16ms tick
seed = roomSeed ?? Math.floor(1e4 * Math.random()) + 1
baseVel = 0.55 * (0.6*speed + 0.4*handling) / 100 + 0.1 * rng()
strideΔ = (rng() - 0.5) * 0.2 // ± jitter → lead changes
position += baseVel + strideΔ // first past the line wins
payout = stake * car.odds // e.g. 50cr on 4.0x → 200crThe six cars are tuned so odds, stats and drama line up: the favourite Thunder Strike sits at 2.5× with high speed, while the long shot Blue Thunder pays 12× - a bigger gamble with weaker odds. A 60% speed / 40% handling weighting decides the baseline, and the per-tick jitter is what creates the lead-changes that make a race worth watching.
A three-font e-sports system: Orbitron for display & numbers, Rajdhani for UI text, and JetBrains Mono for data, timers and stats - all loaded via Google Fonts.
A deep-violet base (--bg-0 → --bg-3) lit by neon accents - orange, cyan and pink over a violet glow - plus a win-green. The whole skin is token-driven, so palettes (Default · Cyber · eSports) swap with one attribute.
In-Game Screens
The arena across its phases - home hub, the matchmaking lobby, grid sync, the betting board and the live race. Click any screen to open it in high resolution.




















The Vibe-Coding Build Process
Vibe coding only works if you bring the architecture. I drove the build in deliberate passes - scaffolding the app, then growing one system at a time, prompting the AI for each piece, reviewing the output, and refactoring before moving on. Treating the AI like a fast pair-programmer (not a vending machine) is what kept a feature-rich game coherent.
1 · Scaffold
Spun up a Next.js + TypeScript app on Turbopack, defined the screen-phase state machine, and stubbed each phase as an empty component so the skeleton was navigable first.
2 · The Race Engine
Built the heart of the game next - the car data model, seeded PRNG, the velocity formula and the fixed-timestep requestAnimationFrame loop - and tuned it until races felt fair and tense.
3 · Betting & Bots
Layered the economy on top: odds, the stake × odds payout, credit balances persisted to localStorage, plus bot opponents and a live wager feed to fill the grid.
4 · Juice
Added the feel - Orbitron/Rajdhani type, the token-driven neon palettes, countdown and ambient audio with volume fades, and Web-Animations transitions between phases.
5 · Ship
Wired up rooms, ranks, seasons and the daily bounty, did a polish-and-bugfix pass, and deployed the static build to Netlify as a no-backend, fully client-side game.
Outcome & Impact
Key Outcomes
"A fully playable betting race - deterministic, 60fps and backend-free - built end-to-end through vibe coding."
📈 Outcome
A shipped, playable web game: six stat-driven cars, odds-based betting and payouts, a seeded fixed-step race engine, bots, rooms, ranks, seasons, audio and a swappable neon e-sports skin - all client-side.
🧗 Challenge
Making races feel random yet fair and reproducible, keeping the simulation smooth across frame rates, and holding a large codebase coherent while generating most of it through AI prompts.
💡 What I Learned
Vibe coding rewards architecture. The clearer my model of the engine, state machine and economy, the cleaner the generated code - the AI accelerates building, but the design decisions stay yours.
🚀 Next Steps
Add real-time multiplayer over WebSockets, server-authoritative seeds for trust, more tracks and cars, and a proper progression economy beyond local persistence.
