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
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A provably fair Plinko game for the browser, built with Three.js and painted in the style of Blender Studio's Spring.
This is a prototype that uses demo credits only. No real money is involved.README 전문 보기 ↗A provably fair Plinko game for the browser, built with Three.js and painted in the style of Blender Studio's Spring.
[!NOTE] This is a prototype that uses demo credits only. No real money is involved.
HMAC-SHA256(serverSeed, clientSeed:nonce:round), decided on the server. Players can rotate seeds and re-verify any bet in the browser with the same code.prefers-reduced-motion.git clone [email protected]:ItsJuniorDias/jungle-plinko.git
cd jungle-plinko
npm install
npm run dev
Open http://localhost:5173. npm run dev starts the game API (:8787) and the Vite dev server (:5173), and Vite proxies /api to the API. Nothing else is needed: every AI asset is already in the repo, and the local API generates a temporary session key on its own.
| Key | Action |
|---|---|
| Space | Drop a ball |
| M | Mute / unmute |
| Esc | Close the big-win banner |
Copy .env.example to .env. Every variable is optional for local play.
| Variable | Used by | Purpose |
|---|---|---|
SESSION_SECRET | API | Key for the encrypted session cookie. Required in production. Generate one with openssl rand -base64 32. |
OPENROUTER_API_KEY | npm run art, npm run mascot | AI image and video generation |
IMAGE_MODEL | npm run art, npm run mascot | Image model (default google/gemini-2.5-flash-image) |
MASCOT_VIDEO_MODEL | npm run mascot | Video model (default heygen/heygen-video-1) |
API_PORT | npm run dev | Port of the local API (default 8787) |
api/ deploys as Vercel Functions alongside the static build.SESSION_SECRET under Project → Settings → Environment Variables. Without it, the API answers server_misconfigured. Changing it later signs every player out, and each gets a fresh demo wallet.main. Every push deploys.| Command | What it does |
|---|---|
npm run dev | API + Vite with hot reload |
npm run build | Type-check and production build to dist/ |
npm run typecheck | TypeScript only |
npm run simulate -- 200000 | Monte Carlo RTP check for 8, 12 and 16 rows at every risk level (default 50,000 bets each) |
npm run art | Generate any missing 2D art (2D art) |
npm run mascot | Generate the mascot's video clips (Mascot clips) |
npm run audio | Rebuild public/audio/ from the curated sources (Audio) |
serverSeed and shows the player only sha256(serverSeed).HMAC_SHA256(key = serverSeed, msg = "clientSeed:nonce:round"). Every 4 bytes become one float in [0, 1).0.5 the ball goes left, otherwise right. The landing bucket is the number of rights.serverSeed. Players can check it against the hash shown earlier and recompute any bet. Clicking a result in the history opens the verifier pre-filled with that bet.The same shared/ code runs on the server and in the browser (Web Crypto), so verification uses exactly the algorithm that produced the result.
Bucket k on an n-row board has probability C(n,k) / 2ⁿ. Each risk level fixes a centre and an edge payout for 8 and 16 rows, interpolated geometrically in between. The curve's exponent is bisected until Σ p(k)·m(k) = 0.99, and every multiplier is rounded down, so the RTP never exceeds the target.
| Rows | Risk | Min | Max | RTP |
|---|---|---|---|---|
| 8 | low / medium / high | 0.5× / 0.4× / 0.2× | 5.6× / 13× / 29× | 98.80% / 98.53% / 98.50% |
| 12 | low / medium / high | 0.5× / 0.4× / 0.2× | 9.46× / 37.8× / 170× | 98.82% / 98.73% / 98.75% |
| 16 | low / medium / high | 0.5× / 0.4× / 0.2× | 16× / 110× / 1000× | 98.62% / 98.72% / 98.61% |
All endpoints are POST with a JSON body. The whole session (balance, seeds, nonce) lives in an HttpOnly cookie encrypted with AES-256-GCM. Every response renews it, so there is no database and the API runs on serverless functions. Players can't read the active server seed or edit their balance.
Replaying an old cookie would roll a session back. That is acceptable for demo credits; a real-money version needs a server-side store. Because each response renews the cookie, the client sends calls one at a time.
| Endpoint | Body | Returns |
|---|---|---|
/api/session | {} | sessionId, balance, serverSeedHash, clientSeed, nonce (starts a session if there is none) |
/api/plinko/bet | { amount (cents), rows, risk } | path, bucket, multiplier, payout, balance, nonce |
/api/seeds/rotate | { clientSeed? } | the revealed previous seed + new public state |
/api/wallet/refill | {} | public state with the demo balance reset |
server/game.ts holds the logic. server/index.ts (local, node:http) and api/ (Vercel Functions) are thin adapters over it.
src/engine/ is a small layer that future games can reuse:
postprocessing), in order: bloom → AgX → hue/saturation → contrast → vignette → grain.springMaterial(), a toon ramp with a fresnel rim. springify() restyles any imported GLB.The pegs are a single InstancedMesh. The ball follows a choreographed path of parabolic arcs, so it plays the same at any frame rate. It squashes and stretches along its velocity, the pegs spring when hit, the buckets swing like struck gongs, and the frame flashes in the bucket's colour.
VideoMascot plays AI-generated clips with alpha: an idle loop, plus drop, tension, happy, bigWin and sad.
Mascot.ts) takes over if the clips can't load.src/engine/sfx.ts is a Web Audio mixer with music, ambience and effects buses feeding a gentle compressor.
pointerdown doesn't count.playback audio session, so it still sounds in Silent mode, like a video. That pauses other apps' music while it plays, and inside a cross-origin iframe it needs allow="microphone".Every visual in the game was made with AI, and every sound is curated. Each pipeline is reproducible from the repo.
npm run art (scripts/generate-art.ts) generates every image with Nano Banana (google/gemini-2.5-flash-image) through OpenRouter.
npm run art # generate images that don't exist yet
npm run art -- board leaf --force # regenerate specific assets
background is generated first. Scene assets are generated with it as a reference; isolated objects only share the style prompt.sharp. The script detects the shade the model actually used and un-mixes the edge pixels, so no fringe remains.art/raw/ (git-ignored). A fresh clone makes no API calls until you pass --force, and re-running re-keys for free.public/art/manifest.json is optional, and missing images get procedural placeholders.The seed ball, spore pegs and bucket plaques (plus the fallback 3D mascot) were generated with Hyper3D Rodin (image-to-3D) through the Blender MCP, using the 2D art as references. In Blender they were then:
The source is art/models/jungle-plinko-props.blend, and public/models/manifest.json lists what the game loads.
npm run mascot (scripts/mascot-video.ts) makes the clips through OpenRouter:
kwaivgi/kling-v3.0-std, every clip also ends on the pose. Otherwise the idle plays as a ping-pong loop.public/mascot/.npm run mascot -- ref # the reference pose
npm run mascot -- idle drop tension happy bigWin sad # the clips
npm run mascot -- happy --force # regenerate one clip (spends credits)
The music and sound effects are curated from Pixabay. npm run audio (scripts/process-audio.ts) turns the originals in art/audio/ into the game's files in public/audio/:
The originals are git-ignored because the license forbids redistributing them as standalone files. Sources are listed in public/audio/CREDITS.md.
shared/ runs on the server and in the browser
fair.ts provably fair core (Web Crypto HMAC-SHA256 → floats)
plinko.ts board math, RTP-solved pay tables, outcome from seeds
server/
game.ts game logic: sealed cookie sessions, demo wallet, bets, seed rotation
index.ts local API server (node:http)
api/ the same API as Vercel Functions (one file per endpoint)
src/
main.ts app wiring: controls, balance, history, big-win banner
api.ts typed API client (calls queued one at a time)
engine/ reusable rendering and audio layer
stage.ts renderer, camera framing, post-processing
environment.ts Spring-style backdrop, parallax, light shafts, particles, wind
materials.ts toon + rim-light materials, springify() for GLBs
moss.ts fur-shell moss grown over a painting's moss
particles.ts pooled additive particle bursts (one draw call)
painted.ts procedural placeholder textures and canvas labels
art.ts loads public/art and public/models manifests (all entries optional)
sfx.ts Web Audio mixer: loops, one-shots, ducking, voice limits, fallback
games/plinko/
PlinkoBoard.ts painted board, pegs, buckets, ball choreography, popups, frame glow
VideoMascot.ts mascot as AI video clips with alpha: idle loop + dissolving reactions
Mascot.ts fallback 3D mascot with spring-driven reactions
ui/fairness.ts provably fair dialog (seeds + verifier)
scripts/
simulate.ts Monte Carlo RTP check
generate-art.ts AI art generation + chroma keying
mascot-video.ts AI mascot clips: reference pose, video, green-screen key, encodes
process-audio.ts cuts, normalizes and loops the curated audio
public/
art/ game-ready 2D art (WebP) + manifest
models/ 3D props (GLB) + manifest
mascot/ mascot clips (H.264 MP4, alpha stacked under the colour)
audio/ music, ambience and effects + CREDITS.md
art/models/ Blender source of the 3D props
docs/ README media
| Area | Tools |
|---|---|
| Rendering | Three.js r186, pmndrs postprocessing, GSAP |
| App | TypeScript 7, Vite 8, Web Audio, Web Crypto |
| Server | Node.js (node:http locally), Vercel Functions in production |
| AI art | Nano Banana (via OpenRouter), Hyper3D Rodin, HeyGen Video 1 (via OpenRouter) |
| Asset tooling | Blender (via MCP), sharp, ffmpeg |
| Audio | Curated from Pixabay |
public/audio/CREDITS.md.Code: MIT © 2026 Alexandre Junior. The audio files keep their Pixabay Content License.