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
SHADOW SIX는 비공식·비영리 팬이 만든 커맨도스에 대한 경의입니다
SHADOW SIX: an unofficial, non-commercial fan tribute to Commandos: Behind Enemy Lines (1998). Not affiliated with Pyro Studios, Eidos, Square Enix or Kalypso Media.
게임JavaScript · PythonGitHub @jaher온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기SHADOW SIX — an unofficial fan tribute: a realistic three.js remake of Commandos: Behind Enemy Lines (1998). Not affiliated with Pyro Studios, Eidos, Square Enix or Kalypso.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A real-time stealth tactics game in your browser: a fan remake of Commandos: Behind Enemy Lines (1998) built
with three.js. Six specialists, enemy vision cones, a fixed 3/4 camera and WWII missions behind enemy lines.
▶ Play now in your browser (nothing to install):README 전문 보기 ↗A real-time stealth tactics game in your browser: a fan remake of Commandos: Behind Enemy Lines (1998) built with three.js. Six specialists, enemy vision cones, a fixed 3/4 camera and WWII missions behind enemy lines.
▶ Play now in your browser (nothing to install): https://jaher.github.io/shadow-six/
Fan tribute, not an official game. SHADOW SIX is an unofficial, non-commercial fan tribute to Commandos: Behind Enemy Lines (Pyro Studios / Eidos Interactive, 1998). It is not affiliated with, endorsed by or connected to Pyro Studios, Eidos, Square Enix or Kalypso Media. 'Commandos' is a trademark of its owner. No assets from the original game are used; all art, audio and code are original or CC0/redistributable (see CREDITS.md).
Reference frames from the web build: the dam breach in mission 3, a U-boat at the pier in mission 7, the battleship at Le Havre in mission 13, and the V2 rockets on their pads in mission 19.
The complete per-mission set — action frames plus non-explosion scene references for missions 1–20 — is catalogued in verify/reference/README.md.
Work in progress.
For development there is no build step: the repository root is the web root.
npm install # dev tools only (esbuild for the web build, playwright-core for the tests)
npm run serve # http://localhost:8080/ (node tools/serve.mjs [port])
The web build that GitHub Pages serves bundles and minifies the code and copies only the runtime assets:
npm run build # → dist/ (size report; fails over 950 MB or on a non-relative URL)
npm run serve:dist # http://localhost:8080/shadow-six/, served like GitHub Pages
node tools/perf/measure-web-start.mjs # first-visit start-up on a throttled 20 Mbit/s link
Any static file server also works, for example npx serve . or python3 -m http.server 8080. Use a desktop
browser with WebGL 2 (Chrome, Edge or Firefox; a discrete GPU is recommended for the higher presets).
URL parameters:
| Parameter | Effect |
|---|---|
?mission=m01 | Skip the title screen and load a mission (m01–m03; m00 is the sandbox). |
?preset=low|medium|high|ultra | Rendering quality preset. |
?test=1 | Test mode: the simulation only advances through window.__game.advance(s). |
?debug | Debug mode: the DEBUG LEVEL SELECT instead of the title (see Debug mode). ?debug=cones also shows every vision cone. |
Add ?debug to the URL (?debug=1, ?debug=cones and any other value work too; ?debug=0 is off), for example
http://localhost:8080/?debug or https://jaher.github.io/shadow-six/?debug. The game skips the title splash and
opens DEBUG LEVEL SELECT: every mission in the mission list, grouped as Behind Enemy Lines (by mission number),
Beyond the Call of Duty and Sandbox / test maps, each with its number, name, theater colour and icon, and a
thumbnail once you have played it in debug mode (a small capture cached in the browser). New missions appear by
themselves. Click or tap a level, or use the arrow keys and Enter. Esc (or Main menu) goes to the normal title.
Options in the overlay, remembered in localStorage (those marked * apply on the next launch):
| Option | Effect |
|---|---|
| Skip briefing (default on) | The level starts at once. |
| All commandos * | Adds every missing commando of the campaign beside the first one. |
| Invulnerable | Commandos take no damage. |
| Enemies blind & deaf | Inspection mode: enemies see and hear nothing, so they never react. |
| All vision cones | Every enemy cone stays drawn. |
| Free camera | No scroll clamp at the map edge and zoom from 0.125× to 4×. |
| Time ×0.5 / ×1 / ×2 / ×4 | Simulation speed. |
| Time of day * / Wind * | Dawn, noon, dusk or overcast lighting; any wind preset. |
| Info HUD | The corner readout of latency, triangles and memory (also F11). |
Keys while ?debug is on: F10 level select (also the small DEBUG button, bottom left), PageDown / PageUp
next / previous level, Ctrl+R instant restart, F11 info HUD (below). The URL follows the level you are in, so
?debug&mission=m05 reloads or shares that level directly, with the remembered options. Without the parameter none
of this exists, and nothing in the normal menus leads to it.
The info HUD sits in the bottom-left corner above the DEBUG buttons (with a finger: right of the MENU / TURN buttons, or above them on a phone held upright). Frame numbers are the average, 95th percentile and worst of the last 2 seconds, so a single hitch stays readable for 2 s:
| Row | What it measures |
|---|---|
| frame, FPS | Time between two displayed frames (one requestAnimationFrame to the next). |
| main cpu | Main-thread ms of each frame: simulation ticks, render, texture uploads (Game.frame). |
| render cpu | The Game.render part of it: scene updates and the WebGL calls (not the GPU's own time). |
| input→frame | From a click, tap, key or wheel (the event's timestamp) to the end of the next frame drawn after it, over the last 32 inputs, plus the last one. GPU, compositor and display time come on top. |
| draws, tris | Draw calls and triangles of the whole frame, including the offscreen passes (water mirror, ocean / ripple / trail maps) drawn before the main view, whose share is shown as offscreen. |
| GPU MB | Live GPU allocations counted as they happen (src/debug/gpu-mem-counter.js, same rules as tools/perf/mem-probe.js): textures with every mip / face / layer, vertex and index buffers, renderbuffers, plus the canvas (estimated). |
| three.js, JS heap | The renderer's live texture and geometry counts; the JS heap in use (Chrome / Edge only). |
Below the table, a perf graph shows the last 15 seconds: each frame's time as a bar (green up to 16.7 ms, amber up to 33.3 ms, red above; one column per pixel keeps the worst frame in it, so a single hitch stays visible), the main-thread CPU as a blue line, dashed 16.7 / 33.3 ms guides, and the GPU memory as a violet line on its own scale. The − button shrinks the HUD to one line (FPS · frame ms · GPU MB) and + brings it back; the choice is remembered. Sampling runs only while the HUD is shown. In video mode the HUD starts minimized in the corner, with the caption moved clear of it; F11 hides or shows it there. Capture runs (?test=1, or ?debug=clean) keep it hidden in video mode, so films stay clean.
Missions with a saved full solution (today Mission 3, Reverse Engineering: tools/solutions/m03.solution.mjs, the
same script the m03-solution regression test plays) can be watched being solved inside the game. In DEBUG LEVEL
SELECT pick ▶ … — play the solution under Solution replays; in a mission press F9 or the small ▶ SOL
button beside DEBUG. The mission is loaded fresh, as authored (the select's All commandos, Invulnerable, Enemies
blind & deaf, time of day and wind are ignored while it plays), and the solution's player orders are issued live in
the real game loop, starting at 1× (real time). The bar at the top has:
| Control | Effect |
|---|---|
| ■ STOP (or Esc, or F9 again) | Stops the replay; you take over the mission where it stands. |
| ❚❚ / ▶ | The game's own pause (P works too). |
| 1× 2× 4× 8× | Replay speed. |
| STEPS ▾ | Skip to a step (stage) of the solution: a later one fast-forwards to it, an earlier one reloads the mission and fast-forwards. |
| CAM | The camera follows the commando the last order went to (on), or is yours to scroll (off). |
A caption shows the current step, each checkpoint as it is reached and the latest order. Your own orders are held
while it plays. Adding a solution for another mission: write tools/solutions/<id>.solution.mjs (exports solve(D, ctx) and STAGES; driver API in tools/solutions/driver.mjs). Nothing to register: the debug menu lists the folder
(the dev server answers tools/solutions/?ls; the web build bakes the list in and bundles every solution as a lazy
chunk).
A guided film of a mission's solution, played live in the game. In DEBUG LEVEL SELECT pick ▶ … — watch the
walkthrough under Watch walkthrough · video mode; in a mission press F8 or the small ▶ VIDEO button; or
open ?debug&walkthrough=m03 directly (&speed=auto, &speed=4, … picks the speed; no speed is AUTO). Missions without a solution are listed under No walkthrough yet (their
button says so). The mission loads fresh, as authored, on a title card (Start, Chapters, Exit); then:
tools/solutions/<id>.walkthrough.mjs, format in docs/walkthrough-format.md).| Control | Keys | Effect |
|---|---|---|
| ✕ | Esc | Back to the debug menu. |
| ⏮ / ⏭ | B / N | Previous / next step. Ahead fast-forwards from where you are; back reloads the mission and fast-forwards (the run is deterministic). ⏮ restarts the current step, or goes to the previous one in its first 4 s. |
| ❚❚ / ▶ | Space | Pause / play. |
| ☰ | L | Chapters and steps: jump to any of them. |
| ½× 1× 2× 4× | 1–4 | A fixed speed. |
| AUTO | 5 / A | The default: the speed follows the action, as the solution films did. Fast through walks and long crawls (4×, up to 8× while the frame rate holds); a true wait (the solution waits for a guard, a gap in the cones, a timer, and nobody acts) is skipped like a jump — about a second of wall time per 30–60 s of game — under a "⏩ waiting: " tag; while the step's men wait their moment the camera shows what moves (another commando at work, the guard they wait for); eased down to 1× ahead of what matters (a knife, a shot, a charge, the uniform, a switch, the raft, a near miss, a man closing in on the guard the step is about) and held there until the action has visibly finished; ½× for blasts and the escape; never above 2.5× while a new caption is read. The button shows the speed it plays at; a fixed speed leaves AUTO. |
| ⏩ | W | Skip waits (fixed speeds; on by default): while every commando waits, the pace rises up to 4× faster; it drops back the moment anything happens. |
| CC | C | Captions on / off. |
| CAM | F | Director on / off. Off is a free camera: scroll, drag and zoom as in the game; a drag or the wheel over the map also switches to it. CAM gives the camera back to the director. |
The walkthrough never changes the simulation: it wins exactly like the scripted run, checkpoint for checkpoint
(tests/debug-walkthrough-sim.test.mjs) — opened from a cold link too, however slow the connection: the load waits for
everything the gameplay world measures on the art, never racing it against a clock, so the run is the tested one tick
for tick (tests/debug-walkthrough-cold.test.mjs: M2 and M3 cold, M2 through a 4 Mbit/s link).
The controls follow the original game.
| Mouse / keyboard | Touch (phone, tablet) | Action |
|---|---|---|
Left click a commando or portrait, 1–6 | Tap a commando or his portrait; long-press a commando adds / removes him | Select (Shift adds to the selection); 0 deselects, 8 selects all |
| Left drag | — | Box select |
| Left click on the ground | Tap the ground; double-tap runs | Walk there; double-click runs |
| Right click | CANCEL button (shown while something is armed) | Cancel the armed item, else deselect |
| Click (Shift+click) an enemy | Long-press an enemy, or the eye button then tap | Show its vision cone |
C / S | Posture button | Lie down and crawl / stand up |
X knife, G pistol, R sniper rifle, J harpoon or trap, B bomb, A detonator, E grenade, L injection, W wire cutters, K first aid, U uniform, Q decoy, T boat, D diving gear, F shovel | Tap the item in the knapsack, then tap the target | Knapsack items (each commando only has his own) |
Tab | — | Move the knapsack panel to the other side |
| Arrow keys, screen edges, middle drag | One-finger drag (flick to coast); two fingers also pan | Pan the camera |
Mouse wheel, numpad + / - (or = / -) | Pinch with two fingers (spread = zoom in, about the fingers' midpoint) | Zoom; numpad * or Backspace resets |
Home | — | Centre on the selection |
P, Esc | MENU button (bottom left) |
On a touch screen a tap never pans and a drag never gives an order: a finger that moves more than 10 px is a drag, and a second finger turns it into a pinch. Phones and tablets start on the medium quality preset (low on older mobile GPUs), which also caps the rendering resolution; a quality picked in OPTIONS is kept.
Each commando carries only the equipment he had in the original game; missions add or change counts.
| Commando | Weapons | Tools |
|---|---|---|
| Green Beret | knife, pistol | decoy, shovel |
| Sniper | sniper rifle (limited rounds, 5 by default), pistol | first-aid kit if neither Driver nor Spy is deployed |
| Marine | knife, harpoon gun (unlimited, shorter range than the pistol), pistol | inflatable boat, diving gear |
| Sapper | pistol, bear trap, time bombs or remote bombs (never both); grenades per mission | detonator (remote bombs), wire cutters per mission |
| Driver | pistol; submachine gun (20 bursts) only in missions 1, 2, 4 and 10 | first-aid kit (6 doses) |
| Spy | pistol, lethal injection (unlimited) | enemy uniform (found on site); first-aid kit if no Driver |
The data lives in src/items.js.
node tests/unit/run.mjs # npm run test:unit: pure simulation (grid, pathfinding, world, objectives, items…)
node tests/run.mjs [pattern] [--swiftshader] [--headed] # npm test: browser tests in headless Chromium
The unit tests run on GitHub Actions for every push and pull request. The browser runner (npm install first,
for playwright-core) starts tools/serve.mjs on a free port and launches a cached playwright Chromium on the
real GPU (--use-angle=gl); --swiftshader uses software rendering. It fails on thrown errors, page errors,
console.error and HTTP errors. Screenshots go to tests/out/ (git-ignored).
.github/workflows/pages.yml builds the web target on every push to master
(npm ci, npm run build, see tools/build/build.mjs) and deploys dist/ to GitHub
Pages. The build bundles src/ with esbuild (code-split, content-hashed file names), keeps every URL relative so
the site works under the /shadow-six/ sub-path, adds version.json, a 404.html that returns to the game and a
small service worker (sw.js).
SS_DIST=1 node tests/run.mjs webbuild checks the build in headless Chromium.
A mission is downloaded once (40–100 MB). Inside a session, RESTART MISSION, quick load and loading a save of a
mission already played reuse what is in memory (textures, models, baked terrain and trees, shader programs): no
download, no loading screen. Between visits the service worker keeps every downloaded asset in the browser's Cache
Storage, keyed by content hash, so a new deploy only re-downloads the files that changed; code, HTML and manifests
always come from the network first (never stale). After a mission loads, the next campaign mission is downloaded in
the background when the connection is not metered or in data-saver mode. Once a mission is cached it also plays
offline. OPTIONS → CLEAR CACHED GAME DATA deletes the cache. Details: docs/ARCHITECTURE.md § Asset cache;
node tools/perf/measure-restart.mjs measures (re)load times on the build.
.github/workflows/ci.yml runs the unit tests and the web build.
index.html entry page (dev: import map → vendor/three.module.js; the build bundles it)
styles/ screen, menu and HUD styles
src/main.js boot: WebGL check → Game → assets → title
src/game.js Game: states, fixed-step loop, mission loading
src/config.js gameplay and render numbers
src/items.js item catalogue and BEL default loadouts
src/core/ events, math, objectives
src/world/ grid (terrain, blocking, line of sight), pathfinding, world, map builder
src/entities/ units, commandos, enemies, vehicles, projectiles, interactables
src/ai/ perception, enemy brain, alarm
src/abilities/ one file per ability
src/engine/ renderer, camera, input, asset loading
src/render/ vision cones, selection markers, effects
src/art/ models, materials, terrain, water, characters
src/ui/, src/audio/ menus, HUD, briefings, credits; audio
src/missions/ mission definitions (m00 sandbox, m01–m03)
assets/ textures, models, HDRIs, audio, portraits, fonts (see CREDITS.md)
vendor/ three.js r186 and addons
tools/ dev server, web build (tools/build), asset build scripts (Blender, audio, portraits)
tests/ browser tests and unit tests
docs/ architecture, design spec, research notes, screenshots
Read docs/ARCHITECTURE.md before changing module interfaces.
All third-party material is listed with its source, author and licence in CREDITS.md. In short:
The original game, Commandos: Behind Enemy Lines, was created by Gonzo Suárez and Pyro Studios and published by Eidos Interactive in 1998. This project exists out of admiration for it.
The original source code is released under the MIT License. Assets in assets/ and the libraries in
vendor/ keep their own licences; see CREDITS.md and vendor/LICENSE.
| Pause and objectives, cancel |
F1 | ? button | Help |
F2–F7 | — | Split-screen camera views |
F8 / F9 (Ctrl+S / Ctrl+L) | MENU → save / load | Quick save / quick load |
Ctrl+B | Notes button | Notes |