Particle System
The engine's flagship subsystem — 100M+ GPU particles with full physics, thermal simulation, chemistry, SPH fluids, SDF collision, flocking, and volumetric rendering. Everything runs on compute shaders via vGPU.
Scale: The particle system spans 60+ simulation files and 30+ rendering files — the largest subsystem in the engine. This guide covers the architecture and key concepts. See individual module docs for deep dives.
Architecture Overview
The particle system has three distinct layers:
┌─────────────────────────────────────────────┐
│ Editor / Game API │
│ EditorParticles.js — orchestrates per-frame │
├─────────────────────────────────────────────┤
│ Rendering Layer (engine/render/particles/) │
│ SDF Billboard · Half-Res Composite · Beams │
│ Bonds · Decals · Distortion · Mesh · Trail │
├─────────────────────────────────────────────┤
│ Simulation Layer (engine/sim/particles/) │
│ ParticleSimWorld — main compute shader │
│ + 30 advanced subsystems (GPU compute) │
├─────────────────────────────────────────────┤
│ Emitter System (ParticleEmitterSystem.js) │
│ Presets, spawn shapes, rate control │
└─────────────────────────────────────────────┘
Per-Frame Pipeline
Each frame in EditorParticles.js:
- Emit — Spawn new particles from active emitters into GPU buffers
- Alive-list compaction — GPU scan writes alive particle indices (skip dead slots)
- Main sim — GPU compute: gravity, curl noise, forces, integration, lifetime
- Advanced systems — Thermal, SPH, flocking, bonds, chemistry, constraints
- Sort — Radix sort by camera distance for correct alpha blending
- Collisions — SDF collision against entity meshes + ground plane
- Light extraction — GPU compute finds hottest particles → async readback → LightManager
- Render — SDF billboard at half-res → additive composite onto scene
Emitter System
ParticleEmitterSystem.js manages emitter presets and controls particle spawning.
Matter States
| State | Phase Value | Render Mode | Examples |
|---|---|---|---|
| Solid | 0 | Matte diffuse + specular | Sparks, debris, snow |
| Liquid | 1 | Fresnel + refraction | Water, blood, rain |
| Gas | 2 | Volumetric (Beer-Lambert) | Smoke, steam, fog |
| Plasma | 3 | Emissive glow | Fire core, lightning, magic |
Spawn Shapes
ParticleSpawnShapes.js provides configurable emission geometries:
- Point — All particles from a single position
- Sphere — Surface or volume of a sphere
- Box — Axis-aligned box volume
- Cone — Directional cone emission
- Ring — Torus-shaped emission
- Mesh surface — Emit from mesh triangles (
MeshToParticlesCompute.js)
GPU Simulation
The core simulation runs in ParticleSimWorld.js — a massive compute shader that processes all particles each frame. It reads from and writes to several GPU buffers:
Buffer Layout
| Buffer | Per-Particle Data | Stride |
|---|---|---|
| Position | vec4(x, y, z, lifetime) | 16 bytes |
| Velocity | vec4(vx, vy, vz, age) | 16 bytes |
| Thermal | vec4(temperature, heat, packedMaterial, latentEnergy) | 16 bytes |
| Color | vec4(r, g, b, size) | 16 bytes |
thermalData.z packing: Lower 8 bits = material index (0–15), upper bits = collision group ID. Encode: (groupId << 8) | materialIdx. Decode in WGSL: materialIdx = u32(thermal.z) & 0xFFu.
Advanced Subsystems
Initialized via initAllAdvancedSystems(), these modules extend the core sim:
Thermal & Chemistry
Temperature simulation with material-specific melt/boil points, latent heat, and phase transitions. 16 material presets (water, metal, wax, lava, etc.).
SPH Fluids
Smoothed Particle Hydrodynamics for realistic fluid behavior — pressure, viscosity, surface tension.
SDF Collision
Particles collide with entity meshes using signed distance fields (sphere, box, cylinder shapes). Per-entity SDF from PrefabRegistry.
Flocking
Boids-style behavior: separation, alignment, cohesion. Uses neighbor grid for spatial queries.
Bonds
Spring connections between particles for soft-body/cloth-like behavior. Async CPU readback for bond evaluation.
Electromagnetic
Electric and magnetic field forces. Charged particle interactions.
Constraints
Distance, position, and velocity constraints. Used for rope, chains, and attached particles.
Event System
Particle events (collision, death, threshold) that trigger sub-emissions or gameplay actions.
Performance Optimizations
Alive-List Compaction
Instead of dispatching threads for all slots (including dead particles), a GPU scan builds an alive list of active particle indices. The main sim then dispatches only aliveCount threads via indirect dispatch.
Radix Sort
Replaced bitonic sort with 4-bit radix sort. For 100M particles: 24 dispatches (8 passes × 3) vs bitonic's ~289 dispatches. Drop-in replacement via ParticleRadixSort.js.
FBM Noise Pre-bake
ParticleNoiseTexture.js pre-bakes 4-octave FBM curl noise into a 3D texture. The sim shader does a single textureSampleLevel instead of computing 4 octaves per particle per frame.
Neighbor Grid
Spatial hashing grid for O(1) neighbor queries. Used by SPH, flocking, and bond systems. Rebuilds each frame on GPU.
Rendering
SDF Billboard Renderer
The primary particle renderer (ParticleSdfRenderer.js) draws each particle as a camera-facing quad and raymarches a signed distance field in the fragment shader to produce volumetric shapes.
The fragment shader branches by matter phase:
- Solid (0) — Matte diffuse + subtle specular, opaque sphere
- Liquid (1) — Broad specular, Fresnel reflection, transparent center
- Gas (2) — Volumetric raymarch with Beer-Lambert transmittance, Henyey-Greenstein phase function, depth-based self-shadowing
- Plasma (3) — Pure emissive glow, no diffuse
Lifetime Effects
Color, size, and alpha are modulated over lifetime via GPU textures:
- Color gradient —
rgba8unorm1D texture, sampled withtextureSampleLevel - Lifetime curves —
rgba32floattexture (alpha, size, velocity curves), sampled withtextureLoad(unfilterable format)
Blackbody Rendering
Hot particles use the Tanner Helland blackbody approximation (CIE 1931) instead of a linear ramp. Very hot particles (thermalGlow > 0.5) get an emissive bloom boost.
Additional Renderers
| Renderer | Purpose |
|---|---|
ParticleBeamRenderer | Laser/lightning beam connections |
ParticleBondRenderer | Spring/bond visualization |
ParticleDecalRenderer | Ground impact decals with age fade |
ParticleDistortionRenderer | Heat haze distortion post-effect |
ParticleMeshRenderer | Mesh-shaped particles (rocks, debris) |
ParticleTrailHistory | Ribbon/trail rendering |
ParticleSPHSurfaceRenderer | Screen-space fluid surface (marching) |
RopeGPURenderer | GPU-accelerated rope/chain rendering |
Audio Integration
ParticleAudioBridge.js maps particle substance properties (temperature, density, velocity) to procedural audio parameters. Fire crackles, water splashes, and wind howls are all generated from particle state — no pre-recorded samples needed.
Key Files
| File | Purpose |
|---|---|
sim/particles/ParticleSimWorld.js | Core GPU compute sim (205K lines) |
sim/particles/ParticleEmitterSystem.js | Emitter presets and spawning (78K) |
sim/particles/ParticleAdvanced.js | Advanced subsystem orchestration (65K) |
sim/particles/ParticleConstraints.js | Distance/position constraints (59K) |
render/particles/ParticleSdfRenderer.js | SDF billboard rendering |
render/particles/ParticleHalfResComposite.js | Half-res compositing |
render/shaders/modules/core/particles_sdf_billboard.js | Main SDF particle shader (700+ lines) |
editor/js/modules/EditorParticles.js | Per-frame orchestration |