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
Castle Siege 3D Two castles enter, one castle leaves. Build turrets, magic towers, and catapults while hurling goblins and dark knights at your friend's castle. Will it crash? Maybe. Will it be fun? A
GamesHTML · JavaScriptGitHub @corycowgillOnlineOwner 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
Castle Siege 3D Two castles enter, one castle leaves. Build turrets, magic towers, and catapults while hurling goblins and dark knights at your friend's castle. Will it crash? Maybe. Will it be fun? Absolutely. Is the AI balanced? We'll get back to you on that.
Taken from the repository README
Loading the full README.
Read the full README ↗A 2-player tower defense browser game built entirely with Claude Code (Anthropic's AI coding agent). No build tools, no bundlers, no frameworks beyond Three.js -- just HTML, JavaScript, and procedural 3D graphics.
Two castles face each other across a medieval battlefield. Each player builds towers to defend, buys enemy units to attack, places walls to maze, and uses special abilities (Meteor, Lightning, Freeze, Heal) to turn the tide. Survive 10 waves as the day cycles from noon through dusk, midnight, dawn, and back.
This game was developed through an extended conversation with Claude Code, Anthropic's AI coding agent. The development spanned 20+ versions (v3.0 through v3.20) covering:
The session progressed through natural language requests. Here's the arc:
Early phase -- graphics foundation:
Performance crisis and fix:
Architecture refactor:
models.js)vfx.js, split the monolith)Gameplay content:
Visual polish + bug fixes:
| Version | What shipped |
|---|---|
| v3.0 | Enemy/projectile mesh pooling + material canonicalization |
| v3.1 | Inline BufferGeometry merger -- collapses ~50 meshes/enemy to ~10 |
| v3.2 | Enemy detail pass (goblin warts, orc mohawk, dark knight runes) |
| v3.3 | Tower detail pass (archer silhouettes, arcanist shelves, crew) + tower merge |
| v3.4 | Fixed-timestep simulation (60 Hz sim decoupled from render rate) |
| v3.5 | Matrix freezing for static subtrees + castle/wall merge |
| v3.6 | Spatial grid for O(local) radial enemy lookups |
| v3.7 | Dynamic time-of-day lighting (sun, fog, sky tint per wave) |
| v3.8 | Castle detail pass (garrison, rose window, courtyard well, braziers) |
| v3.9 | Wall/barricade detail pass with per-instance randomization |
| v3.10 | Night sky (stars, rain weather system) |
| v3.11 | Projectile detail pass (barbed arrows, molten cannonballs, arcane orbs) |
| v3.12 | Battlefield ground decals (cobblestones, grass tufts) |
| v3.13 | Engine audit -- cached HUD DOM refs, merged battlefield decals |
| v3.14 | Boss wave feature (Troll, Dragon, Lich models + gameplay) |
| v3.15 | Balance pass (10 waves, tower rebalance, heal/meteor buff) |
| v3.16 | Boss HUD buttons moved to specials row |
| v3.17 | Boss damage doubled |
| v3.18 | Boss secondary animations (dragon tail sway, troll club swing, lich staff bob) + night sky fix |
| v3.19 | Boss detail pass (tribal paint, phylactery, chest heart glow) |
| v3.20 | Moon removal + cloud night-fade fix |
index_3d.html (4,079 lines) -- Game engine, UI, game loop
models.js (3,370 lines) -- All 3D model builders (shared with guide)
vfx.js ( 301 lines) -- Particle system, damage numbers, special VFX
guide.html ( 584 lines) -- Interactive field guide with animated previews
index.html (3,323 lines) -- Original 2D Phaser version (legacy)
three.min.js -- Three.js r150+ (bundled locally)
manifest.json -- PWA manifest (fullscreen landscape)
assets/0x72/ -- Tileset (used by 2D version only)
1. three.min.js -- defines THREE
2. models.js -- mkMat, mkMesh, createCastle, createTower, createWall,
createBarricade, createEnemyModel, createProjectileModel
3. vfx.js -- particlePool, emitParticles, spawnMeteorStrike,
spawnLightningBolt, spawnHealAura, addVfx, updateVfx
4. inline <script> -- game state, game loop, init()
All files use classic <script> tags (not ES modules). Top-level function and const/let declarations in each script share a common scope, so models.js functions are callable from the inline script at runtime.
Every model in the game goes through the same optimization pipeline:
createXxxModel() -- builds a Group of 30-100+ sub-meshes using mkMesh(geometry, material)__canonicalizeEnemyMaterials() -- deduplicates materials by signature (color+roughness+metalness+emissive)__mergeEnemyModel() -- inline BufferGeometry merger collapses sub-meshes per material into ~1 draw call each, preserving limb Groups for animation__freezeMergedMeshes() -- sets matrixAutoUpdate=false on static Mesh childrenacquireEnemyMesh()/releaseEnemyMesh() reuse meshes across spawn/death cyclesResult: a v3.19 boss with ~200 sub-primitives renders at ~10 draw calls, animates at 60 Hz, and costs zero GC on spawn/death.
SIM_DT = 1/60s
gameLoop():
rawDt = clock.getDelta()
simAcc += rawDt
while simAcc >= SIM_DT: -- 60 Hz simulation
processInput(SIM_DT)
updateBuild/updateCombat(SIM_DT)
resetInputs()
simAcc -= SIM_DT
syncBuildings/Towers/Enemies() -- every render frame
updateParticles/VFX(rawDt) -- visual smoothness at display rate
renderer.render(scene, camera)
Tower targeting and enemy combat scanning use a tile-resolution spatial grid (__spatCells[ROWS][COLS]) rebuilt once per sim tick. Radial lookups via __spatQuery(cx, cy, radius, out) replace full enemy-array walks, dropping updateCombat from O(towers x enemies) to O(towers x local).
# Any static file server works
npx serve .
# or
python -m http.server 8000
Open http://localhost:8000/index_3d.html (or just open index_3d.html directly in a browser).
Player 1 (Keyboard / Controller):
| Action | Keyboard | Controller |
|---|---|---|
| Move cursor | WASD / Arrows | L.Stick / D-Pad |
| Place | Space | A |
| Sell | E | B |
| Cycle build | Q / Tab | LB / RB |
| Cycle special | Z | Y |
| Use special | F | X |
| Ready | R | Start |
Player 2 (Mouse):
| Action | Input |
|---|---|
| Move cursor | Mouse |
| Place | Left click |
| Sell | Right click |
| Cycle build | Scroll wheel |
| Use special | Click special button |
| Ready | Middle click / UI button |
Mobile: On-screen D-pad + action buttons.
MIT