A voxel engine built from scratch in JavaScript + Rust/WASM with a custom WebGPU rendering pipeline.
Un moteur voxel construit from scratch en JavaScript + Rust/WASM avec un pipeline de rendu WebGPU custom.
Not a tutorial project. This is a full-featured Minecraft clone running entirely in the browser — custom WebGPU renderer, procedural world generation compiled to WebAssembly, greedy meshing, cascaded shadow maps, screen-space reflections, volumetric god rays, and real-time multiplayer.
The rendering pipeline is written against the WebGPU API directly (no Three.js), with 24 hand-written WGSL shaders totaling 3,500+ lines. Performance-critical subsystems (terrain generation, mesh building, fluid simulation) are implemented in Rust, compiled to WASM, and executed in dedicated Web Workers to keep the main thread free.
Pas un projet tutoriel. C'est un clone Minecraft complet tournant entièrement dans le navigateur — renderer WebGPU custom, génération procédurale compilée en WebAssembly, greedy meshing, shadow maps en cascade, réflexions screen-space, god rays volumétriques, et multijoueur temps réel.
Le pipeline de rendu est écrit directement contre l'API WebGPU (pas de Three.js), avec 24 shaders WGSL écrits à la main totalisant 3 500+ lignes. Les sous-systèmes critiques (génération de terrain, construction de mesh, simulation de fluides) sont implémentés en Rust, compilés en WASM, et exécutés dans des Web Workers dédiés.
┌─────────────────────────────────────────────────────────────────┐
│ BROWSER CLIENT │
│ │
│ Game Loop (60 Hz) │
│ ├── InputManager ── keyboard, mouse, pointer lock │
│ ├── Physics ── AABB collision, raycasting │
│ ├── ChunkManager ── LOD selection, spiral loading │
│ ├── World ── block state, entity management │
│ └── WebGPU Renderer ─────────────────────────────────┐ │
│ ├── Terrain Pipeline (930-line terrain.wgsl) │ │
│ ├── Shadow Renderer (CSM, 3 cascades) │ │
│ ├── PostProcess Pipeline │ │
│ │ ├── GTAO (screen-space AO) │ │
│ │ ├── SSR (screen-space reflections)│ │
│ │ ├── Bloom (3-pass threshold/blur) │ │
│ │ ├── Volumetric (god rays + blur) │ │
│ │ ├── Clouds (procedural sky layer) │ │
│ │ └── Composite (HDR tone mapping) │ │
│ ├── Water Pipeline (refraction + caustics) │ │
│ ├── Entity Renderer (GLB + VAT skinning) │ │
│ ├── Weather Renderer (rain / snow particles) │ │
│ └── LOD5 Instance Renderer (GPU instancing) │ │
│ │
│ Web Workers (off main thread) │
│ ├── WasmChunkWorker ── terrain gen (Rust/WASM) │
│ ├── WasmMeshWorker ── greedy meshing (Rust/WASM) │
│ └── WasmFluidWorker ── fluid simulation (Rust/WASM) │
│ │
├─────────────────────────────────────────────────────────────────┤
│ Rust → WASM (6 crates, 4,053 lines) │
│ ├── mc-core types, block registry, 52 block types │
│ ├── mc-noise Simplex/Perlin noise, octave stacking │
│ ├── mc-worldgen biome selection, terrain shaping, caves │
│ ├── mc-mesh greedy meshing, LOD0–LOD5 mesh generation │
│ ├── mc-fluid water/lava propagation, level simulation │
│ └── mc-wasm JS ↔ Rust FFI bindings │
│ │
├────────────── WebSocket (Socket.io) ────────────────────────────┤
│ │
│ Server (Node.js) │
│ ├── GameServer ── tick loop (20 TPS) │
│ ├── PlayerManager ── auth, state sync, interpolation │
│ ├── ChunkLoader ── server-side chunk persistence │
│ └── WorldSync ── block change broadcast │
└─────────────────────────────────────────────────────────────────┘
| Metric | Value |
|---|---|
| JavaScript source files | 90 |
| JavaScript lines of code | 31,480 |
| Rust lines of code | 4,053 |
| WGSL shader files | 24 |
| WGSL shader lines | 3,532 |
| Rust crates | 6 |
| Block types | 52 |
| Biomes | 9 |
| Mob types | 4 |
| Block textures | 56 |
| LOD levels | 6 (LOD0–LOD5) |
The renderer targets WebGPU natively — no abstraction layer, no Three.js. Each frame executes the following pass chain:
Shadow Pass (CSM × 3 cascades, 2048² per cascade)
│
▼
Terrain Pass ──→ HDR Color Buffer (rgba16float)
│ + GBuffer Normals
│ + Depth (depth32float)
▼
Water Pass (refraction, depth absorption, foam, caustics)
│
▼
Entity Pass (GLB models + Vertex Animation Textures)
│
▼
Weather Pass (particle rain / snow)
│
▼
┌──────────────── Post-Processing ────────────────┐
│ GTAO (screen-space ambient occlusion) │
│ SSR (screen-space reflections) │
│ Bloom (threshold → downsample → upsample) │
│ Volumetric Light (god rays + radial blur) │
│ Clouds (procedural, 4 presets) │
│ Hole Fill (edge gap repair) │
│ Composite (tone mapping, exposure control) │
└─────────────────────────────────────────────────┘
│
▼
Swap Chain
| Shader | Lines | Purpose |
|---|---|---|
terrain.wgsl |
930 | Block rendering, AO, lighting, fog, water vertex animation |
clouds.wgsl |
297 | Procedural volumetric cloud layer |
weather.wgsl |
276 | Rain/snow particle rendering |
skybox.wgsl |
237 | Atmospheric scattering, sun/moon, day-night cycle |
vatEntity.wgsl |
212 | Vertex Animation Texture skinning for mobs |
ssr.wgsl |
181 | Screen-space reflections with ray marching |
composite.wgsl |
135 | Final HDR compositing and tone mapping |
entity.wgsl |
135 | Entity rendering with directional light |
gtao.wgsl |
116 | Ground-truth ambient occlusion |
shadowPass.wgsl |
110 | Cascaded shadow map generation |
| + 14 more | 903 | Bloom, blur, sprites, lines, overlays... |
Terrain is generated entirely in Rust/WASM via dedicated Web Workers. The pipeline:
- Noise sampling — Layered Simplex noise (continental, erosion, temperature, humidity, mountain)
- Biome selection — 9 biomes (Plains, Forest, Desert, Snow, Mountains, Ocean, Swamp, Taiga, Birch Forest) derived from temperature × humidity × continental maps
- Height shaping — Per-biome height modifiers and scale factors applied to base elevation
- Cave carving — 3D noise-based cave systems
- Structure placement — Trees (oak, birch, spruce), vegetation, cactus, dead bushes
- Ore distribution — Coal, iron, gold, diamond, emerald, lapis, redstone, copper + deepslate variants
- Chunk dimensions: 16 × 16 × 256 (65,536 blocks per chunk)
- LOD system with 6 tiers:
| LOD | Range (chunks) | Strategy |
|---|---|---|
| LOD0 | 0–4 | Full geometry + ambient occlusion |
| LOD1 | 4–8 | Full geometry, no AO |
| LOD2 | 8–18 | Surface only, caves culled |
| LOD3 | 18–32 | Merged 2×2 block geometry |
| LOD4 | 32–64 | Heightmap quads (~256 quads/chunk) |
| LOD5 | 64–1024 | Single quad per chunk, GPU instanced |
- Greedy meshing in Rust — merges coplanar adjacent faces with identical textures into larger quads, drastically reducing draw call and vertex count
- Spiral loading — chunks load outward from the player in concentric rings
- Block data eviction — beyond 64 chunks, block arrays are freed while GPU meshes persist
Water and lava propagation is computed in Rust/WASM inside a dedicated Web Worker. The system supports:
- Multi-level water with 8 discrete levels
- Horizontal spreading with distance-based level decay
- Vertical falling with instant level fill
- Source block detection and infinite water rules
- Lava with slower tick rate
Real-time multiplayer via Socket.io with authoritative server:
- Server tick rate: 20 TPS (50ms interval)
- Player state interpolation on client
- Chunk loading/unloading synced per player viewport
- Block change broadcast to all connected clients
- Chat system
- Inventory — 36-slot grid + 9-slot hotbar (max stack: 64)
- Crafting — Shaped recipe system with 3×3 grid (10 recipes: tools, blocks, torches)
- Health — HUD with health bar and hunger display
- Mobs — Zombie, Skeleton, Creeper, Pig with AI pathfinding and GLB animated models
- Block breaking — Raycasting + overlay animation + particle effects
- Dropped items — Physics-based with water floating
- Day/night cycle — 20-minute full cycle with dynamic sky, sun/moon, and light changes
- Weather — Rain and snow with particle rendering
- World persistence — IndexedDB-based save/load via SaveManager
- Debug tools — Performance profiler, memory profiler, shadow/water/sky settings panels
- Node.js 18+
- Rust toolchain +
wasm-pack - Browser with WebGPU support (Chrome 113+, Edge 113+, Firefox Nightly)
# 1. Build WASM modules
./build-wasm.sh
# → compiles 6 Rust crates to client/src/wasm/pkg/
# 2. Start client (dev server)
cd client
npm install
npm run dev
# → http://localhost:5173
# 3. Start multiplayer server (optional)
cd server
npm install
npm start
# → ws://localhost:3001├── client/
│ ├── src/
│ │ ├── core/ Engine, InputManager, AudioManager, Settings
│ │ ├── renderer/ WebGPU renderer, 24 WGSL shaders, post-processing
│ │ ├── world/ Chunk, ChunkManager, World, Block, FluidManager
│ │ ├── terrain/ BiomeManager, NoiseGenerator, StructureGen
│ │ ├── entities/ Player, Mob, AI, 4 mob types
│ │ ├── physics/ AABB collision, raycasting
│ │ ├── inventory/ Inventory, Hotbar, ItemRegistry
│ │ ├── crafting/ CraftingManager, shaped recipes
│ │ ├── network/ NetworkManager, interpolation, packets
│ │ ├── storage/ WorldStorage, SaveManager (IndexedDB)
│ │ ├── workers/ 3 WASM Web Workers
│ │ ├── ui/ 12 UI modules (debug, profiler, inventory...)
│ │ └── wasm/pkg/ Compiled Rust → WASM output
│ └── public/textures/ 56 block/UI textures + GLB models
├── rust/
│ ├── mc-core/ Block types, constants, color/texture maps
│ ├── mc-noise/ Simplex noise implementation
│ ├── mc-worldgen/ Biome + terrain generator
│ ├── mc-mesh/ Greedy meshing (LOD0–LOD5)
│ ├── mc-fluid/ Fluid propagation simulation
│ └── mc-wasm/ JS ↔ Rust FFI layer
├── server/ Node.js multiplayer server
└── shared/ Constants, block definitions, packet types
Mathieu Fournier · mathieufournierqc@outlook.com — @Maaattqc