Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
Drive a Gen3 Camaro or Mustang Supercar around a real-scale Mount Panorama, Bathurst.
Translation pending
GamesTypeScript · JavaScriptGitHub @vanderhakaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Browser racing game on a real-scale Mount Panorama Circuit, Bathurst. three.js + TypeScript + Vite.
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A browser racing game on a real-scale Mount Panorama Circuit, Bathurst NSW, with
generated (code-only) medium-poly Gen3 Supercars: the Chevrolet Camaro ZL1, the
Ford Mustang GT and the Toyota GR Supra (new in 2026), plus a tribute car: Peter Brock's 1979
Holden Torana A9X hatchback #05 with 1979 Group C physics. Built with three.js, TypeScript and Vite.
The work order and item status are in docs/PLAN.md: graphics realism (toward about 75 % of the way to photoreal), phone strength, then racing realism.Read the full README ↗A browser racing game on a real-scale Mount Panorama Circuit, Bathurst NSW, with generated (code-only) medium-poly Gen3 Supercars: the Chevrolet Camaro ZL1, the Ford Mustang GT and the Toyota GR Supra (new in 2026), plus a tribute car: Peter Brock's 1979 Holden Torana A9X hatchback #05 with 1979 Group C physics. Built with three.js, TypeScript and Vite.
The work order and item status are in docs/PLAN.md: graphics realism (toward about 75 % of the way to photoreal), phone strength, then racing realism.
This run ships to the codex/bathurst-plan preview branch. Main and production
stay at their existing revision. Phase 1 adds measured afternoon light, AgX tone
mapping, cascaded shadows, generated asphalt and terrain detail, eucalyptus crowns,
trackside detail and High-only AO, bloom, SMAA and camera effects. The fixed photo
round is in docs/REALISM.md; user review is pending. The rejected
car-material and floor-spark candidates are recorded in the plan rather than shipped.
High/Medium/Low pass the four frozen Mac evidence points. Real iPhone Medium
(three laps, at least 50 fps) and Low remain pending. See docs/VERIFICATION.md
for the per-tier feature audit and the limits of phone emulation.
npm install
npm run dev # http://127.0.0.1:5180/
Production build: npm run build, then npm run preview (http://127.0.0.1:5181/).
Choose Circuit on the title screen to step through Mount Panorama, Adelaide Parklands and Surfers Paradise. Switching reloads the scene.
?track=adelaide. The current 2026 Supercars
layout is 3.219 km, clockwise, with 14 numbered turns and generated
city/parkland surroundings. Sources and estimates:
docs/research/adelaide.md.?track=gold-coast. The
2025 Gold Coast 500 layout is 2.960 km, anticlockwise, with 15 numbered turns,
the beach chicane, the OSM high-rise skyline, the beach and the Broadwater.
The hairpins, the beach chicane and Hill Parade are measured from Queensland
Government aerial photos. The G:link tram and the public Gold Coast Highway
run beside the pit straight, and three pedestrian bridges cross the track.
Sources and estimates:
docs/research/gold-coast.md.Elevation, road widths, sectors and timing lines on both street circuits are documented estimates.
Both Shootout modes run at Bathurst with the Camaro, Mustang, or Supra. After picking a car, every run asks where the warm-up starts: the grid (pole position, start lights and one full warm-up lap; the default) or after Forrest's Elbow (the car is held for a 3, 2, 1 countdown on Conrod Straight just past the Elbow, then rolls on warm tyres). The last answer is remembered per mode in this browser. Then comes one full timed lap from the line. Both use full damage, track limits, tyre wear, automatic gears, soft tyres, and the standard setup. ABS, traction control and steering assist are on for everyone (phone and keyboard pedals are on/off, so without them every brake press locks the wheels). Racing lines and automatic recovery are disabled; Top 10 shows no ghost, while Arcade races the current #1 lap for the car (or your own Arcade best). Steering sensitivity, touch auto-throttle, analog pedals and other controls stay the player's choice in Settings during a run. On a touch screen, every run then asks whether to steer with Finger or Tilt. Tilt drives straight when the phone's left-right axis is level, however upright or flat it is held; Centre tilt makes the angle held straight ahead (up to 15° off level). Touch controls include gear buttons when manual gears are active.
Each warm-up and flying lap ends at the 10-minute limit without a score. The result screen explains the timeout and offers another warm-up or the main menu. A warm-up timeout uses no competition attempt. A timed Top 10 timeout keeps the attempt used; Arcade practice remains unlimited.
The boards show the current season only: all cars together, or one car. Each shows the fastest published lap per browser, the Top 10, and refreshes every 15 seconds. After publishing, the result shows the lap's place among every browser this season. Board laps carry a replay that Arcade can race as a ghost. The introduction shows the rules, remaining attempts, and previous results. Competition is unavailable until the shared database is configured. Arcade remains available.
DEFAULT_HANDLING is 1:56.49 for the Mustang and Supra (sectors 48.30, 30.51 and
37.69 s) and 1:56.63 for the Camaro. Laps under 1:52 or with sectors under 46, 29
or 36 s (about 97 % of the fastest ideal, rounded down) are refused by the game,
the API and the database. tests/shootout-floors.test.ts recomputes them.GET /api/shootout?car=all|camaro|mustang|supra returns { available, season, car, entries }, where season is { id, startsAt, endsAt } and each entry is { id, rank, nickname, car, timeS } (id is the attempt id). GET /api/shootout?car=…&replay=RANK returns { season, car, rank, timeS, replay }. Both
are cached for 10 s at the edge. POST takes { action: 'start', format: 'top10', browserToken, requestId, car, number, season } and returns { attempt }, or
{ action: 'submit', format: 'top10', browserToken, attemptId, nickname, timeS, sectorsS, replay? } and returns { publication: 'published', rank, of }. Errors are
{ error } with a short message.
The dedicated Bathurst Shootout project runs in Sydney. All its migrations, including the five seasons parts, are applied (10 October 2026), and the local server and Vercel production environment have their credentials. The game source still needs a release to activate the competition on the public website.
20261010100000 to 20261010100400), each small enough for one remote migration
call. They are safe to apply before the matching API deploys, and again: the first
migration's RPCs keep their signatures and responses (under the season rules), and the
new API calls shootout_start_attempt, shootout_submit_lap, shootout_board and
shootout_replay. Old rows in shootout_ip_starts stop counting after a day; prune
them with delete from public.shootout_ip_starts where started_at < now() - interval '1 day';
if the table ever grows large.SHOOTOUT_SUPABASE_URL and SHOOTOUT_SUPABASE_SERVICE_KEY in the server
environment. Use a server secret API key. Optionally set SHOOTOUT_IP_SALT to a
long random string (without it, the service key salts the IP hashes). For local
development, copy .env.example to the ignored .env.local file and restart Vite.
For the hosted game, use Vercel environment variables.api/shootout.ts together. Verify that GET /api/shootout
returns available: true, then verify a published test score from another browser.The service key stays on the server. The database denies direct access to browser roles and enforces three allocations per browser per season, the start limits, retry-safe requests, and immutable scores. No login is required. Identity follows browser storage, so clearing that storage creates a new identity. Lap times come from the client simulation; the floors, timing and replay checks make faking a lap harder, but this is not a server-authoritative anti-cheat system.
To hide a score (it leaves every board, and that browser's next best lap counts),
run this in the Supabase SQL editor with the attempt id from the board (entries[].id):
update public.shootout_attempts set hidden = true where id = '00000000-0000-4000-8000-000000000000';
-- Every lap from the same browser:
update public.shootout_attempts set hidden = true
where browser_hash = (select browser_hash from public.shootout_attempts where id = '00000000-0000-4000-8000-000000000000');
Set hidden = false to restore it. bash supabase/tests/verify-shootout.sh runs the
SQL tests in a throwaway PostgreSQL 17 container; without Docker, run it with
SHOOTOUT_TEST_LOCAL_PG=1 and the usual PGHOST/PGUSER variables to use a local
server (it creates and drops a temporary database).
node scripts/verify-shootout-live.mjs checks the local game against its configured
hosted database. It uses controlled lap positions and the actual API. It creates a
test score and three attempts. The report in artifacts/shootout-live/report.json
identifies its browser hash and attempt IDs for scoped cleanup after verification.
| Action | Keyboard | Gamepad (standard mapping) |
|---|---|---|
| Steer | ← → or A D | Left stick |
| Throttle / brake | ↑ ↓ or W S | RT / LT |
| Reverse (automatic gears) | Stop, release the brake, then press and hold it to drive backwards | Same with LT |
| Shift up / down | E or Shift / Q or Ctrl | A / X |
| Change camera (chase, far chase, bonnet, cockpit, TV) | C | RB (R1), like Gran Turismo 7 and F1 |
| Look back (hold; from the in-car views a rear roof camera) | V | B |
| Reset to track (also repairs the car; also in the pause menu) | R | View |
| Pause (press again to resume) | Esc or P | Menu (Options on PlayStation) |
| Ghost on/off | G | LB |
| Racing line (off / braking / full) | L | Y (△) |
| Hide HUD | H | — |
| Graphics tuner (a Mac sends F2 to the screen brightness, so use T) | T or F2 | — |
Damage (Settings, under Driving assists) has three modes:
The gamepad column uses the Xbox names. A PlayStation controller (DualShock 4 or DualSense, USB or Bluetooth) works with the same buttons: RT/LT = R2/L2, A/B/X/Y = ✕/○/□/△, LB/RB = L1/R1, View = Share (PS4) or Create (PS5), Menu = Options. When a PlayStation controller is connected, the menus, hints and controls screen show its symbols and layout. Press a button once after you connect a controller, because the browser shows a gamepad to the page only after a press. Chrome, Edge and Safari read both controllers in the standard mapping. Firefox can read a PlayStation controller in a different mapping.
docs/research/circuit-facts.md).docs/research/car-specs.md. The Supra uses Toyota's 5.2 L quad-cam V8 (Lexus 2UR-GSE based) with its own firing order and engine sound. Parity rules give the three Gen3 cars the same power, so they lap in about the same time. The Torana A9X (section 12 of the same doc) has a 5.0 L Holden V8, a four-speed box, no downforce and its own tyre heat gain; with the measured handling it laps about 22 s slower than the Camaro (151 s against 129 s), as the real 1979 car would on today's circuit. Its first livery follows the 1979 #05 colour zones with plain blocks where the sponsor words were; every other livery is fictional, and no livery carries a real logo or sponsor name.MEASURED_HANDLING), the ideal lap of the racing-line profile is 2:05.7. The real Gen3 pole is 2:04.0, and the race lap record is 2:06.7.DEFAULT_HANDLING in src/config/handling.ts): 20 % more tyre grip, 10 % more rear grip and downforce, and a softer, more forgiving limit. With it, the ideal lap is 1:57.5, and the test AI (90 % of the grip) laps both cars cleanly in about 2:04.Press L to cycle the line: off, braking zones only, full line.
The colours and the HUD "next corner" speed use a profile that a real driver can hold (92 % of the grip, braking in a straight line). A test driver that only obeys the colours laps both cars without contact (tests/line-follower.test.ts).
The cockpit view has a live rear-view mirror (a small second render at half rate, without your own car) and a working dash display. In this view the timing tower moves below the mirror.
Best laps, sectors, the delta trace and the ghost are saved per circuit and car in the browser (localStorage keys bathurst.records.v2.<car>, adelaide.records.v2.<car> and gold-coast.records.v2.<car>). Existing Bathurst records keep their original key. A lap counts only after you drive at least 90 % of it forwards. The standing-start lap (from the grid) is shown, but it never becomes your best lap, delta reference or ghost. Version 2 drops records from older builds, because those builds could save a ghost that replayed too fast.
The game runs in the phone's web browser (Safari on iPhone, Chrome on Android). It plays in landscape only: in portrait, a note asks you to turn the phone, and a running race pauses.
Touch controls show while you race, after your first touch:
| Control | Use |
|---|---|
| Steer | Put your left thumb down anywhere on the left half of the screen, then drag it sideways. |
| Throttle and brake | Use the two pedals at the bottom right. Slide your thumb from one pedal to the other. |
| II | Pause. |
| View | Change the camera. |
The Handling tab and the graphics tuner are dev tools. They show in local builds and on Vercel preview deployments, but not on the production deployment.
The car feels a change at once, and the game saves it. The taller tick under each meter shows the default. Reset handling puts all values back to the defaults. The help text for each value gives the measured car's value.
| Value | Effect |
|---|---|
| Tyre grip | Grip of all four tyres (1.00 = the measured car). |
| Grip in a slide | Grip that stays when the car slides (0.59 = the measured car). Higher = slides are easier to catch. |
| Rear grip | Above 1.00 = a more stable rear. Below 1.00 = a looser rear. |
| Peak slip angle | Higher = the grip limit comes on more gently. |
| Downforce | Grip at high speed. |
| Steering speed | How fast the front wheels turn to the steering input. |
The racing-line colours and corner-speed hints follow handling, fuel load and actual tyre grip once per simulation second. Fresh warm soft tyres preserve the tuned grip; cold, overheated or worn tyres have less grip. The compound windows and wear rates are game estimates, since measured Gen3 Dunlop curves are unpublished.
Every build has Settings > Graphics and audio > Frame rate limit (30, 60, 120 or Max). Every build also has Settings > Steering: a sensitivity for each device (controller, keyboard and touch), from 50 % to 200 %. A higher controller value gives more steering near the centre of the stick, and full stick is always full lock. A higher keyboard value turns the wheel faster. A higher touch value needs a shorter thumb drag for full lock.
All visual values live in a small set of config files:
src/config/graphics.ts: lighting, colour grade, world density.src/art/palette.ts: colours.src/car/models/look.ts: cars.src/props/look.ts: trees and props.src/hud/theme.css: HUD.| Path | Contents |
|---|---|
scripts/ | Track data builders (OSM, SRTM, contours), screenshot, play and evidence tools |
src/track/ | Track model, layout, racing line, speed profile, kerbs |
src/physics/ | Tyres, suspension, powertrain, collisions, damage |
src/world/ | Terrain, road, barriers, sky and lighting, scenery placement |
src/car/models/ | Generated Camaro, Mustang, Supra and Torana models |
src/props/ | Generated trees, people, tents, buildings and structures |
src/hud/, src/ui/ | Broadcast-style HUD and menus |
src/audio/ | Procedural V8 engine, tyre and impact sound |
src/game/ | Game loop, race session, cameras, input bridge |
docs/references/ | 642 checked reference links |
docs/research/ | Circuit facts and car specs, with sources |
npm test # 130+ unit tests (physics lap test, timing, ghost, input, HUD, props, cars, audio)
node scripts/verify-shootout.mjs # native menu flow and phone layouts with an explicit API/lap fixture
bash supabase/tests/verify-shootout.sh # migration, permissions, RPCs, and concurrent quota in a temporary Docker Postgres
node scripts/capture-evidence.mjs artifacts/review/latest # frozen evidence set + metrics.json
The independent review rubric is in docs/VERIFICATION.md.
The cars and liveries are generic and do not use real team or sponsor branding.