ThreeJS Game Development
Build production-ready 3D games using Three.js with proper architecture, input handling, and performance optimization.
Instructions
When building Three.js games, follow these architectural and implementation guidelines:
1. Project Architecture Setup
Implement a modular architecture with clear separation of concerns:
**Application** - Main entry point and game loop management**InputManager** - Handle keyboard and mouse input with state tracking**AssetManager** - Asynchronous loading and management of 3D models, textures, audio**GameWorld** - Scene, lighting, and physical world management**UIManager** - UI interactions and HUD rendering**Player** - Player character controller with camera management**EntityManager** - Game entities lifecycle (enemies, pickups, NPCs)**PhysicsSystem** - Collision detection and physics simulation2. Input System Implementation
**Keyboard Input:**
Track key states in real-time using an object (not just events)Use standardized key codes (KeyW, KeyA, KeyS, KeyD)Handle both keydown and keyup eventsStore states: `this.keyState[e.code] = true/false`Check states in game loop: `if (this.keyState['KeyW']) { /* move */ }`**Mouse Input for FPS Camera:**
Use Pointer Lock API for FPS mouse controlRequest pointer lock on container click: `container.requestPointerLock()`Track pointer lock state via `pointerlockchange` eventCapture movement deltas: `e.movementX`, `e.movementY`Apply sensitivity scaling and reset after each frame3. FPS Camera Controls
**Critical camera setup:**
```javascript
camera.rotation.order = 'YXZ'; // Required for FPS camera
```
**Rotation implementation:**
Yaw (left/right): Rotate around Y-axis using horizontal mouse movementPitch (up/down): Rotate around X-axis using vertical mouse movementClamp vertical rotation to prevent flipping: `Math.max(-Math.PI/2 + 0.01, Math.min(Math.PI/2 - 0.01, pitch))`Apply sensitivity scaling (e.g., `0.002`) to mouse deltas4. Character Movement
**Movement calculation:**
1. Get input direction from keyboard state (WASD → vector components)
2. Create movement vector: `new THREE.Vector3(moveX, 0, moveZ)`
3. Normalize to prevent faster diagonal movement: `if (length > 1) normalize()`
4. Get forward/right vectors from camera rotation (Y-axis only)
5. Transform movement to world space: combine forward/right with input
6. Apply movement scaled by speed and delta time
5. Game Loop Structure
Use requestAnimationFrame with proper timing:
Calculate delta time: `(now - lastTime) / 1000`Clamp delta time to prevent time jumps: `Math.min(deltaTime, 0.1)`Update systems in order: Input → Player → Entities → Physics → RenderTrack FPS for debugging and performance monitoring6. Asset Management
**Async loading pattern:**
Return promises for all asset loadsTrack loading progress: `loaded / total`Call progress callbacks for UI updatesUse central registry for asset accessPreload critical assets before game startImplement error handling and retries7. Performance Optimization
Apply these techniques:
Object pooling for frequently created/destroyed objectsLOD (Level of Detail) for complex modelsFrustum culling for off-screen objectsMesh batching for similar materialsInstancing for repeated geometryBaked shadows where possibleMinimize draw calls by combining materialsUse sprite sheets for particles8. Debugging Tools
Implement developer aids:
FPS counter tracking frame timeDebug mode with visual helpers (axes, wireframes)THREE.js inspector integrationConsole commands for testingVisual debugging for physics/collisionsPerformance monitors (draw calls, triangles)9. Best Practices
Follow these guidelines:
Pre-compute and cache expensive calculationsUse Object3D hierarchy for logical groupingDispose resources properly (geometries, materials, textures)Use raycasting for interaction and collisionSeparate game logic from rendering codeImplement custom events for communicationUse quaternions for smooth rotationsAvoid unnecessary matrix computationsHandle window resize for responsive canvas10. Common Pitfalls to Avoid
Watch out for:
❌ Not normalizing movement vectors (causes faster diagonal movement)❌ Incorrect camera rotation order (must use YXZ for FPS)❌ Missing delta time in movement (inconsistent speeds)❌ Memory leaks from not disposing resources❌ Excessive draw calls from too many materials❌ Missing pointer lock for FPS controls❌ Not handling window resize eventsExample: FPS Player Controller
```javascript
class Player {
constructor(camera) {
this.camera = camera;
this.camera.rotation.order = 'YXZ'; // Critical!
this.rotation = new THREE.Euler(0, 0, 0, 'YXZ');
this.position = new THREE.Vector3();
this.moveSpeed = 5.0;
}
update(deltaTime, input) {
// Apply mouse look
this.rotation.y -= input.mouseMovement.x * 0.002;
this.rotation.x -= input.mouseMovement.y * 0.002;
this.rotation.x = Math.max(-Math.PI/2 + 0.01, Math.min(Math.PI/2 - 0.01, this.rotation.x));
// Calculate movement
const moveX = (input.keyState['KeyD'] ? 1 : 0) - (input.keyState['KeyA'] ? 1 : 0);
const moveZ = (input.keyState['KeyS'] ? 1 : 0) - (input.keyState['KeyW'] ? 1 : 0);
const moveDir = new THREE.Vector3(moveX, 0, moveZ);
if (moveDir.length() > 1) moveDir.normalize();
const forward = new THREE.Vector3(0, 0, 1).applyEuler(new THREE.Euler(0, this.rotation.y, 0));
const right = new THREE.Vector3(1, 0, 0).applyEuler(new THREE.Euler(0, this.rotation.y, 0));
const worldDir = new THREE.Vector3();
worldDir.addScaledVector(forward, -moveDir.z);
worldDir.addScaledVector(right, moveDir.x);
worldDir.normalize();
this.position.addScaledVector(worldDir, this.moveSpeed * deltaTime);
// Apply to camera
this.camera.rotation.copy(this.rotation);
this.camera.position.copy(this.position);
}
}
```
Constraints
Always use delta time for movement and animationSet camera rotation order to 'YXZ' for FPS controlsNormalize movement vectors to prevent speed inconsistenciesDispose of Three.js resources when no longer neededClamp delta time to prevent physics explosionsUse Pointer Lock API for FPS mouse controlsTrack key states continuously, not just events