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
Open virtual crates to win sneakers and collectibles, then sell or keep them
Translation pending
AI summary · website and README
GamesJavaScript · CSSGitHub @CainBeaudoinOnlineOwner 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 crate/gacha-style prize picker. Pick a prize tier and how you're paying
for it, watch a slot-machine reel settle on three real 3D crates, then
click one to open it. Your crate opens first — its prize is revealed on a
hero card with Cash Back / Keep buttons — then the other two
crates open afterward so you can see what you could have won.
A balance bar at the top of every screen tracks two simulated balances —
Credits and Cash (USDC) — both play-money, localStorage-only,
not connected to any real payment or crypto system.Read the full README ↗A crate/gacha-style prize picker. Pick a prize tier and how you're paying for it, watch a slot-machine reel settle on three real 3D crates, then click one to open it. Your crate opens first — its prize is revealed on a hero card with Cash Back / Keep buttons — then the other two crates open afterward so you can see what you could have won.
A balance bar at the top of every screen tracks two simulated balances —
Credits and Cash (USDC) — both play-money, localStorage-only,
not connected to any real payment or crypto system.
Before each round you pick which one you're "opening with" (a small modal,
see openPaymentPicker in js/app.js). That choice only decides where a
win pays out:
There's no cost to open a crate — the payment choice is purely about which balance a win lands in, so credits simply accumulate from playing rather than needing a separate "free crate" meter.
Three tiers — $100, $250, $1000 — run identical mechanics,
just against progressively pricier pools scraped from the same source (see
Prize catalog below), so a $1000-tier Legendary is a real step up from a
$100-tier Legendary. Add more in CATEGORIES in js/app.js.
js/app.js defines the crates in CATEGORIES. Crates are product
categories — Sneakers, Streetwear, Collectibles (plus Stocks) — each
drawing from its own weighted pool (js/prizeDataSneakers.js,
prizeDataStreetwear.js, prizeDataCollectibles.js). They used to be
price tiers cut out of one footwear pool; LEGACY_TIER_KEYS in app.js
keeps saved history from before that change rendering.js/boxViewer.js renders each crate as an interactive 3D model
(assets/models/nike_shoe_box/) using three.js. Idle crates spin slowly;
hovering eases a crate back to facing forward. Clicking always finishes
that turn before the lid opens, so every crate opens facing the camera
the same way, regardless of what angle it was spinning at.js/reveal.js: a
vortex/particle CSS effect plus a synthesized WebAudio "hype" sound,
~1-3s depending on rarity) before the image, name, price, and Cash
Back / Keep buttons fade in. This reveal only ever plays for the
crate you picked — the other two crates open with the plain price card,
no FX.js/player.js)Progress persists to localStorage (per-browser, not a real account):
Sound (js/sound.js) is entirely procedural WebAudio (oscillators +
filtered noise) — no external audio files. A mute toggle (top-right) turns
off both the ambient hum and every UI/reveal sound. All icons throughout
the UI (js/icons.js) are inline SVG — no emoji.
Three nav tabs sit under the wallet bar: Boxes (the crate-opening flow above), Marketplace, and Account — loosely modeled on phygitals.com's packs/marketplace/profile structure (studied for reference, not copied).
player.js's inventory, separate from Credits/Cash
history).js/market.js), which immediately appears in both
Account → My Listings and the main Marketplace grid.FAKE_USERNAMES in market.js) drawn from the real prize
catalog — clearly-simulated "other sellers" for a marketplace demo, not
a live multiplayer system or fabricated real-user activity.market.botDecision): ≥88% of asking price accepts, ≥60% counters
with the midpoint, otherwise declines.Crates are grouped into product lines (Stocks, Sneakers, Streetwear,
Collectibles); a line can have several price points. Sneakers comes in
$125, $250 and $1,000 crates: the two dearer ones (prizeDataSneakers250.js,
prizeDataSneakers1000.js) are higher price bands of the same ODTO
footwear scrape, topping out around $11,000. A crate's line in
CATEGORIES ties it to its base crate; Drops has a filter row by line,
and Home shows one card per line.
js/prizeDataSneakers.js / prizeDataStreetwear.js /
prizeDataCollectibles.js are each generated from a scrape of the matching
ODTO collection (footwear,
apparel,
collectibles) — real product
names, prices, and photos (downloaded into assets/prizes/). ODTO quotes
CAD; prices are converted to USD at the rate its own storefront uses. Each
crate draws from a
different price band of the same catalog (scraped via
?sort_by=price-descending across several pages), then splits its own
band into 5 rarity tiers by price quintile — so "Legendary" always means
"the priciest fifth of this tier's pool," not a fixed dollar figure:
| Tier | Common | Legendary |
|---|---|---|
| $100 | ~$74-161 | ~$404-734 |
| $250 | ~$154-367 | ~$881-1,614 |
| $1000 | ~$550-587 | ~$3,302-11,005 |
| Rarity | Color |
|---|---|
| Common | light gray |
| Uncommon | green |
| Rare | blue |
| Epic | purple |
| Legendary | gold |
Each item's weight is its rarity tier's total draw weight split evenly
across the items in that tier (commons draw far more often than
legendaries). To refresh a pool with different stock, re-scrape that
collection and regenerate its file: take available products with a photo,
clamp to a sensible price window, sample 40 evenly across it, then assign
eight per rarity band lowest to highest.
Each crate also wears its own pool: boxViewer.js bakes a tiled collage of
that crate's product shots into the 3D box texture (registerTierArt), so
a Collectibles crate is visibly covered in Bearbricks and a Sneakers crate
in shoes.
The CHOSEN × ODTO cardboard box (box-chosen-od.glb, used on Home and
Drops) keeps its own print instead, and wears a few of its crate's grails
as die-cut stickers: registerTierStickers hands boxViewer cut-outs of the
crate's priciest pieces, and it paints the most colourful four onto a copy
of the box texture at fixed spots (STICKER_SPOTS).
Add an entry to CATEGORIES in js/app.js with its own pool (an array
of { name, price, rarity, weight, image, category } objects — see
prizeDataSneakers.js for the shape). A card for it appears automatically
on the crate-select screen, including its own "View Prizes" dropdown, and
its pool is registered as the box's collage wrap.
assets/models/nike_shoe_box/ is the "Nike Shoe Box" model by
samplemem, licensed
CC BY 4.0. Textures were
downscaled from the original 4K Sketchfab export to keep the page light.
The model ships with its own baked lid-hinge rotation, which is what
boxViewer.js drives on click.
This is a static site with no build step (three.js loads from a CDN via an
import map in index.html, so you'll need network access). Serve the
folder with any static file server — .devserver.py is included and sends
Cache-Control: no-store (handy since browsers can otherwise cache the JS
modules and hide your changes):
python3 .devserver.py
Then open http://localhost:8123.