Particle Components
GPU-accelerated particle systems with ParticleEmitter for fire, smoke, explosions, and visual effects.
Particle components enable high-performance visual effects like fire, smoke, explosions, sparks, and weather. Web Engine uses GPU-accelerated particle systems for rendering thousands of particles with minimal CPU overhead.
ParticleEmitter#
The ParticleEmitter component configures particle emission, lifetime, appearance, and behavior. Supports various emitter shapes, particle animations, and physics-based motion.
Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| maxParticles | ui16 | 1000 | Maximum particle count |
| emissionRate | f32 | 10.0 | Particles emitted per second |
| lifetime | f32 | 2.0 | Particle lifetime in seconds |
| speed | f32 | 5.0 | Initial particle speed (m/s) |
| size | f32 | 1.0 | Initial particle size |
| sizeEnd | f32 | 0.5 | Final particle size (interpolated) |
| color | [f32, 3] | [1, 1, 1] | Initial particle color [r, g, b] |
| colorEnd | [f32, 3] | [1, 1, 1] | Final particle color [r, g, b] |
| gravity | [f32, 3] | [0, -9.81, 0] | Gravity acceleration [x, y, z] |
| textureAssetId | ui32 | 0 | Particle texture asset ID |
| playing | ui8 | 1 | Emission active (0=paused, 1=playing) |
| rotationSpeed | f32 | 0.0 | Particle rotation angular velocity (rad/s) |
| shape | ui8 | 0 | Emitter shape: 0=Sphere, 1=Cone, 2=Box |
| angle | f32 | 0.5 | Cone angle in radians (for shape=1) |
Advanced Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| noiseStrength | f32 | 0.0 | Turbulence/noise strength |
| noiseFrequency | f32 | 1.0 | Turbulence/noise frequency |
| tilesX | ui8 | 1 | Texture sheet columns (sprite animation) |
| tilesY | ui8 | 1 | Texture sheet rows (sprite animation) |
| animationSpeed | f32 | 0.0 | Texture animation frames per second |
| opacity | f32 | 1.0 | Initial particle opacity (0-1) |
| opacityEnd | f32 | 0.0 | Final particle opacity (0-1) |
| velocityVariation | f32 | 0.0 | Speed randomness (0-1) |
| sizeVariation | f32 | 0.0 | Size randomness (0-1) |
| rotationVariation | f32 | 0.0 | Rotation randomness (0-1) |
| colorVariation | f32 | 0.0 | Color randomness (0-1) |
| emissionBursts | ui16 | 0 | Number of emission bursts |
| emissionBurstSize | ui16 | 0 | Particles per burst |
| emissionBurstInterval | f32 | 0.0 | Time between bursts (seconds) |
Audio Properties#
| Property | Type | Default | Description |
|---|---|---|---|
| audioAssetId | ui32 | 0 | Audio clip to play with particles |
| audioVolume | f32 | 1.0 | Audio volume (0-1) |
| audioPitch | f32 | 1.0 | Audio pitch multiplier |
| audioSpatial | ui8 | 1 | Spatial audio (0=2D, 1=3D) |
| audioRefDistance | f32 | 1.0 | Audio reference distance (meters) |
| audioMaxDistance | f32 | 100.0 | Audio max distance (meters) |
| audioRolloff | f32 | 1.0 | Audio distance rolloff factor |
Basic Usage#
import { addComponent, ParticleEmitter } from '@web-engine/core'; // Create smoke emitterconst smoke = world.addEntity();addComponent(world, Transform, smoke);addComponent(world, ParticleEmitter, smoke); // Position above fireTransform.position[smoke] = [0, 2, 0]; // Configure smoke particlesParticleEmitter.maxParticles[smoke] = 500;ParticleEmitter.emissionRate[smoke] = 20.0; // 20 particles/secParticleEmitter.lifetime[smoke] = 3.0; // 3 second lifetimeParticleEmitter.speed[smoke] = 2.0; // Slow upward driftParticleEmitter.size[smoke] = 2.0; // Large particlesParticleEmitter.sizeEnd[smoke] = 4.0; // Grow over time // Gray smoke colorParticleEmitter.color[smoke][0] = 0.3; // Dark grayParticleEmitter.color[smoke][1] = 0.3;ParticleEmitter.color[smoke][2] = 0.3; // Fade outParticleEmitter.opacity[smoke] = 0.8;ParticleEmitter.opacityEnd[smoke] = 0.0; // Slow upward drift with slight gravityParticleEmitter.gravity[smoke][1] = -1.0; // Weak gravity ParticleEmitter.playing[smoke] = 1; // Start emittingEmitter Shapes#
Particle emitters support three shapes for controlling emission direction:
Sphere Emitter (0)#
Emits particles in all directions from a spherical volume.
ParticleEmitter.shape[entity] = 0; // Sphere// Particles emit in all directions (explosion, magic orb)Cone Emitter (1)#
Emits particles in a cone shape, useful for fountains, jets, and directional effects.
ParticleEmitter.shape[entity] = 1; // ConeParticleEmitter.angle[entity] = Math.PI / 6; // 30 degree cone// Particles emit in a cone (fire hose, rocket exhaust, fountain)Box Emitter (2)#
Emits particles from a box volume, useful for rain, snow, and area effects.
ParticleEmitter.shape[entity] = 2; // Box// Particles emit from a box volume (rain, dust cloud)Common Particle Effects#
Fire Effect#
const fire = world.addEntity();addComponent(world, Transform, fire);addComponent(world, ParticleEmitter, fire); ParticleEmitter.maxParticles[fire] = 300;ParticleEmitter.emissionRate[fire] = 50.0;ParticleEmitter.lifetime[fire] = 1.5;ParticleEmitter.speed[fire] = 3.0;ParticleEmitter.size[fire] = 1.5;ParticleEmitter.sizeEnd[fire] = 0.5; // Shrink // Orange to red to blackParticleEmitter.color[fire][0] = 1.0; // OrangeParticleEmitter.color[fire][1] = 0.5;ParticleEmitter.color[fire][2] = 0.0; ParticleEmitter.colorEnd[fire][0] = 0.2; // Dark redParticleEmitter.colorEnd[fire][1] = 0.0;ParticleEmitter.colorEnd[fire][2] = 0.0; // Fade outParticleEmitter.opacity[fire] = 1.0;ParticleEmitter.opacityEnd[fire] = 0.0; // Cone shape pointing upParticleEmitter.shape[fire] = 1;ParticleEmitter.angle[fire] = Math.PI / 8; // 22.5 degree cone // Slight gravity upward (buoyancy)ParticleEmitter.gravity[fire][1] = 2.0; // Add turbulenceParticleEmitter.noiseStrength[fire] = 2.0;ParticleEmitter.noiseFrequency[fire] = 1.5; ParticleEmitter.playing[fire] = 1;Explosion Effect#
const explosion = world.addEntity();addComponent(world, Transform, explosion);addComponent(world, ParticleEmitter, explosion); ParticleEmitter.maxParticles[explosion] = 200;ParticleEmitter.emissionRate[explosion] = 0.0; // Use burst insteadParticleEmitter.lifetime[explosion] = 2.0;ParticleEmitter.speed[explosion] = 15.0; // Fast initial speedParticleEmitter.size[explosion] = 2.0;ParticleEmitter.sizeEnd[explosion] = 0.0; // Shrink to nothing // Yellow-orange flashParticleEmitter.color[explosion][0] = 1.0;ParticleEmitter.color[explosion][1] = 0.8;ParticleEmitter.color[explosion][2] = 0.2; ParticleEmitter.colorEnd[explosion][0] = 0.3; // Dark smokeParticleEmitter.colorEnd[explosion][1] = 0.3;ParticleEmitter.colorEnd[explosion][2] = 0.3; // Sphere emission (all directions)ParticleEmitter.shape[explosion] = 0; // Downward gravityParticleEmitter.gravity[explosion][1] = -9.81; // Single burst of 200 particlesParticleEmitter.emissionBursts[explosion] = 1;ParticleEmitter.emissionBurstSize[explosion] = 200; // Add audioParticleEmitter.audioAssetId[explosion] = explosionSoundId;ParticleEmitter.audioVolume[explosion] = 1.0;ParticleEmitter.audioSpatial[explosion] = 1;ParticleEmitter.audioRefDistance[explosion] = 10.0;ParticleEmitter.audioMaxDistance[explosion] = 200.0; ParticleEmitter.playing[explosion] = 1; // Auto-cleanup after effect finishessetTimeout(() => { removeEntity(world, explosion);}, 2500);Rain Effect#
const rain = world.addEntity();addComponent(world, Transform, rain);addComponent(world, ParticleEmitter, rain); // High above playerTransform.position[rain][1] = 20.0; ParticleEmitter.maxParticles[rain] = 2000;ParticleEmitter.emissionRate[rain] = 200.0; // Heavy rainParticleEmitter.lifetime[rain] = 2.0;ParticleEmitter.speed[rain] = 0.0; // Gravity onlyParticleEmitter.size[rain] = 0.2; // Small dropletsParticleEmitter.sizeEnd[rain] = 0.1; // Blue-white colorParticleEmitter.color[rain][0] = 0.7;ParticleEmitter.color[rain][1] = 0.8;ParticleEmitter.color[rain][2] = 1.0; // Box emitter (area coverage)ParticleEmitter.shape[rain] = 2; // Strong downward gravityParticleEmitter.gravity[rain][1] = -20.0; // Slight randomnessParticleEmitter.velocityVariation[rain] = 0.2; ParticleEmitter.playing[rain] = 1;Sparks Effect#
const sparks = world.addEntity();addComponent(world, Transform, sparks);addComponent(world, ParticleEmitter, sparks); ParticleEmitter.maxParticles[sparks] = 100;ParticleEmitter.emissionRate[sparks] = 30.0;ParticleEmitter.lifetime[sparks] = 0.8;ParticleEmitter.speed[sparks] = 8.0;ParticleEmitter.size[sparks] = 0.3;ParticleEmitter.sizeEnd[sparks] = 0.1; // Bright yellow-orangeParticleEmitter.color[sparks][0] = 1.0;ParticleEmitter.color[sparks][1] = 0.9;ParticleEmitter.color[sparks][2] = 0.3; ParticleEmitter.colorEnd[sparks][0] = 0.8;ParticleEmitter.colorEnd[sparks][1] = 0.2;ParticleEmitter.colorEnd[sparks][2] = 0.0; // Fade out quicklyParticleEmitter.opacity[sparks] = 1.0;ParticleEmitter.opacityEnd[sparks] = 0.0; // Sphere emissionParticleEmitter.shape[sparks] = 0; // GravityParticleEmitter.gravity[sparks][1] = -15.0; // High variationParticleEmitter.velocityVariation[sparks] = 0.5;ParticleEmitter.sizeVariation[sparks] = 0.3; ParticleEmitter.playing[sparks] = 1;Texture Sheet Animation#
For animated particle effects (like fire, smoke, or magic), use texture sheet animation with tilesX, tilesY, and animationSpeed:
// Texture is 4x4 grid of animation frames (16 frames total)ParticleEmitter.tilesX[entity] = 4;ParticleEmitter.tilesY[entity] = 4;ParticleEmitter.animationSpeed[entity] = 12.0; // 12 FPSParticleEmitter.textureAssetId[entity] = animatedTextureId; // Particles will animate through all 16 frames during their lifetimePerformance Optimization#
- Keep maxParticles reasonable (100-1000 for most effects, 2000+ for large-scale effects like rain)
- Use lower emission rates when possible (10-50 particles/sec is often enough)
- Set lifetime appropriately (1-3 seconds for most effects, longer for ambient effects)
- Disable particles when off-screen or too far from camera
- Use texture atlas/sprite sheets instead of individual textures
- Limit concurrent particle systems to 5-10 visible at once
- Use burst emission for one-shot effects instead of continuous emission
- Avoid excessive turbulence/noise (computationally expensive)
Best Practices#
- Use size variation and color variation for more organic effects
- Combine multiple emitters for complex effects (fire + smoke + sparks)
- Use opacity fade (opacityEnd=0) to prevent hard edges at particle death
- Add audio to particle effects for immersion (explosions, fire crackling)
- Use cone emitters for directional effects (jets, fountains, thrust)
- Use box emitters for area effects (rain, snow, dust clouds)
- Set gravity appropriately: negative for rain/snow, positive for fire/smoke buoyancy
- Use bursts for one-shot effects that shouldn't loop (explosions, impacts)