Particle Configuration

Configure how particles are generated and behave.

Position Distribution

  • uniform - Particles spawn uniformly across emitter area
  • normal - Particles spawn with normal (gaussian) distribution

Speed, Direction, Size

Can be single values or ranges:

  • Single number: 100
  • Number range: { "min": 50, "max": 150 }
  • Single vec2 (size): { "x": 10, "y": 10 }
  • Vec2 range (size): { "min": { "x": 5, "y": 5 }, "max": { "x": 20, "y": 20 } }

Rotation

  • null - Auto-calculate from velocity
  • Number/Range - Rotation in radians

Style Types

Dot Style
{
  "style": "dot",
  "color": "#ff0000",
  "glow": {
    "color": "#ff8800",
    "amount": 10
  }
}
  • color - Required. Particle color(s)
  • glow - Optional. Adds glow effect with color and amount
Radial Style
{
  "style": "radial",
  "color": ["#ff0000", "#ffff00"]
}
  • color - Required. Radial gradient color(s)
Line Style
{
  "style": "line",
  "color": "#00ff00",
  "rotationOffset": 1.57,
  "glow": {
    "color": "#88ff88",
    "amount": 5
  }
}
  • color - Required. Line color(s)
  • rotationOffset - Optional. Additional rotation in radians
  • glow - Optional. Adds glow effect
Image Style
{
  "style": "image",
  "image": "path/to/image.png",
  "rotationOffset": 0
}
  • image - Required. Path or URL to image (string or HTMLImageElement)
  • rotationOffset - Optional. Additional rotation in radians

Colors

Single color or array for random selection:

  • Single: "#ff0000"
  • Multiple: ["#ff0000", "#00ff00", "#0000ff"]

Fade

Fade in/out times in seconds (applies to all styles):

{
  "fade": {
    "in": 0.5,
    "out": 1.0
  }
}

Trail

Optional particle trail effect (applies to all styles):

{
  "trail": {
    "length": 10,
    "color": "#ff0000",
    "width": 2,
    "widthDecay": 0.8,
    "alphaDecay": 0.8,
    "decayTime": 0.5
  }
}
  • length - Required. Number of trail segments
  • color - Optional. Trail color override (otherwise uses particle color)
  • width - Optional. Trail width in pixels
  • widthDecay - Optional. 0=no decay, 1=full decay, negative=grow
  • alphaDecay - Optional. 0=no decay, 1=full decay along trail length
  • decayTime - Optional. Time in seconds for trail to fade (default: 0.5)

Options

  • useAttractors - Control attractor effects:
    • false - Not affected by any attractors
    • true - Affected by all attractors
    • "id" - Affected only by attractor with this id
    • ["id1", "id2"] - Affected only by attractors with these ids
  • useForceFields - Control force field effects (same format as useAttractors)
  • useColliders - Control collider interactions (same format as useAttractors)
  • useSinks - Control sink effects (same format as useAttractors)
  • maxSpeed - Maximum velocity magnitude (use -1 for no limit)
  • defaultUpdates - Which default updates to run: "all", "none", or array
  • defaultDraws - Which default draws to run: "all", "none", or array

Emission Configuration

Configure how particles are emitted over time.

Emission Types

Rate Emission

Continuously emits particles at a specified rate:

{
  "type": "rate",
  "rate": 10
}
  • rate - Particles per second (can be number or range)
  • Single value: 10
  • Range: { "min": 5, "max": 15 }
Burst Emission

Emits a specific number of particles at once:

{
  "type": "burst",
  "n": 50,
  "delay": 0
}
  • n - Number of particles to emit (can be number or range)
  • delay - Optional delay before burst in seconds (default: 0)
  • Single value: 50
  • Range: { "min": 30, "max": 70 }
Custom Emission

Use custom logic for emission (advanced):

{
  "type": "custom",
  "f": "function() { return 1; }"
}

Note: Custom functions cannot be edited in JSON format.

Examples

// Steady stream
{ "type": "rate", "rate": 20 }

// Variable rate
{
  "type": "rate",
  "rate": { "min": 10, "max": 30 }
}

// Single burst
{ "type": "burst", "n": 100 }

// Delayed burst
{
  "type": "burst",
  "n": 50,
  "delay": 2.0
}

Particle Generation Functions

Define custom functions for particle generation.

Parameters

n - Index within the current emission batch (0-based)

  • Resets to 0 for each emission batch
  • To emit with varying n values, increase emission rate or use burst mode
  • For a global counter, use emitter.totalParticlesEmitted

Context (this)

Inside custom functions, this is bound to the Emitter instance with these properties:

  • this.position - Emitter position {x, y}
  • this.size - Emitter size {x, y}
  • this.age - Time since emitter creation (seconds)
  • this.totalParticlesEmitted - Global particle counter

Position Function

Determines where particles spawn within the emitter area.

function(n) {
  return { x: 0, y: 0 };
}

Returns: {x: number, y: number} - position vector (absolute coordinates)

Speed Function

Sets initial particle velocity magnitude.

function(n) {
  return 100;
}

Returns: number - speed in pixels/second

Direction Function

Sets initial particle velocity direction.

function(n) {
  return 0;
}

Returns: number - angle in radians (0 = right, Ο€/2 = down)

Size Function

Sets particle dimensions.

function(n) {
  return { x: 10, y: 10 };
}

Returns: {x: number, y: number} - size vector

Rotation Function

Sets initial particle rotation.

function(n) {
  return 0;
}

Returns: number - rotation in radians

Note: Return null for auto-rotation based on velocity.

Lifespan Function

Sets how long particles live.

function(n) {
  return 3;
}

Returns: number - lifespan in seconds

Examples

// Spiral using global counter (direction)
function(n) {
  const total = this.totalParticlesEmitted;
  const angle = (total / 10) * Math.PI * 2;
  return angle;
}

// Emit in a circle (direction)
function(n) {
  // n is 0-9 if emitting 10 at once
  return (n / 10) * Math.PI * 2;
}

// Random within emitter (position)
function(n) {
  return {
    x: this.position.x + (Math.random() - 0.5) * this.size.x,
    y: this.position.y + (Math.random() - 0.5) * this.size.y
  };
}

// Grid pattern (position)
function(n) {
  const cols = 5;
  const row = Math.floor(n / cols);
  const col = n % cols;
  return {
    x: this.position.x + col * 20,
    y: this.position.y + row * 20
  };
}

// Pulsing size
function(n) {
  const pulse = Math.sin(this.age * 2) * 5 + 10;
  return { x: pulse, y: pulse };
}

Custom Force Parameters

Configure parameters for built-in force field functions.

Wave

Creates a wave effect, oscillating particles perpendicular to the force direction.

{
  "frequency": 1,
  "amplitude": 50
}
  • frequency - Oscillations per second (default: 1)
  • amplitude - Distance particles are pushed side to side (default: 50)

Vortex

Creates a vortex effect, causing particles to spiral around a center point.

{
  "center": { "x": 400, "y": 300 },
  "strength": 1,
  "range": 100,
  "clockwise": false
}
  • center - Required. Center point of the vortex (vec2)
  • strength - How strongly particles are pulled (default: 1)
  • range - Distance from center where effect applies (default: 100)
  • clockwise - Spiral direction (default: false)

Orbital

Creates an orbital effect, causing particles to orbit around a center point.

{
  "center": { "x": 400, "y": 300 },
  "strength": 1,
  "range": 100
}
  • center - Required. Center point of the orbit (vec2)
  • strength - How strongly particles are pulled into orbit (default: 1)
  • range - Distance from center where effect applies (default: 100)

Vector Field

Creates a vector field using noise for complex, flowing motion patterns.

{
  "noise": null,
  "noiseScale": 0.01,
  "timeScale": 0.1,
  "forceAmount": 100
}
  • noise - Required. Noise function: (x, y, z) => number (returns [-1, 1])
  • noiseScale - Size of noise features (smaller = smoother) (default: 0.01)
  • timeScale - Speed of field changes (default: 0.1)
  • forceAmount - Strength of particle effect (default: 100)

Note: The noise function must be provided programmatically (cannot be edited in JSON).

Turbulence

Creates turbulence using random forces for chaotic, jittery motion.

{
  "strength": 100,
  "frequency": 10
}
  • strength - How strongly particles are affected (default: 100)
  • frequency - How often random force changes (default: 10)

Drag

Creates a drag effect, simulating air resistance or friction.

{
  "coefficient": 0.5
}
  • coefficient - Amount of drag (0 = none, 1 = maximum) (default: 0.5)

Boids

Implements flocking behavior with separation, alignment, and cohesion.

{
  "separationDistance": 25,
  "alignmentDistance": 50,
  "cohesionDistance": 50,
  "separationWeight": 1.5,
  "alignmentWeight": 1.0,
  "cohesionWeight": 1.0
}
  • separationDistance - How close is too close (default: 25)
  • alignmentDistance - Range for alignment behavior (default: 50)
  • cohesionDistance - Range for cohesion behavior (default: 50)
  • separationWeight - Strength of separation force (default: 1.5)
  • alignmentWeight - Strength of alignment force (default: 1.0)
  • cohesionWeight - Strength of cohesion force (default: 1.0)

Warning: O(nΒ²) complexity. Only use with <200 particles.

Custom Emission Control

Define a custom function to control particle emission.

Function Signature

function() {
  return 1;
}

Returns: number - number of particles to emit this frame

Context (this)

Inside the function, this is bound to the Emitter instance:

  • this.position - Emitter position {x, y}
  • this.size - Emitter size {x, y}
  • this.age - Time since emitter creation (seconds)
  • this.totalParticlesEmitted - Total particles emitted so far

Examples

// Emit based on age
function() {
  return Math.floor(this.age * 2);
}

// Pulsing emission
function() {
  const pulse = Math.sin(this.age * 3) * 5 + 5;
  return Math.floor(pulse);
}

// Stop after 100 particles
function() {
  return this.totalParticlesEmitted < 100 ? 1 : 0;
}

// Random bursts
function() {
  return Math.random() < 0.1 ? 10 : 0;
}

Particle Lifecycle Hooks

Define custom update and draw functions for particles.

Update Function

Called every frame for custom particle behavior.

function(system, dt) {
  // Custom update logic
}

Parameters:

  • system - ParticleSystem instance
  • dt - Delta time (seconds)

PreDraw Function

Called before default rendering. Set context state here.

function(system, context) {
  // Set shadows, alpha, etc.
}

Parameters:

  • system - ParticleSystem instance
  • context - CanvasRenderingContext2D

PostDraw Function

Called after default rendering. Add custom effects.

function(system, context) {
  // Draw additional effects
}

Context (this)

All functions are bound to the Particle instance:

  • this.position - Particle position {x, y}
  • this.velocity - Particle velocity {x, y}
  • this.size - Particle size {x, y}
  • this.rotation - Particle rotation (radians)
  • this.age - Particle age (seconds)
  • this.lifespan - Particle lifespan (seconds)

Examples

// Update: Wobble effect
function(system, dt) {
  this.position.x += Math.sin(this.age * 10) * 2;
}

// PreDraw: Add shadow
function(system, context) {
  context.shadowColor = 'black';
  context.shadowBlur = 10;
}

// PostDraw: Draw text
function(system, context) {
  context.fillStyle = 'white';
  context.fillText('!', 0, 0);
}

Custom Force Function

Define a custom force field function to apply to particles.

Function Signature

function(system, forceField, dt) {
  // Apply forces to this particle
}

Parameters:

  • system - ParticleSystem instance
  • forceField - ForceField instance
  • dt - Delta time (seconds)

Context (this)

The function is bound to the Particle being affected:

  • this.position - Particle position {x, y}
  • this.velocity - Particle velocity {x, y}
  • this.size - Particle size {x, y}
  • this.age - Particle age (seconds)

ForceField Properties

  • forceField.force - Force vector {x, y}
  • forceField.age - Force field age (seconds)
  • forceField.customForceParams - Custom parameters

Examples

// Circular motion
function(system, forceField, dt) {
  const center = { x: 400, y: 300 };
  const dx = this.position.x - center.x;
  const dy = this.position.y - center.y;
  this.velocity.x += -dy * dt;
  this.velocity.y += dx * dt;
}

// Age-based force
function(system, forceField, dt) {
  const strength = this.age * 100;
  this.velocity.y += strength * dt;
}

// Particle interaction
function(system, forceField, dt) {
  system.particles.forEach(other => {
    if (other === this) return;
    const dx = other.position.x - this.position.x;
    const dy = other.position.y - this.position.y;
    const dist = Math.sqrt(dx*dx + dy*dy);
    if (dist < 50 && dist > 0) {
      this.velocity.x -= (dx/dist) * 10 * dt;
      this.velocity.y -= (dy/dist) * 10 * dt;
    }
  });
}