A 3D browser-based stress-relief destruction game. Smash everything in sight with LEGO-style weapons across 10 themed worlds, 5 playable characters, and endless monster waves.
Built with Three.js, Cannon-es, Express, and Prisma.
# Install dependencies
npm install
cd client && npm install && cd ..
cd server && npm install && cd ..
# Start dev servers (client :5173 + server :3001)
npm run dev
# Or start separately
cd client && npm run dev # Vite dev server
cd server && npm run dev # Express + tsx watch
# Build for production
npm run buildOpen http://localhost:5173 in your browser.
lego-world/
├── client/ # Frontend — Vite + TypeScript + Three.js
│ └── src/
│ ├── main.ts # Game entry point, wiring all systems
│ ├── core/ # Engine: Game loop, Camera, InputManager
│ ├── entities/ # Player, Monsters, Characters, Pickups
│ ├── weapons/ # Hammer, Sword, Bat + sector-based hit detection
│ ├── world/ # LEGO block system, scenes, decorations
│ │ └── scenes/ # 10 themed scene definitions + builders
│ ├── effects/ # Particles, ScreenShake, Sound, Combo text
│ ├── systems/ # WaveManager, GameBalance
│ ├── ui/ # HUD, Menu, Leaderboard, MobileControls
│ ├── api/ # ApiClient for backend communication
│ └── test/ # BotTest — automated headless stress test
├── server/ # Backend — Express 5 + Prisma 6 + SQLite
│ ├── prisma/schema.prisma # Database schema (players, scores, sessions)
│ └── src/
│ ├── index.ts # Express server entry
│ ├── routes/ # REST API: players, scores, sessions
│ ├── services/ # Business logic layer
│ └── middleware/ # Error handling, validation
├── Dockerfile # Production deployment
├── docker-compose.yml # Full stack orchestration
└── nginx.conf # Reverse proxy config
| Layer | Technology | Why |
|---|---|---|
| 3D Rendering | Three.js (r184) | Industry-standard WebGL library, rich ecosystem |
| Physics | Cannon-es | Lightweight rigid-body physics for debris simulation |
| Frontend Build | Vite 8 + TypeScript 6 | Fast HMR, native ES modules, strict typing |
| Backend | Express 5 | Minimal, flexible Node.js web framework |
| Database | Prisma 6 + SQLite | Type-safe ORM, zero-config database |
| Mobile | Custom Virtual Joystick | 王者荣耀-style touch controls, landscape-only |
| Testing | Puppeteer-core Bot Test | Headless Chrome automated 5×10 scene stress test |
| Deployment | Docker + nginx | Production-ready containerization |
Start Screen → Character Select → Scene Select → Play → Game Over → Submit Score → Leaderboard
- Choose a character — 5 unique LEGO minifigures with distinct appearances
- Choose a scene — 10 themed worlds with unique monsters and decorations
- Fight waves — Monsters spawn in escalating waves, smash them all
- Destroy everything — All scene objects are destructible
- Score & survive — Chain kills for combos, manage HP with health pickups
- Submit score — Compare on the global leaderboard
| Character | ID | Theme | Parts |
|---|---|---|---|
| Classic Spaceman | spaceman |
LEGO Classic Space — white suit, blue visor, oxygen tank | 27 |
| Knight | knight |
LEGO Castle — silver armor, red cross, shield | 30 |
| Pirate Captain | pirate |
LEGO Pirates — tricorn hat, eyepatch, hook hand, gold earring | 30 |
| Ninja | ninja |
LEGO Ninjago — black mask, red headband, back sword | 22 |
| Wizard | wizard |
LEGO Fantasy — pointed hat, white beard, robes, magic staff | 33 |
Each character is built from 20-33 individual LEGO mesh pieces with animated arms and legs for walk/attack cycles.
All weapons use sector-based attack detection — a fan-shaped area on the XZ plane with auto-aim toward the nearest target.
| Weapon | Damage | Speed | Style |
|---|---|---|---|
| Hammer | 40 | 0.22s | Slow, heavy hits — destroys buildings fast |
| Sword | 12 | 0.08s | Fast slashes — best for rapid monster kills |
| Bat | 20 | 0.12s | Balanced — good all-rounder |
Switch weapons with 1, 2, 3 keys. An aim indicator shows the attack direction with auto-targeting.
Each scene has unique ground colors, atmosphere (fog, lighting, background), themed decorations, and reskinned monsters.
| # | Scene | Theme | Background | Monsters |
|---|---|---|---|---|
| 1 | Green Village | LEGO City | Sky blue | Slime, Skeleton, Spider, Giant, Dragon |
| 2 | Ocean Depths | LEGO Atlantis | Light blue | Jellyfish, Shark, Crab, Giant Squid, Sea Serpent |
| 3 | Desert Sands | Pharaoh's Quest | Orange | Sand Beetle, Mummy, Scorpion, Sphinx, Pharaoh |
| 4 | Enchanted Forest | LEGO Elves | Purple | Mushroom, Dark Elf, Vine Crawler, Treant, Forest Spirit |
| 5 | Frozen Tundra | LEGO Arctic | Ice gray | Frost Bat, Ice Wolf, Snow Spider, Yeti, Ice Dragon |
| 6 | Volcanic Wasteland | Power Miners | Orange-red | Lava Slime, Fire Imp, Magma Beetle, Lava Golem, Fire Dragon |
| 7 | Space Colony | LEGO Space | Deep blue | Alien Bug, Robot Guard, Space Crawler, Battle Mech, Alien Queen |
| 8 | Haunted Swamp | Monster Fighters | Green | Zombie, Ghost, Swamp Spider, Werewolf, Vampire Lord |
| 9 | Pirate Cove | LEGO Pirates | Warm orange | Crab, Ghost Pirate, Sea Snake, Giant Octopus, Kraken |
| 10 | Jungle Ruins | LEGO Adventurers | Jungle green | Poison Frog, Jungle Warrior, Anaconda, Stone Guardian, Temple God |
Each monster type has unique geometry built from LEGO blocks, AI behavior, and stat scaling:
| Type | Base HP | Speed | Behavior |
|---|---|---|---|
| Slime | 30 | 2 | Low, slow — swarm fodder |
| Skeleton | 50 | 4 | Humanoid, balanced |
| Spider | 40 | 6 | Fast, low-profile |
| Giant | 150 | 1.5 | Tank, high damage |
| Dragon | 300 | 3 | Boss-tier, appears wave 6+ |
Monsters scale with wave number:
- Slime: 5 + 3×wave — always present
- Skeleton: 1.5×wave — from wave 1
- Spider: 1.0×(wave-1) — from wave 2
- Giant: 0.8×(wave-3)+1 — from wave 4
- Dragon: 0.4×(wave-5)+1 — from wave 6
Each monster follows: Idle → Chase → Attack → Cooldown → Chase
- Detection range: monster-specific (8-20 units)
- Attack range: melee (1.5-5 units)
- Frustum culling: monsters >40 units skip AI updates
- Kill monsters within 1 second to chain combos
- Score = base × combo × kill streak
- Kill streak multiplier: every consecutive kill within the window
- Visual combo text floats above the player (Dota-style: COMBO, RAMPAGE, KILL STREAK)
Monsters drop health pickups on death:
| Monster | Drop Rate | Heal Amount |
|---|---|---|
| Dragon | 100% | 40 HP |
| Giant | 40% | 40 HP |
| Skeleton | 40% | 25 HP |
| Spider | 40% | 25 HP |
| Slime | 20% | 15 HP |
Each scene contains 15-40 LEGO-built decorations that are all fully destructible:
- Village: Houses, trees, walls, fences, flowers, campfires, lamp posts
- Ocean: Coral, seaweed, treasure chests, pillars, ruins walls
- Desert: Pyramids, obelisks, cacti, baobab trees, palm trees, sand dunes
- Forest: Giant mushrooms, crystals, dead trees, spider webs, magic lamps
- Arctic: Ice walls, igloos, crystal formations, snow piles, campfires
- Volcanic: Volcanic rocks, lava pools, fire pillars, dark walls
- Space: Rockets, satellite dishes, habitat domes, UFOs, alien rocks
- Swamp: Dead trees, tombstones, coffins, spider webs, swamp pools
- Pirate: Ship masts, treasure chests, barrels, palm trees, campfires
- Jungle: Temple pillars, stone heads, vine arches, baobab trees, palm trees
Objects are auto-classified by bounding box size into medium (30+ HP) or large (80+ HP). Player collision prevents walking through obstacles.
5-track procedural BGM system using Web Audio API:
| Track | Key | Scene |
|---|---|---|
| Track 1 | C Major | Waves 1-2 — light and peaceful |
| Track 2 | G Major | Waves 3-4 — upbeat adventure |
| Track 3 | D Minor | Waves 5-6 — tension building |
| Track 4 | A Minor | Waves 7-8 — dark and intense |
| Track 5 | E Minor | Wave 9+ — boss battle |
Each track layers drums + bass + melody. Sound effects for swing, hit, destroy, wave start, death, and pickup.
- 王者荣耀-style virtual joystick on the left side
- Attack button + weapon switch on the right
- Landscape-only with rotation prompt
- Continuous analog input for smooth movement
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
Health check |
| POST | /api/players |
Create/find player by nickname |
| GET | /api/players/:id |
Get player stats |
| GET | /api/players/:id/scores |
Get player score history |
| POST | /api/scores |
Submit a new score |
| GET | /api/scores/leaderboard |
Top scores |
| POST | /api/game-sessions |
Record a game session |
Player ───< Score
Player ───< GameSession
- Player: nickname, total games, best score, total kills/destroyed
- Score: per-game score with kills, destroyed, wave reached, survive time
- GameSession: detailed session with timestamps and kill/destroy breakdowns
Automated headless stress test via Puppeteer:
# Run 5 characters × 10 scenes × 15s each
# Requires Chrome at /Applications/Google Chrome.app
NODE_PATH=node_modules node stress-test.jsTest validates:
- All 5 characters render and animate correctly
- All 10 scenes load without errors
- Monster spawning and wave progression
- Scene object destruction
- No uncaught errors or memory leaks
Multi-stage build: client (Vite) + server (Express) in a single container with nginx.
# Build and run — serves on http://localhost:8080
docker-compose up --build
# Or build manually
docker build -t lego-world .
docker run -p 8080:80 -p 3001:3001 lego-worldThe container runs nginx (port 80) for the frontend and Node.js (port 3001) for the API, with nginx proxying /api/* to the backend.
# Build client
cd client && npm install && npm run build && cd ..
# Build server
cd server && npm install && npx prisma db push && cd ..
# Run server
cd server && npm start
# Serve client/dist/ with any static file server| Input | Action |
|---|---|
| WASD / Arrow Keys | Move |
| Space | Attack |
| Shift | Sprint |
| 1 / 2 / 3 | Switch weapon |
| Escape | Pause |
| Tab | Leaderboard |
MIT






