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
Guess the shitcoin. Closest wins, worst pays.
Translation pending
GamesTypeScript · CSSGitHub @N3R01NOnlineOwner 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
a fun mobile game about guessing degen coin data
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A lobby party game for phones. Spin a wheel, land on a number, and that number is
a coin's market-cap rank. Everyone guesses what it's worth at once. Closest wins,
worst pays.
Open two browser windows (one private, so they get separate identities) and host
a game in one, join in the other.
No database is needed to develop: without Upstash credentials the app falls back
to an in-memory store and says so at startup. State dies with the dev server.Read the full README ↗A lobby party game for phones. Spin a wheel, land on a number, and that number is a coin's market-cap rank. Everyone guesses what it's worth at once. Closest wins, worst pays.
npm install
echo "COINGECKO_API_KEY=your-demo-key" > .env.local
npm run dev
Open two browser windows (one private, so they get separate identities) and host a game in one, join in the other.
No database is needed to develop: without Upstash credentials the app falls back to an in-memory store and says so at startup. State dies with the dev server.
The first room you create builds the coin universe, which crawls 27 pages of the CoinGecko API and takes about 45 seconds. Every room after that is instant until the snapshot ages out.
Coins spins a market-cap rank and guesses price, market cap, ATH, FDV and 24h volume. NFTs spins an all-time-volume rank over Ethereum collections and guesses floor price, all-time volume, owners and sales. Punks spins a CryptoPunk's own index, 0–9999, and guesses what it last sold for.
They are the same game over a different universe. lib/universe.ts picks the
source; everything downstream — the state machine, scoring, every screen — is
indifferent to which one it got.
The two sources have opposite shapes, and that is the whole design:
| CoinGecko | OpenSea | |
|---|---|---|
| Ranked list | 27 requests, numbers included | 30 requests, no numbers at all |
| Per-entity numbers | free, in the list | one request each, at spin time |
| Budget | 10,000 credits/month | 120 per sub-minute window |
OpenSea's ranked list carries names and art but no floor price, and there is no batched stats endpoint. Pricing the whole ladder up front would cost one request per collection, so NFT mode stores only the ladder and fetches the numbers for the one collection a spin lands on: 30 requests to build, one per round. A 100-round game costs about 135.
That trade has one consequence worth knowing: the build can no longer filter
on numbers it hasn't fetched. A collection with no floor is only discovered on
landing, so applySpin walks to the next rank and burns the dead one. What can
be filtered up front — unverified, NSFW, disabled, art-less — is, because that
all arrives in the cheap list call.
Measured against the live API rather than assumed: thirty pages yield 2,876
collections, 96% of raw rows clear the filters (/collections/top is already
curated, so the safelist filter does less work than you would expect), and
5% of the ladder comes back unguessable — so a round costs 1.05 requests on
average. Slider bounds are set from the same crawl: floors run 0.00024 to 29.7
ETH, all-time volume 269 to 1.38M, owners 220 to 5.3K, sales 476 to 39K.
The number on the reel is the punk itself rather than a position in a ladder, because a punk's index says nothing about its value — so this mode has no difficulty tiers and the host screen hides the control.
Prices come from cryptopunks.app, which documents its own API at /llms.txt.
No key, no auth. One crawl of action=all-sales walks the entire history —
31 pages, ~30,500 sales back to June 2017, about 17 seconds — and folds it
into one row per punk. After that a round costs nothing: the prices are
local and the image is a URL the phone fetches itself, served immutable.
Three of its documented endpoints do not do what the docs say, so only the
crawl is trusted: /details?includeHistory=true returns no history at all,
batch-recent-history caps at three rows per punk, and hasNextPage is false
on every page including the first. The crawl stops when a page stops adding
anything rather than when the API claims to be done.
Two answers are not numbers, which is most of what makes this mode different:
Both are scored on the same axis as everything else, and the axis is left to
rank the near misses. Calling "never sold" outright is exact; failing that, the
lowest guess wins, because it was most nearly right that the punk was worth
nothing. A sale above the cap sits off the top of the axis, so the highest guess
takes it — and OVER_CAP_GUESS is tuned so that saying "over" beats pinning
the slider at 5,000 for all three sales that live up there, rewarding the call
rather than a guess at how far over. Nobody has to have named a bucket for
someone to have been closest to it.
The reveal carries the date of both the last and the highest sale. It has to: only 10% of punks have sold in the last year and 60% last changed hands four or more years ago, so 45 ETH in 2021 and 45 ETH today are not the same claim.
The documented 600/hour is not what a key reports. X-RateLimit-Limit comes
back as 120 against a sub-minute reset, and 67 requests in 40 seconds drew no
429 at all — far more headroom than this game asks for. lib/nfts.ts backs off
to X-RateLimit-Reset rather than trusting any of these numbers.
Get a free Demo key from the CoinGecko developer dashboard — no card required, 10,000 credits a month, of which this uses roughly 7,000.
The keyless public API returns 429 on the third consecutive request, and the
snapshot builder needs 27 in a row. CoinGecko's own docs say the keyless tier is
"not suitable for production workloads, scheduled polling, or high-frequency
updates."
UPSTASH_REDIS_REST_URL and
UPSTASH_REDIS_REST_TOKEN or KV_REST_API_URL and KV_REST_API_TOKEN
depending on how the store was added. The app reads both pairs, so nothing
needs renaming. REDIS_URL is the rediss:// protocol endpoint and is not
used — the client speaks REST.COINGECKO_API_KEY under Settings → Environment Variables, and
OPENSEA_API_KEY too if you want NFT mode. Either mode fails with a plain
503 naming the key it is missing, so a game without one is never a mystery.There is no cron to configure: the coin snapshot rebuilds itself lazily whenever a lobby is created and the existing one is more than three hours old, behind a lock so only one builder ever runs. Vercel's Hobby plan only permits daily cron fired within a ±59 minute window, which is no use for this.
Note that Vercel's Hobby plan is for personal, non-commercial projects.
| Resource | Ceiling | This uses |
|---|---|---|
| CoinGecko credits | 10,000/mo | ~7,000/mo |
| OpenSea requests | 120/window | ~30/ladder/day + ~1.05/round |
| cryptopunks.app | undocumented | ~31/history + 0/round |
| Upstash commands | 500,000/mo | ~1,200/game |
| Upstash storage | 256 MB | ~1.8 MB (all three snapshots) |
npm run dev # local server
npm test # 85 tests, no test framework — node --test runs the TS directly
npm run snapshot # rebuild the coin universe and print what it found
npm run snapshot:nft # rebuild the collection ladder, and measure the dead rate
npm run snapshot:punks # rebuild the punk sale history and report the split
npm run playthrough # drive a full game over HTTP against a running server
playthrough is the fastest way to check a change end to end:
npm run dev &
BASE=http://localhost:3000 npm run playthrough
MODE=nfts BASE=http://localhost:3000 npm run playthrough
MODE=punks BASE=http://localhost:3000 npm run playthrough
lib/types.ts domain model, and every constant both sides need
lib/score.ts log-squared error, winner/loser split, FDV merge, reveal tiers
lib/universe.ts which source a game draws from (server only)
lib/coins.ts CoinGecko: crawl, exclude, filter, chunk, lazy rebuild
lib/nfts.ts OpenSea: the ladder up front, the numbers per round
lib/punks.ts cryptopunks.app: the whole sale history, folded per punk
lib/room.ts the game state machine (server only)
lib/useRoomState.ts polling hook — the transport seam
components/phases/ one file per screen
Four runtime dependencies: next, react, @upstash/redis, zod.
A few decisions worth knowing before changing things:
market_cap_rank
field disagrees with its own market_cap_desc ordering — one sampled page held
ranks 2113–2588 interleaved, with all 250 rows mismatched.