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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A Block Blast–style puzzle game for the browser, built with TypeScript, Pixi.js 7 and Vite. You drag pieces onto an 8×8 board and clear full rows and columns. It has neon "jewel" blocks, 19 themed worlds to collect, a combo system, revives, a recorded stadium announcer and background music. It installs as a PWA.
Just for fun. EPIC BLAST is a hobby project, made for the fun of building it and playing it. It isn't a commercial product, has no ads or tracking, and isn't affiliated with Block Blast or its makers. Most of the art, the announcer voice and the logo were made with AI image and audio tools; the prompts are in
docs/prompts/.
| Event | Points |
|---|---|
| Placing a piece | 1 per block in the piece |
| Clearing lines | lines × 8 × (10 × combo + 6) |
| All clear (board empty after a clear) | The clearing move pays ×3 (×4, ×5, up to ×6 for each further all clear in the game), plus 300 |
| Gold jewel cleared | The clear's points again (at least 250) |
Combo goes up by one for every move that clears at least one line. If a whole round (three pieces) passes without a clear, the combo resets to 0. Three pips beside the combo show how many pieces are left to keep it (green once the round has a clear). Your best score is saved in the browser, and the game in progress is saved after every move: closing or reloading the page continues where you left off.
The world changes every 20 cleared lines, independent of score level. There are 19 worlds, each with its own scenery, colors and board material:
Neon Arcade, Candy Reactor, Spring Bloom, Summer Splash, Autumn Ember, Frozen Aurora, Dungeon Loot, Cosmic Overdrive, Volcanic Core, Storm Citadel, Deep Sea Abyss, Sky Kingdom, Glitch Dimension, Toy Box, Steam Foundry, Jackpot Palace, Desert Mirage, Sketchbook and Moonlit Dojo.
A game opens in Neon Arcade (or the world you picked in the gallery) and then visits the others in shuffled order. Once every world has been visited, the next round of worlds is called "ECLIPSE 1", "ECLIPSE 2", and so on, with each world's two main colors swapped. Within a world, the theme also steps through four stages as you get closer to the next world.
There are 39 fixed shapes, each spawned with its own weight: dot; dominoes; straight lines of 3, 4 and 5 (horizontal and vertical); 2×2 and 3×3 squares; 2×3 rectangles; small and large corners; L, T, S and Z shapes; and two-cell diagonals.
Dealing is mostly random, with some help built in:
Worlds unlock for good, across games: by reaching them in a game, or once enough lines are cleared in total (the n-th world needs n × 40 lines). Tapping the world name below the board opens the gallery, where an unlocked world can be chosen to open every new game. The game over screen shows the nearest goals (the record, the next world, the next level) and a few numbers from the game.
Banners appear for special moves. Only one is shown per move, picked by priority, and each banner has a 6.5-second cooldown:
| Banner | Trigger |
|---|---|
| PERFECT ROUND! | All three pieces placed and the board is empty |
| CLEAN SWEEP! | A clear empties the board |
| BRILLIANT MOVE! | Your round matched the solver's optimal result |
| PHEW! CLOSE ONE! | You cleared a line when only one legal move was left |
| NEW PERSONAL BEST! | First time this game beats your best score |
| BLOCKBUSTER! | 4+ lines in one move |
| LEGENDARY! / UNSTOPPABLE! / ON FIRE! | Combo reaches 10 / 5 / 3 |
| WHAT A FINISH! | The last piece of a round clears 2+ lines after a round with no clears so far |
| IN THE ZONE! | Every 5 rounds in a row that each had a clear |
| CROSS STRIKE! | A row and a column cleared together |
| TRIPLE HIT! | Exactly 3 lines in one move |
| ROOM TO BREATHE! | A clear made with ≤3 legal moves left opens up ≥12 |
For Brilliant Move, a Web Worker searches every position and order for the current three pieces. It ranks results by pieces placed, then lines cleared, then empty cells and adjacent empty pairs. The solver only feeds this celebration and never changes the game state. If the search isn't finished, or workers aren't available, the celebration is skipped.
The menu button in the top-right corner opens the options menu:
All settings are saved in localStorage. The game still works if storage is blocked.
The game is available in English, German and Spanish and follows the browser's language. Any other language falls back to English. Adding ?lang=de (or en, es) to the URL picks a language by hand. World names and the recorded announcer stay in English.
src/assets/audio/announcer/, from "Triple clear!" and "Unstoppable!" to "New world!", "Perfect round!" and "One million!". There are at least 6.5 seconds between calls, and frequent ones like "Unstoppable!" or "On fire!" repeat at most every 45 seconds. How the voice was made: docs/prompts/voice.md. Background music gets quieter while the announcer speaks.src/assets/audio/music/smth-smth.mp3) with its own on/off setting. It starts after your first click or tap, because browsers block autoplay.Pixi.js draws the board, the tray, the jewel-style block textures and the per-world board materials. Effects include shatter particles (up to 450 at a time), placement puffs, screen shake, floating text and score counters that fly into the score display. The DOM layer adds impact rings, clear beams and a background that changes with your combo (calm, warm, hot, legend).
Requires Node.js 22.18+, which can run the TypeScript tests directly.
npm install
npm run dev # dev server at http://localhost:3000
npm run build # typecheck + production build into dist/
npm run preview # serve dist/
| Command | What it does |
|---|---|
npm test | Typecheck + unit tests (tests/unit, Node's built-in test runner) |
npm run test:build | Production build, then checks the service worker, manifest and assets |
npm run test:e2e | Playwright tests on desktop and mobile screen sizes |
npm run test:all | All of the above |
To run one test file: node --test tests/unit/session.test.ts
index.html Page shell: header, scoreboard, game over screen, options and combo-save dialogs
src/
main.ts Starts the audio engine and the game; registers the service worker (production only)
core/ Game rules only (no DOM, no Pixi)
board.ts 8×8 grid, placement, line detection, clear preview
shapes.ts The 39 shapes and weighted random picking
session.ts GameSession: rounds, combo, scoring, levels, revives, combo save, celebrations
scoring.ts Clear score formula, all-clear bonus
progression.ts Level thresholds and world progress (20 lines per world)
collection.ts Unlocked worlds across games
opening.ts The prepared board of the very first game
streak-recovery.ts When a combo can be saved (combo ≥ 10)
ai/
solver.ts Bitmask board model and exhaustive round solver
solver.worker.ts Runs the solver in a Web Worker
round-predictor.ts Starts and cancels the worker for each round
helpful-dealer.ts Occasional helpful piece deals
revive.ts Picks revive pieces that guarantee a clear
game/game.ts Connects GameSession to rendering, audio, haptics and the page
render/ Pixi board, tray, textures, materials, particles, shake, floating text,
plus Pixi-free layout and drag math
audio/ Sound effects engine, announcer, background music
ui/ Page UI: HUD, options menu, celebrations, score animation, effects, theme colors
i18n/ Language detection, t() lookup and the translation files (locales/en.json, de.json, es.json)
theme/ The 19 worlds, palette generation and world art lookup
platform/ Safe localStorage, haptics, PWA registration
styles/main.css
assets/ Images, font (Press Start 2P), announcer clips, music
public/ Manifest, PWA icons, favicon, install screenshots (copied as-is; regenerate with tools/generate_icons.py)
build/ Vite PWA plugin and service worker template
tests/
unit/ Unit tests for rules, solver, audio, rendering math, platform code
build/ Checks on the production build
e2e/ Playwright browser tests (with small test pages in harness/)
tools/ Promo + icon generators, voice clip tools
docs/
prompts/ Prompts for the world boards and scenes, the announcer voice and the logo
screenshots/ The screenshots in this README
marketing/ Gameplay captures and promo images (1280×720, 1280×550, 512×512, 512×384, 200×120)
Game rules go in core/ and are tested there. game/game.ts only turns the results that GameSession returns into visuals and sound.
Texts shown in the game live in src/i18n/locales/: add a key to en.json first, then to de.json and es.json (a unit test checks that every language has the same keys and placeholders). Static texts in index.html get a data-i18n, data-i18n-aria-label or data-i18n-title attribute; code calls t('key', { name: value }).
Production builds include a web app manifest, icons and a service worker that saves all app files for offline use. The service worker is not used in dev mode.
sw.js and its cache version are generated from a hash of the build output, so a changed build always gets a new cache.dist/ at once. Serve sw.js, index.html and manifest.webmanifest with Cache-Control: no-cache. Hashed files in assets/ can be cached for a year with immutable..github/workflows/deploy-ftp.yml runs on pushes to main that change the game itself: src/, public/, index.html, build/, the Vite and TypeScript config, the package files or the workflow. Changes to docs, tests, tools/ or marketing/ alone don't deploy. You can also start it by hand under Actions → Deploy to FTP → Run workflow. It runs npm test, builds dist/ and uploads it with lftp. index.html, sw.js and manifest.webmanifest go up last, so visitors never load a page whose assets are still missing.
All values go under Settings → Secrets and variables → Actions. That page has two tabs, and each value must be in the right one. The workflow reads secrets only from the Secrets tab and variables only from the Variables tab. A value in the wrong tab counts as not set.
Tab "Secrets" → "New repository secret" (hidden in logs):
| Name | Required | Meaning |
|---|---|---|
FTP_HOST | yes | Server name, e.g. ftp.example.com (no ftp://) |
FTP_USER | yes | Login name |
FTP_PASSWORD | yes | Password |
Tab "Variables" → "New repository variable" (visible in logs):
| Name | Required | Meaning |
|---|---|---|
FTP_REMOTE_DIR | yes | Target folder on the server, e.g. /htdocs/epic-blast. Created if missing |
FTP_PROTOCOL | no | ftps (default, FTP with TLS), ftp (unencrypted) or sftp |
FTP_PORT | no | Only if the server doesn't use the default port (21 for FTP/FTPS, 22 for SFTP) |
FTP_DELETE_STALE | no | true deletes files in FTP_REMOTE_DIR that are not in the build (old hashed assets). .htaccess and .well-known/ are kept. Default false |
Use repository secrets and variables, not environment ones. The job doesn't use a GitHub environment, so it can't see values stored there. If a run fails with FTP_… is not set, check that the value is in the right tab.
Only turn on FTP_DELETE_STALE when FTP_REMOTE_DIR holds nothing but the game.
EPIC BLAST is released under the MIT License, © 2026 Felix Wahner. You may use, change and share the code, also commercially, as long as you keep the copyright notice and the license text, so the original author stays credited.
The bundled Press Start 2P font is not covered by the MIT License. It is © The Press Start 2P Project Authors and licensed under the SIL Open Font License 1.1.