web-game-engine-expert
Expert guide for modern web-based game development. Covers PlayCanvas, Babylon.js Havok, Three.js/R3F Rapier & Jolt Physics, Fixed-Timestep Accumulators, Zero-GC Pooling, ECS (bitECS/Miniplex), Spatia
Files1
View on GitHubSKILL.mdskills/web-game-engine-expert/SKILL.md
GitHubWeb Game Engine Expert (Physics, Architecture & Game Loops)
<a name="english"></a>
English
Orchestration & Integration
Connects and orchestrates with relevant domain skills:
web-3d-graphics-expert— Visual rendering layer (WebGPU, Three.js, Babylon.js, PlayCanvas).glsl-shader-expert— Custom gameplay shaders, damage flashes, VFX materials.ui-ux-pro-max— In-game HUD overlays, crosshairs, radial inventory menus, and responsive virtual touch joysticks.design-system-architect— Consistent game UI tokens, custom bitmap/web fonts, and health/resource bar theming.visual-ui-auditor— Auditing responsive canvas scaling, DPI-aware pixel ratios (window.devicePixelRatio), and HUD contrast against 3D backgrounds.anti-slop— Enforcing zero placeholder physics, non-jittering deterministic game loops, and complete asset lifecycles.webxr-ar-vr-expert— Spatial controls and VR/AR controller tracking.realtime-collaboration-expert— Low-latency multiplayer state synchronization (WebRTC / WebTransport).performance-web-vitals— Frame budgeting (16.6ms for 60fps, 8.3ms for 120fps) and eliminating Garbage Collection stalls.wasm-edge-computing-expert— Compiling custom Rust/C++ simulation modules to WASM.
Description
Production-grade guidance for engineering commercial-quality 2D and 3D web games running at steady 60 to 144+ FPS. Enforces strict game architectural patterns: Fixed-Timestep Accumulators with render interpolation, WebAssembly Physics Engines (Rapier 3D, Jolt Physics, Havok), Zero-GC Object Pooling to eliminate JavaScript garbage collection stutters, Entity Component System (ECS) architectures, Web Audio API 3D Spatial Sound (HRTF), and WebTransport / WebRTC low-latency netcode.
Trigger Conditions
Activate this skill when the user is:
- Developing a web-based game or physics simulation (2D or 3D).
- Integrating physics with
@dimforge/rapier3d,jolt-physics,@babylonjs/havok, or@react-three/rapier. - Building or refactoring a game loop to fix frame pacing or physics stuttering on 120Hz/144Hz monitors.
- Implementing an Entity Component System (ECS) with
bitECSorminiplex. - Designing zero-allocation object pools for projectiles, particles, or temporary math vectors.
- Adding Gamepad API, Pointer Lock mouse look, or mobile virtual touch controls.
- Integrating 3D positional audio via the Web Audio API.
- Architecting multiplayer game networking with WebTransport (QUIC datagrams) or WebRTC DataChannels.
Modern Web Game Engine Landscape (2026)
| Engine / Stack | Dimension | Physics Backend | Key Strengths |
|---|---|---|---|
| PlayCanvas Engine | 3D | Ammo.js / Rapier WASM | Complete production game engine, tiny runtime (~200KB), mobile-first 60fps, built-in sound & input. |
| Babylon.js + Havok | 3D | Havok Physics WASM | AAA physics simulation, native sub-stepping, character controllers, convex decomposition. |
| Three.js / R3F + Rapier / Jolt | 3D | Rapier 3D (Rust) / Jolt (C++) | Modular, massive ecosystem, best for React/Vue indie games and procedural worlds. |
| LittleJS | 2D | Built-in lightweight physics | Micro-size (<50KB), hyper-fast hybrid WebGL/2D canvas, ideal for 2D platformers & game jams. |
| PixiJS v8 | 2D | Matter.js / Rapier 2D | Next-gen WebGPU 2D rendering pipeline, thousands of animated sprites at 144 FPS. |
| Needle Engine | 3D | PhysX / Rapier | Export directly from Unity/Blender into Three.js with full component lifecycles. |
1. The Deterministic Game Loop: Fixed-Timestep Accumulator
CRITICAL RULE: Never tie physics updates directly to
requestAnimationFramedelta time. A variable delta causes non-deterministic physics, tunneling, and jitter on 120Hz/144Hz monitors.
- Solution: Accumulate frame time and step physics at a fixed tick rate (e.g., 60Hz). Calculate
alphato interpolate render transforms smoothly between the previous and current physics state.
export class GameLoop {
private accumulator = 0;
private lastTime = performance.now();
private readonly FIXED_TIMESTEP = 1 / 60; // 60Hz fixed physics step
private readonly MAX_FRAME_TIME = 0.25; // Clamp to prevent "spiral of death" during lag
constructor(
private updatePhysics: (dt: number) => void,
private render: (alpha: number) => void
) {}
public start() {
this.lastTime = performance.now();
requestAnimationFrame(this.tick);
}
private tick = (currentTime: number) => {
let frameTime = (currentTime - this.lastTime) / 1000;
this.lastTime = currentTime;
// Clamp huge lags (e.g., user changed browser tab)
if (frameTime > this.MAX_FRAME_TIME) {
frameTime = this.MAX_FRAME_TIME;
}
this.accumulator += frameTime;
// Execute fixed updates
while (this.accumulator >= this.FIXED_TIMESTEP) {
this.updatePhysics(this.FIXED_TIMESTEP);
this.accumulator -= this.FIXED_TIMESTEP;
}
// Alpha is the leftover fractional progress towards the next physics step [0.0, 1.0)
const alpha = this.accumulator / this.FIXED_TIMESTEP;
this.render(alpha);
requestAnimationFrame(this.tick);
};
}
2. High-Performance WASM Physics: Rapier & Jolt
Option A: Rapier 3D (Rust + WASM + SIMD)
Lightweight, determinism-friendly, native support in React Three Fiber (@react-three/rapier):
import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier';
export function GamePhysicsWorld({ children }: { children: React.ReactNode }) {
return (
<Physics gravity={[0, -9.81, 0]} timeStep="vary">
{/* Player Dynamic Capsule */}
<RigidBody position={[0, 2, 0]} enabledRotations={[false, false, false]} colliders="ball">
<mesh>
<sphereGeometry args={[0.5]} />
<meshStandardMaterial color="dodgerblue" />
</mesh>
</RigidBody>
{/* Static Floor */}
<RigidBody type="fixed">
<CuboidCollider args={[20, 0.5, 20]} position={[0, -0.5, 0]} />
</RigidBody>
{children}
</Physics>
);
}
Option B: Jolt Physics WASM (jolt-physics)
The AAA open-source physics engine used in titles like Horizon Forbidden West. Best for massive simulations (>2,000 rigid bodies) and advanced ragdolls in vanilla Three.js or Babylon.js.
Option C: Babylon.js + Havok Physics V2 (@babylonjs/havok)
The industry standard AAA physics engine compiled to WebAssembly with SIMD acceleration. Provides native sub-stepping, character controllers, convex hull decomposition, and constraint solvers:
import {
Scene,
Vector3,
MeshBuilder,
HavokPlugin,
PhysicsAggregate,
PhysicsShapeType,
PhysicsCharacterController
} from '@babylonjs/core';
import HavokPhysics from '@babylonjs/havok';
export async function initBabylonHavokPhysics(scene: Scene) {
// 1. Initialize Havok WASM binary and plugin
const havokInstance = await HavokPhysics();
const havokPlugin = new HavokPlugin(true, havokInstance);
scene.enablePhysics(new Vector3(0, -9.81, 0), havokPlugin);
// 2. Static Ground PhysicsAggregate
const ground = MeshBuilder.CreateGround('ground', { width: 40, height: 40 }, scene);
const groundAggregate = new PhysicsAggregate(
ground,
PhysicsShapeType.BOX,
{ mass: 0, restitution: 0.1, friction: 0.8 },
scene
);
// 3. Dynamic Rigid Body (Crate)
const crate = MeshBuilder.CreateBox('crate', { size: 1.5 }, scene);
crate.position.set(0, 5, 0);
const crateAggregate = new PhysicsAggregate(
crate,
PhysicsShapeType.BOX,
{ mass: 10, restitution: 0.3, friction: 0.5 },
scene
);
// 4. Kinematic Character Controller with slope limit and step handling
const playerCapsule = MeshBuilder.CreateCapsule('playerCapsule', { radius: 0.5, height: 1.8 }, scene);
playerCapsule.position.set(0, 1, 0);
const characterController = new PhysicsCharacterController(
playerCapsule.position,
{
capsuleHeight: 1.8,
capsuleRadius: 0.5,
maxSlope: Math.PI / 4, // 45 degree slope climbing limit
stepHeight: 0.3
},
scene
);
return { havokPlugin, groundAggregate, crateAggregate, characterController };
}
Physics Best Practice: ALWAYS use primitive colliders (
Sphere,Box,Capsule) for moving dynamic bodies. NEVER useTrimesh(triangle mesh) for dynamic objects;Trimeshshould only be used for static world terrain.
3. Zero-GC Memory Management & Object Pooling
CRITICAL RULE: In games, memory allocation (
new Vector3(),new Bullet()) inside the tick loop triggers V8 Garbage Collection (GC) pauses every few seconds, dropping frames from 60 to 45 FPS.
- Scratch Object Reuse: Create scratch variables once at module level.
// BAD: Allocates a new vector every frame const dir = new THREE.Vector3().subVectors(target, source).normalize(); // GOOD: Reuses module-level scratch vector const _scratchVec = new THREE.Vector3(); _scratchVec.subVectors(target, source).normalize(); - Generic Object Pool:
export class ObjectPool<T> {
private pool: T[] = [];
constructor(
private factory: () => T,
private reset: (item: T) => void,
initialSize = 50
) {
for (let i = 0; i < initialSize; i++) {
this.pool.push(this.factory());
}
}
public acquire(): T {
return this.pool.length > 0 ? this.pool.pop()! : this.factory();
}
public release(item: T): void {
this.reset(item);
this.pool.push(item);
}
}
4. Input Management: Pointer Lock & Gamepad API
// 1. First-Person / Third-Person Mouse Look
export function setupPointerLock(canvas: HTMLCanvasElement, onLook: (dx: number, dy: number) => void) {
canvas.addEventListener('click', () => {
if (document.pointerLockElement !== canvas) {
canvas.requestPointerLock();
}
});
window.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === canvas) {
onLook(e.movementX, e.movementY);
}
});
}
// 2. Gamepad API with Deadzone Normalization
export function pollGamepad(deadzone = 0.15) {
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
const gp = gamepads[0];
if (!gp) return null;
const filterAxis = (val: number) => (Math.abs(val) > deadzone ? val : 0);
return {
moveX: filterAxis(gp.axes[0]),
moveY: filterAxis(gp.axes[1]),
lookX: filterAxis(gp.axes[2]),
lookY: filterAxis(gp.axes[3]),
buttonJump: gp.buttons[0]?.pressed ?? false,
buttonAction: gp.buttons[1]?.pressed ?? false
};
}
5. 3D Spatial Positional Audio (Web Audio API)
export class SpatialAudioManager {
private ctx = new AudioContext();
private listener = this.ctx.listener;
public updateListenerPosition(pos: { x: number; y: number; z: number }, forward: { x: number; y: number; z: number }, up = { x: 0, y: 1, z: 0 }) {
if (this.listener.positionX) {
this.listener.positionX.value = pos.x;
this.listener.positionY.value = pos.y;
this.listener.positionZ.value = pos.z;
this.listener.forwardX.value = forward.x;
this.listener.forwardY.value = forward.y;
this.listener.forwardZ.value = forward.z;
this.listener.upX.value = up.x;
this.listener.upY.value = up.y;
this.listener.upZ.value = up.z;
}
}
public createSpatialSound(audioBuffer: AudioBuffer, pos: { x: number; y: number; z: number }) {
const source = this.ctx.createBufferSource();
source.buffer = audioBuffer;
const panner = this.ctx.createPanner();
panner.panningModel = 'HRTF'; // High-fidelity Head-Related Transfer Function
panner.distanceModel = 'inverse';
panner.refDistance = 1;
panner.maxDistance = 1000;
panner.rolloffFactor = 1;
panner.positionX.value = pos.x;
panner.positionY.value = pos.y;
panner.positionZ.value = pos.z;
source.connect(panner);
panner.connect(this.ctx.destination);
source.start();
}
}
6. Modern Multiplayer Networking: WebTransport vs WebRTC
For fast-paced real-time action games:
- Avoid WebSockets for player movement: WebSockets use TCP. When a packet is lost, TCP pauses delivery of all subsequent packets (Head-of-Line blocking), causing massive lag spikes.
- Use WebTransport (QUIC) or WebRTC DataChannels:
- Unreliable, unordered datagrams for continuous movement updates (
x, y, z, vx, vy, vz). - Reliable streams for critical state transitions (damage, weapon fire, chat, player joined).
- Unreliable, unordered datagrams for continuous movement updates (
- Client-Side Prediction & Reconciliation:
- Client immediately simulates movement locally upon keypress and stores input history with tick numbers.
- Server processes input at fixed 20Hz-60Hz and replies with authoritative position.
- Client reconciles authoritative state and replays unacknowledged inputs to avoid input lag.
Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
|---|---|
| Frontier Model Cascade | Medium / High (GPT-5.6 Sol Medium, Gemini 4 Argon, Claude Sonnet 5.5 High, Grok 4.7 High Fast) |
| Availability Fallback | Gemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Zero unhandled missing-model errors) |
| KV-Cache & Token Protocol | Pin static instructions & schemas at prompt prefix (kv-cache-prefix-optimizer); pass surgical CONTEXT_MAP.md slices (<250 tokens/call) during Swarm Fan-Out |
| SaaT CLI & MCP Dispatch | CLI: vibes skill exec web-game-engine-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_ast_query, vibes_compile_cache, vibes_optimize_budget, vibes_verify_gate |
| NFR & Anti-Slop Invariants | P99 API Gateway Latency < 85ms, OpenAPI 3.1 / TypeSpec strict schema parity, RFC 9457 Problem Details error contracts, Idempotency-Key enforcement; 100% complete zero-placeholder output verified via node scripts/check-anti-slop.js --strict |
<a name="bahasa-indonesia"></a>
Bahasa Indonesia
Integrasi Orkestrasi
Terhubung dan mengorkestrasi skill domain yang relevan:
web-3d-graphics-expert— Lapisan visual rendering (WebGPU, Three.js, Babylon.js, PlayCanvas).glsl-shader-expert— Shader kustom untuk efek visual, kilatan luka, dan partikel game.ui-ux-pro-max— Overlay HUD dalam game, crosshair, menu inventaris radial, dan joystick virtual sentuh responsif.design-system-architect— Desain token UI game yang konsisten, font bitmap/web kustom, dan tema bar health/stamina.visual-ui-auditor— Audit visual penskalaan canvas responsif, pixel ratio sadar-DPI (window.devicePixelRatio), dan kontras HUD terhadap latar 3D.anti-slop— Penegakan fisika bebas placeholder, game loop deterministik tanpa jitter, dan siklus hidup aset lengkap.webxr-ar-vr-expert— Kontrol spasial dan pelacakan kontroler VR/AR.realtime-collaboration-expert— Sinkronisasi state multiplayer real-time (WebRTC / WebTransport).performance-web-vitals— Manajemen frame budget (16.6ms untuk 60fps) dan pencegahan Garbage Collection stall.wasm-edge-computing-expert— Kompilasi modul simulasi C++/Rust kustom ke WebAssembly.
Deskripsi
Panduan produksi untuk merekayasa game web komersial 2D dan 3D dengan target stabil di 60 hingga 144+ FPS. Menerapkan arsitektur game modern: Fixed-Timestep Accumulator dengan interpolasi render, Physics Engine berbasis WebAssembly (Rapier 3D, Jolt Physics, Havok), Zero-GC Object Pooling untuk mencegah lonjakan Garbage Collector JavaScript, arsitektur ECS (Entity Component System), Audio Spasial 3D Web Audio API (HRTF), dan Netcode WebTransport / WebRTC berlatensi rendah.
Kondisi Pemicu
Aktifkan skill ini ketika pengguna:
- Membangun game web atau simulasi fisika (2D maupun 3D).
- Mengintegrasikan mesin fisika
@dimforge/rapier3d,jolt-physics,@babylonjs/havok, atau@react-three/rapier. - Memperbaiki ketidakteraturan fisika atau stuttering pada monitor 120Hz/144Hz.
- Mengimplementasikan ECS (Entity Component System) dengan
bitECSatauminiplex. - Mendesain Object Pool tanpa alokasi memori berulang untuk proyektil, peluru, atau partikel.
- Mengintegrasikan kontrol Gamepad API, Pointer Lock (mouse look), atau kontrol sentuh virtual mobile.
- Menambahkan audio spasial 3D menggunakan Web Audio API.
- Merancang arsitektur multiplayer real-time menggunakan WebTransport atau WebRTC.
Prinsip Inti Rekayasa Game Web
Fixed-Timestep Accumulator Loop (Wajib):
- Jangan pernah menjalankan komputasi fisika langsung pada delta waktu
requestAnimationFrame. Perubahan refresh rate monitor (60Hz, 120Hz, 144Hz) akan merusak konsistensi fisika. - Akumulasikan waktu frame dan jalankan langkah fisika pada tick tetap (misal 60Hz), lalu hitung
alphauntuk menginterpolasikan posisi render grafis secara mulus.
- Jangan pernah menjalankan komputasi fisika langsung pada delta waktu
Mesin Fisika WASM (Rapier / Jolt / Havok):
- Gunakan Rapier 3D (Rust WASM + SIMD) untuk Three.js/R3F.
- Gunakan Jolt Physics untuk simulasi skala besar ribuan objek dinamis.
- Gunakan Babylon.js + Havok Physics V2 (
@babylonjs/havok) untuk simulasi kelas AAA:
import { Scene, Vector3, MeshBuilder, HavokPlugin, PhysicsAggregate, PhysicsShapeType, PhysicsCharacterController } from '@babylonjs/core'; import HavokPhysics from '@babylonjs/havok'; export async function inisialisasiFisikaHavok(scene: Scene) { const havokInstance = await HavokPhysics(); const havokPlugin = new HavokPlugin(true, havokInstance); scene.enablePhysics(new Vector3(0, -9.81, 0), havokPlugin); // Batas lantai statis const lantai = MeshBuilder.CreateGround('lantai', { width: 40, height: 40 }, scene); new PhysicsAggregate(lantai, PhysicsShapeType.BOX, { mass: 0, friction: 0.8 }, scene); // Objek kotak dinamis const kotak = MeshBuilder.CreateBox('kotak', { size: 1.5 }, scene); kotak.position.set(0, 5, 0); new PhysicsAggregate(kotak, PhysicsShapeType.BOX, { mass: 10, restitution: 0.3 }, scene); // Character Controller dengan batas kemiringan (slope) dan tangga const pemain = MeshBuilder.CreateCapsule('pemain', { radius: 0.5, height: 1.8 }, scene); const controller = new PhysicsCharacterController( pemain.position, { capsuleHeight: 1.8, capsuleRadius: 0.5, maxSlope: Math.PI / 4, stepHeight: 0.3 }, scene ); return { havokPlugin, controller }; }- Hindari penggunaan mesh collider (Trimesh) pada objek bergerak; selalu gunakan primitif (Bola, Kotak, Kapsul).
Manajemen Memori Zero-GC:
- Pembuatan objek baru (
new Vector3(), objek peluru) setiap frame memicu Garbage Collection di browser yang menyebabkan frame drop berkala. - Daur ulang objek menggunakan pola Object Pooling dan gunakan kembali variabel scratch statis.
- Pembuatan objek baru (
Kontrol Responsif:
- Manfaatkan Pointer Lock API untuk kontrol kamera first-person / third-person dengan
movementXdanmovementY. - Gunakan Gamepad API dengan deadzone filter untuk mendukung controller konsol (PlayStation, Xbox).
- Manfaatkan Pointer Lock API untuk kontrol kamera first-person / third-person dengan
Audio Spasial 3D (HRTF):
- Hubungkan AudioListener ke transformasi kamera, dan hubungkan suara ke PannerNode dengan model panning
'HRTF'untuk audio 3 dimensi realistis berdasarkan jarak dan arah.
- Hubungkan AudioListener ke transformasi kamera, dan hubungkan suara ke PannerNode dengan model panning
Netcode Modern (WebTransport):
- Jangan gunakan WebSocket (TCP) untuk posisi pergerakan pemain karena rentan terhadap Head-of-Line blocking.
- Gunakan WebTransport (datagram UDP over QUIC) atau WebRTC DataChannels disertai Client-Side Prediction dan Server Reconciliation.
Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
|---|---|
| Kaskade Model Frontier | Medium / High (GPT-5.6 Sol Medium, Gemini 4 Argon, Claude Sonnet 5.5 High, Grok 4.7 High Fast) |
| Invarian Fallback Ketersediaan | Gemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Bebas gagal saat model belum tersedia/kuota penuh) |
| Protokol Hemat Token & KV-Cache | Kunci instruksi & skema statis di prefix awal (kv-cache-prefix-optimizer); kirim potongan CONTEXT_MAP.md (<250 token/panggilan) saat Fan-Out |
| Eksekusi SaaT CLI & MCP | CLI: vibes skill exec web-game-engine-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_ast_query, vibes_compile_cache, vibes_optimize_budget, vibes_verify_gate |
| Invarian NFR & Anti-Slop | Latensi P99 API < 85ms, paritas skema ketat OpenAPI 3.1 / TypeSpec, kontrak error RFC 9457 Problem Details, penegakan Idempotency-Key; kode 100% tuntas tanpa placeholder diverifikasi melalui node scripts/check-anti-slop.js --strict |