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#

PropertyTypeDefaultDescription
maxParticlesui161000Maximum particle count
emissionRatef3210.0Particles emitted per second
lifetimef322.0Particle lifetime in seconds
speedf325.0Initial particle speed (m/s)
sizef321.0Initial particle size
sizeEndf320.5Final 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]
textureAssetIdui320Particle texture asset ID
playingui81Emission active (0=paused, 1=playing)
rotationSpeedf320.0Particle rotation angular velocity (rad/s)
shapeui80Emitter shape: 0=Sphere, 1=Cone, 2=Box
anglef320.5Cone angle in radians (for shape=1)

Advanced Properties#

PropertyTypeDefaultDescription
noiseStrengthf320.0Turbulence/noise strength
noiseFrequencyf321.0Turbulence/noise frequency
tilesXui81Texture sheet columns (sprite animation)
tilesYui81Texture sheet rows (sprite animation)
animationSpeedf320.0Texture animation frames per second
opacityf321.0Initial particle opacity (0-1)
opacityEndf320.0Final particle opacity (0-1)
velocityVariationf320.0Speed randomness (0-1)
sizeVariationf320.0Size randomness (0-1)
rotationVariationf320.0Rotation randomness (0-1)
colorVariationf320.0Color randomness (0-1)
emissionBurstsui160Number of emission bursts
emissionBurstSizeui160Particles per burst
emissionBurstIntervalf320.0Time between bursts (seconds)

Audio Properties#

PropertyTypeDefaultDescription
audioAssetIdui320Audio clip to play with particles
audioVolumef321.0Audio volume (0-1)
audioPitchf321.0Audio pitch multiplier
audioSpatialui81Spatial audio (0=2D, 1=3D)
audioRefDistancef321.0Audio reference distance (meters)
audioMaxDistancef32100.0Audio max distance (meters)
audioRollofff321.0Audio distance rolloff factor

Basic Usage#

import { addComponent, ParticleEmitter } from '@web-engine/core';
// Create smoke emitter
const smoke = world.addEntity();
addComponent(world, Transform, smoke);
addComponent(world, ParticleEmitter, smoke);
// Position above fire
Transform.position[smoke] = [0, 2, 0];
// Configure smoke particles
ParticleEmitter.maxParticles[smoke] = 500;
ParticleEmitter.emissionRate[smoke] = 20.0; // 20 particles/sec
ParticleEmitter.lifetime[smoke] = 3.0; // 3 second lifetime
ParticleEmitter.speed[smoke] = 2.0; // Slow upward drift
ParticleEmitter.size[smoke] = 2.0; // Large particles
ParticleEmitter.sizeEnd[smoke] = 4.0; // Grow over time
// Gray smoke color
ParticleEmitter.color[smoke][0] = 0.3; // Dark gray
ParticleEmitter.color[smoke][1] = 0.3;
ParticleEmitter.color[smoke][2] = 0.3;
// Fade out
ParticleEmitter.opacity[smoke] = 0.8;
ParticleEmitter.opacityEnd[smoke] = 0.0;
// Slow upward drift with slight gravity
ParticleEmitter.gravity[smoke][1] = -1.0; // Weak gravity
ParticleEmitter.playing[smoke] = 1; // Start emitting

Emitter 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; // Cone
ParticleEmitter.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 black
ParticleEmitter.color[fire][0] = 1.0; // Orange
ParticleEmitter.color[fire][1] = 0.5;
ParticleEmitter.color[fire][2] = 0.0;
ParticleEmitter.colorEnd[fire][0] = 0.2; // Dark red
ParticleEmitter.colorEnd[fire][1] = 0.0;
ParticleEmitter.colorEnd[fire][2] = 0.0;
// Fade out
ParticleEmitter.opacity[fire] = 1.0;
ParticleEmitter.opacityEnd[fire] = 0.0;
// Cone shape pointing up
ParticleEmitter.shape[fire] = 1;
ParticleEmitter.angle[fire] = Math.PI / 8; // 22.5 degree cone
// Slight gravity upward (buoyancy)
ParticleEmitter.gravity[fire][1] = 2.0;
// Add turbulence
ParticleEmitter.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 instead
ParticleEmitter.lifetime[explosion] = 2.0;
ParticleEmitter.speed[explosion] = 15.0; // Fast initial speed
ParticleEmitter.size[explosion] = 2.0;
ParticleEmitter.sizeEnd[explosion] = 0.0; // Shrink to nothing
// Yellow-orange flash
ParticleEmitter.color[explosion][0] = 1.0;
ParticleEmitter.color[explosion][1] = 0.8;
ParticleEmitter.color[explosion][2] = 0.2;
ParticleEmitter.colorEnd[explosion][0] = 0.3; // Dark smoke
ParticleEmitter.colorEnd[explosion][1] = 0.3;
ParticleEmitter.colorEnd[explosion][2] = 0.3;
// Sphere emission (all directions)
ParticleEmitter.shape[explosion] = 0;
// Downward gravity
ParticleEmitter.gravity[explosion][1] = -9.81;
// Single burst of 200 particles
ParticleEmitter.emissionBursts[explosion] = 1;
ParticleEmitter.emissionBurstSize[explosion] = 200;
// Add audio
ParticleEmitter.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 finishes
setTimeout(() => {
removeEntity(world, explosion);
}, 2500);

Rain Effect#

const rain = world.addEntity();
addComponent(world, Transform, rain);
addComponent(world, ParticleEmitter, rain);
// High above player
Transform.position[rain][1] = 20.0;
ParticleEmitter.maxParticles[rain] = 2000;
ParticleEmitter.emissionRate[rain] = 200.0; // Heavy rain
ParticleEmitter.lifetime[rain] = 2.0;
ParticleEmitter.speed[rain] = 0.0; // Gravity only
ParticleEmitter.size[rain] = 0.2; // Small droplets
ParticleEmitter.sizeEnd[rain] = 0.1;
// Blue-white color
ParticleEmitter.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 gravity
ParticleEmitter.gravity[rain][1] = -20.0;
// Slight randomness
ParticleEmitter.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-orange
ParticleEmitter.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 quickly
ParticleEmitter.opacity[sparks] = 1.0;
ParticleEmitter.opacityEnd[sparks] = 0.0;
// Sphere emission
ParticleEmitter.shape[sparks] = 0;
// Gravity
ParticleEmitter.gravity[sparks][1] = -15.0;
// High variation
ParticleEmitter.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 FPS
ParticleEmitter.textureAssetId[entity] = animatedTextureId;
// Particles will animate through all 16 frames during their lifetime

Performance 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)
Components | Web Engine Docs | Web Engine Docs