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
battle royale win for pot
Translation pending
GamesTypeScript · CSSGitHub @looterstudioOnlineOwner 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
Loading the full README. The saved excerpt is shown below.
A battle royale drawn in ballpoint on graph paper, for up to 100 players per match: third person over the shoulder (or first person, P), with every wall breakable.
Glide in, loot guns, shields and perks off the floor and out of pencil cases, and fight your way to
the King's Tower in the middle of the island: the final circle always closes on it. Ten guns from
common to legendary (the SCAR and a one-shot Heavy Sniper are the prizes), shields and medkits, and
five perks: grenades, smoke, launch pads, instant forts and, for the luckiest, an atomic bomb you
can drop anywhere on the map.Read the full README ↗A battle royale drawn in ballpoint on graph paper, for up to 100 players per match: third person over the shoulder (or first person, P), with every wall breakable. Glide in, loot guns, shields and perks off the floor and out of pencil cases, and fight your way to the King's Tower in the middle of the island: the final circle always closes on it. Ten guns from common to legendary (the SCAR and a one-shot Heavy Sniper are the prizes), shields and medkits, and five perks: grenades, smoke, launch pads, instant forts and, for the luckiest, an atomic bomb you can drop anywhere on the map. Play solo, duos or squads; every match scores points (a win, a top-10 finish, kills), and the token's trading fees fill one shared pot: every hour, on the hour (UTC), the player with the most points that hour takes it.
Fast fights. 100 health + 100 shield. An assault rifle takes a full-shield player down in about 1.5 s, a pump shotgun in two blasts up close (one once their shield is gone), a Heavy Sniper headshot in one. Shotguns keep a tight cone on the move and full damage out to 8 m. Hit an enemy and their health bar shows over their head for two seconds; damage numbers, headshot and kill markers on every hit.
Sights per gun, your own hands. Right click aims down the gun's own sight: irons on pistols, SMGs and shotguns, a red dot on the rifles, a real scope only on the two snipers, an open ring on the launchers (held on the shoulder, never over the middle of the screen). In first person you see your arms, in your character's colours, holding the gun through recoil, reloads and swaps. The drawing is made at full screen resolution with FXAA on the pen lines (settings: quality, render scale, antialias).
A storm that forces the fight. It starts closing 30 s in, while the last players are still gliding, and keeps coming in every half minute through seven circles (about 4 minutes to nothing). Once fewer than 60% of the teams are left it hurries, up to 4x as fast with a tenth of them, so a match is always decided in the last small circles, never across an empty island.
Knocked, not out. In duos and squads, zero health knocks you down while a teammate is still
standing: you crawl, bleed out in 30 s, and a teammate holding E next to you for 5 s brings you
back at 30 health. When nobody on the team is left standing, the knocked are out. A kill board
shows the top teams by kills all match.
Everything breaks. Every building is a structure of blocks. Rockets, grenades, C4, tank shells and crashes break the boxes they reach into 2 m blocks and knock them out (wood, brick and metal take more or fewer hits); enough damage and the whole building comes down at once, rubble hurting whoever is inside, loot and cases dropping to what is left. A plane flown into a tower can bring it down (a few planes for the tallest). Ground, hills and mountains never break. The server decides and sends only what changed; clients apply the same changes to their own world, so movement prediction stays exact (a test replays every change on a mirror and compares).
The axe. X takes it out: chop walls, cars and people; every broken block gives building
material, and right click places a 1 m block where you look, Minecraft style.
The Needle. An 80-floor megatower: an updraft in its core carries you up (step out at any floor), rare loot on every floor, golden cases every tenth floor and on the roof. Fall from high up anywhere and your glider opens by itself.
Tanks and motorbikes. Tanks are slow and armoured (the crew can't be shot), their cannon flattens houses and their tracks drive through walls. Motorbikes are the fastest thing on wheels, with a seat for a friend.
Characters, radio, voice. Five characters to pick in the lobby (looks only). In a vehicle the
radio comes on: three 80s stations composed live in the browser (synthwave, disco, darkwave) with a
DJ; N next station, M off. In duos and squads, hold V to talk to your team (WebRTC between
teammates; the server only relays the handshake, and only between teammates).
The island. 800 × 800 m, 24 named places, a snowfield across the north and a desert in the
south-east. The Needle (80 floors), The Spire (a 30-floor skyscraper with a helipad), Mount Doodle, Snowpeak and
Eraser Ridge (terraced mountains to climb), Frost Lodge (ski lodge, chalets, chairlift), Dune
Town, Dust Fort, the Ink Pyramid and an Oasis. The classics, around downtown Crown City and the
King's Tower, joined by a ring road: Scribble Suburbs (house grid, mansion, sports park), Castle Crayon (walls, corner
towers and a keep on a two-step hill), Margin Mart (shops, a supermarket with aisles, a water
tower), Paper Port (piers, a cargo ship you can board, gantry cranes, a lighthouse), Staple Depot
(warehouses with catwalks, a factory with chimneys and tanks), Crumple Junk (car-stack alleys,
crusher, crane), Eraser Lake (island house, piers, boathouse), Inkwood (forest lookout lodge),
Tally Farms (barns with haylofts, twin silos), Doodle Drive-In (giant screen with a walkway),
Pit Stop (gas station, diner, motel), Graphite Mine (tunnels through a hill) and Paper Plane
Field (runway, hangar, control tower). npm run check:map walks the whole island with the
game's own movement rules and fails on any case or loot spot you can't reach on foot.
Vehicles. Cars on the roads (run people over, drive-by with your own gun, hop with Space), helicopters on rooftops (a nose gun that aims where you look) and planes at Paper Plane Field (wing guns and bombs). Crashes dent them, bullets and blasts wreck them, a wreck explodes on whoever is near. Driving uses the same client prediction as walking, so it responds instantly.
Arsenal. 12 guns including a Rocket Launcher and a Stinger whose missiles lock onto helicopters and planes. 9 perks: grenades, molotovs (fire on the ground), shockwaves (throw people through the air, yourself included), smoke, launch pads, instant forts, upgrade kits, C4 and the atomic bomb. Upgrade benches and kits add up to three stars (+66% damage) to a gun.
Squads in vehicles. Teammates ride along as passengers (3 in cars and helicopters, 1 in a plane) and shoot out of the windows; bail out of an aircraft and your glider opens.
C4 and supply drops. C4 sticks where it lands and goes off on the second press: a full health bar (100) inside 2.5 m, your own included, so you survive your charge only with shields up. Every time the storm moves, a balloon crate drops into the next circle with a legendary gun, C4 or a nuke.
Modes and points. A win is 100 points solo, 50 each in duos, 25 each in squads (the same per match on average); a top-10 finish (top 5 duos, top 3 squads) is +20 and every kill +5 (up to 10). Friends type the same party code to drop on one team; empty spots are filled. No friendly fire; teammates are marked through walls and always on the minimap.
Rooms. The lobby lists every room filling or live, with a join button. Up to 5 rooms fill at
once (OPEN_ROOMS), 10 full matches run per process (MAX_ROOMS), spread over worker threads.
Menus and HUD. Before a match: the controls as keycaps, your name, one button in. Esc opens
Resume, Inventory, Settings (sensitivity, aiming sensitivity, invert, camera, field of view, master /
music / effects volume, announcer voice, graphics quality, resolution; saved per device, applied live),
Controls and Back to menu. The hotbar sits bottom centre (gun silhouettes in their rarity colour, stars,
ammo); hold Tab (or I) for the inventory with what everything does, hold Z for the item wheel.
First Blood, multi-kills (10 s apart) and streaks get a synthesized sting and a spoken line.
Spectating. Out? Click / right-click to switch players. While a teammate lives you can only
watch your team (no ghosting); once your team is out, F gives you a free flying camera.
Anti-cheat. The server is the authority for movement, fire rate, damage and loot. On top:
replayed inputs are dropped, aimbot patterns (snap-to-target hits, absurd headshot rates) flag the
player, and enemies you have no line of sight to beyond 40 m are never sent to your client, so a
wallhack has nothing to draw. Flagged players earn no points and land in data/flags.jsonl
(GET /api/admin/flags with ADMIN_TOKEN).
The look and feel follow the ballpoint-shooter and classic battle-royale genres; all code here is original, and every place name and item is our own.
npm install
npm run dev:server # game server on :8787 (mock pot, guests allowed)
npm run dev:client # vite on :5173, proxies /ws and /api to the server
Open two tabs at http://localhost:5173, join as guests, pick a mode and click Drop in in both.
A match starts at 100 players, or 45 s after the second player joins. npm run build:demo builds a
single-player version that runs entirely in the browser against bots.
Production: npm run build && npm run build:server && npm start (plain-JS server bundle in
dist-server/, serves dist/). Or docker build -t pot-royale .
The game has two parts, and they deploy to different places:
vercel.json already sets the build. With no environment variables it builds the playable
offline demo (bots, simulated pot). Set VITE_SERVER_URL (e.g. https://koth-server.fly.dev)
in the Vercel project and redeploy to build the live client that plays on your game server.Dockerfile on any host that does: Fly.io, Railway, Render or a
VPS (docker build -t koth . && docker run -p 8787:8787 -v koth-data:/data koth). Keep
data/ on a persistent volume: it holds the points log and the epoch secret. The server also
serves the page itself, so it can run alone without Vercel.The first screen is a landing page over the lobby (client/src/landing.ts, landing.css): what the
game is, how to win, the hourly pot, the modes, the controls as keycaps, a minute of gameplay and a
big Play button that lifts it off the lobby. #play in the URL skips it; the lobby's logo brings it back.
The video is recorded from the offline demo, in one command:
npm run record:gameplay # builds the demo, plays a match headlessly, writes client/public/media/
npm run record:gameplay -- --seconds 60 --max-mb 8 --no-build --keep
It joins as a guest and lets the bots' brain play your seat (client/src/autopilot.ts, loaded only on
#autotest in the demo build) through the normal input path: the paper plane, the glide, looting and
fights, in third person. The page runs on a virtual clock that the recorder steps one frame at a time
and screenshots at 1280x720, so the clip is a smooth 30 fps however slow the machine is (it takes a
few minutes). ffmpeg encodes gameplay.mp4 (H.264), gameplay.webm (VP9) and the poster
gameplay.jpg, raising compression until each file fits the size budget. Needs ffmpeg and
Playwright (playwright or playwright-core, or PLAYWRIGHT_CORE=/path); the browser is
CHROME_PATH, Playwright's Chromium, a Puppeteer Chrome or the system Chrome. Re-run it whenever the
game looks different.
npm run typecheck
npm test # sim, payouts, merkle, epoch settlement and crash recovery
cargo test -p pot_vault # on-chain merkle matches the server tree byte for byte
npm run loadtest -- --bots 2000 # bot swarm against a running server
Measured on the production bundle, one process with 3 match workers (4 vCPU container), line-of-sight culling and vehicles on: 1000 bots in ten full 100-player matches (solo, duos and squads), 3–8 ms per tick against a 33 ms budget, ~14 000 snapshots/s, 0 errors.
The quick way to go live: the pot is a wallet the team holds, and the team sends each hour's prize.
shared/src/token.ts (mint), commit, redeploy the page (Vercel) and the server (railway up).POT_SOURCE=wallet, POT_WALLET=<pot wallet address>, TOKEN_MINT=<CA>, a paid
SOLANA_RPC_URL (Helius or similar), and ADMIN_TOKEN.curl -H "authorization: Bearer $ADMIN_TOKEN" https://<server>/api/admin/payouts lists who is owed what;
send it from the pot wallet, then
curl -X POST -H "authorization: Bearer $ADMIN_TOKEN" "https://<server>/api/admin/paid?epoch=N&tx=<signature>".
The transfer signature goes on the public record at /api/epochs/N.Players trust the team to pay in this mode. The vault program below removes that trust once it is deployed.
shared/src/token.ts (mint). That one line puts it in the lobby
with copy / pump.fun / Jupiter / chart links, in the demo build, and on the server (where TOKEN_MINT in
the environment overrides it). Symbol and the $25 hold rule live in the same file.programs/pot_vault, run initialize, and send the token's creator fees to
the vault PDA (claim them and transfer, on a schedule, or point the fee recipient at the vault if the
launchpad allows it). Set POT_SOURCE=solana, VAULT_ADDRESS, CONFIG_ADDRESS and a paid SOLANA_RPC_URL.REQUIRE_WALLET as you like (guests can still play for fun with ALLOW_GUESTS=true) and
MIN_VERIFIED_FOR_TICKET (4 is a good floor against farming with a few wallets).data/epochs/ are what the keeper posts with settle_epoch every hour; winners claim.vault PDA of programs/pot_vault.min(HOLD_TOKENS, $HOLD_MIN_USD)
worth of the token, priced at the one-hour median and fixed in tokens when the epoch starts.
A price drop never pushes a holder out mid-epoch (the 50K cap), a pump never prices new players
out (the dollar side).data/voids.jsonl). Scoring itself closes at a
random minute inside the last 10 (a candle close), so nobody can time the end; points after it
count for the next epoch. Snapshot times and the close minute derive from the epoch secret, whose
hash is published at epoch start and which is revealed at settlement, so anyone can check them.
Points only count if the match started with at least MIN_VERIFIED_FOR_TICKET distinct wallets,
so a handful of your own wallets can't farm a near-empty match.ROLLOVER_BPS (10%) to start
the next pot and pays the rest out (PAYOUT_MODE=winner: the hour's top scorer takes it all, a tie splits it;
or prorata by points, or draw with weighted 60/25/15 tiers seeded by commit-reveal), and writes data/epochs/<epoch>.json with a merkle root and a proof per winner.settle_epoch(root, total, count) on chain. Winners call claim with their proof.
The program never lets the authority pay out more than the free balance, settle an epoch early or twice,
or touch funds already owed to unclaimed winners. Unclaimed prizes go back into the pot after 30 days.| var | default | meaning |
|---|---|---|
POT_SOURCE | mock | solana polls the vault program; wallet uses the team's wallet as the pot (prizes sent by hand); off runs real matches with no pot |
POT_WALLET | POT_SOURCE=wallet: the address the creator fees are claimed into | |
SOLANA_RPC_URL | mainnet | use a paid RPC in production |
VAULT_ADDRESS / CONFIG_ADDRESS | PDAs of the vault program | |
TOKEN_MINT | shared/src/token.ts | the game's coin (the CA); required when HOLD_MIN_USD > 0 |
HOLD_MIN_USD | 25 | USD side of the hold requirement for scoring points |
HOLD_TOKENS | 0 | optional cap in tokens: the requirement never exceeds this many (0 = dollars only) |
TOKEN_SYMBOL | KOTH | shown in the lobby |
PRICE_URL | Jupiter price v3 | mint is appended to it |
REQUIRE_WALLET / ALLOW_GUESTS | false / true | set true / false in production |
MIN_VERIFIED_FOR_TICKET | 4 with wallets, 1 in dev | anti-farm floor |
PAYOUT_MODE | winner | the hour's top scorer takes the pot; or prorata (split by points), draw |
ROLLOVER_BPS | 1000 | share of each pot carried into the next |
MAX_ROOMS, OPEN_ROOMS, MAX_CONNECTIONS | 10 / 5 / 5000 | per-process caps |
WORKERS | cores − 1 | threads that run live matches |
See docs/ARCHITECTURE.md for the design and what is still missing before mainnet.
ADMIN_TOKEN |
bearer token for /api/admin/flags |