Foundry Virtual Tabletop - API Documentation - Version 14
    Preparing search index...

    A lightweight, native particle generator designed for VFX.

    ParticleGenerator manages:

    • An internal container on the chosen canvas layer (usually canvas.primary)
    • Particle pooling (reusing sprites instead of constantly allocating new ones)
    • Lifetime, fade-in/out, basic motion, and optional constraints
    • Two usage styles:
      • ambient: keep a steady density in the visible area (viewport-driven budget)
      • effect: spawn particles in a specific area (manual spawns or a fixed target count)

    The API is intentionally compact:

    • Put most settings at the top level (textures, blend, alpha, scale, count, lifetime, etc.)
    • Use area to define where particles spawn
    • Use spawnParticle() or spawnParticles() to spawn particles (optionally overriding texture/area/position)
    • Use start({spawn: n}) when you want to immediately seed a certain number of particles on start
    • Use onSpawn to customize each particle after it has been positioned and configured
    • Use onDeath to react when particles are recycled (optional)
    • mode: "ambient" maintains a stable density in the visible region. count is scaled by the visible area ratio, and spawning uses the padded viewport (viewPadding).
    • mode: "effect" spawns in a defined area. count is treated as an absolute target. You can use manual: true and spawn at your own rate via spawnParticle() / spawnParticles().

    fade.in / fade.out accept:

    • milliseconds when value >= 1
    • a fraction of the particle lifetime when 0 < value < 1

    velocity can be:

    • fixed: {x, y} or new PIXI.Point(x, y)
    • ranged: {x: [min, max], y: [min, max]}
    • polar: {speed: [min, max], angle: [min, max]} (angle in degrees)

    area supports:

    • Point: {x, y}
    • Rect: {x, y, width, height} or new PIXI.Rectangle(x, y, w, h)
    • Circle: {x, y, radius}
    • Ring: {x, y, innerRadius, outerRadius} or {x, y, radius: [inner, outer]}
    • Line: {from: {x, y}, to: {x, y}}

    If you provide an anchor, object-based areas can be treated as offsets relative to that anchor. To force an absolute rectangle while anchored, pass a PIXI.Rectangle.

    • Preload textures before creating the generator.
    • Prefer calling spawnParticle() or spawnParticles() with no options when you can. It avoids per-spawn object allocations.
    • Keep positionTest cheap. It runs in a hot path.
    • Avoid blur unless you really need it (pre-blurred textures are better).

    ParticleGenerator is meant for local, short-lived effects (bursts, embers, motes, small auras), not for filling the entire scene with massive particle counts. If you push it into many thousands of active particles, performance could degrade quickly. In practice, you’ll get better results by using moderate counts, short lifetimes, and small spawn areas, and by stopping the effect when it’s no longer needed.

    Texture size also matters. Large particle textures (or heavy blur) increase pixel work, especially with additive/screen blends and overdraw, which can become fill-rate bound on some GPUs. Prefer small textures (and sprite sheets!), keep particle sizes reasonable on screen, and avoid expensive full-screen coverage when you only need a local effect.

    Use this for soft, always-on atmospheric particles.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "ambient",
    textures: [
    "worlds/shared_assets/particles/dust1.png",
    "worlds/shared_assets/particles/dust2.png"
    ],
    blend: PIXI.BLEND_MODES.NORMAL,
    count: 300,
    viewPadding: 0.25,
    lifetime: [1200, 2200],
    fade: {in: 0.15, out: 0.25},
    velocity: {x: [-5, 5], y: [-10, -30]},
    alpha: [0.08, 0.18],
    scale: [0.35, 0.85]
    });
    gen.start();

    Great for snowfall that looks continuous when you pan the camera.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "ambient",
    textures: ["worlds/shared_assets/particles/snow.png"],
    count: 400,
    viewPadding: 0.3,
    randomizeAgeInPadding: true,
    lifetime: [2500, 4500],
    fade: {in: 0.2, out: 0.2},
    velocity: {x: [-8, 8], y: [-25, -55]},
    alpha: [0.12, 0.25],
    scale: [0.3, 0.9],
    blend: PIXI.BLEND_MODES.SCREEN
    });
    gen.start();

    Use this for clicks, impacts, or quick one-shot effects.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: true,
    textures: ["worlds/shared_assets/particles/dust1.png"],
    lifetime: [450, 900],
    fade: {in: 0.05, out: 0.4},
    velocity: {speed: [80, 200], angle: [0, 360]},
    alpha: [0.4, 0.8],
    scale: [0.3, 0.8]
    });
    gen.start();

    // Spawn a burst at {x,y}
    const p = {x: 1000, y: 800};
    gen.spawnParticles(120, {position: p});

    Use this to continuously spawn particles around something.

    const token = canvas.tokens.controlled[0];
    let acc = 0;
    const area = {radius: 70}; // relative to anchor, no x/y needed

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: true,
    anchor: token,
    anchorPoint: "center",
    area,
    elevation: token.document.elevation ?? 0,
    textures: [
    "worlds/shared_assets/particles/dust1.png",
    "worlds/shared_assets/particles/dust2.png",
    "worlds/shared_assets/particles/dust3.png",
    "worlds/shared_assets/particles/dust4.png"
    ],
    lifetime: [450, 900],
    fade: {in: 0.05, out: 0.4},
    velocity: {x: [-160, 160], y: [-160, 160]},
    rotationSpeed: 180,
    alpha: [0.5, 0.75],
    scale: [0.25, 0.75],
    blend: PIXI.BLEND_MODES.NORMAL,
    onTick: (dt, generator) => {
    const RATE = 90; // particles/sec
    acc += (dt * RATE) / 1000;
    const n = acc | 0;
    acc -= n;
    if ( n > 0 ) generator.spawnParticles(n, {area});
    }
    });
    gen.start();

    Good for shockwaves, sparks, or “pushing out” energy.

    const center = {x: 1200, y: 900};
    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: true,
    textures: ["worlds/shared_assets/particles/spark.png"],
    area: {x: center.x, y: center.y, radius: [80, 120]},
    lifetime: [500, 900],
    fade: {in: 0.1, out: 0.3},
    alpha: [0.4, 0.9],
    scale: [0.3, 0.7],
    onSpawn: (p) => {
    const cx = center.x - gen._bounds.x;
    const cy = center.y - gen._bounds.y;

    let dx = p.x - cx;
    let dy = p.y - cy;

    const d = Math.hypot(dx, dy) || 1;
    dx /= d;
    dy /= d;

    const speed = Math.mix(120, 240, Math.random());
    p.movementSpeed.x = dx * speed;
    p.movementSpeed.y = dy * speed;
    }
    });
    gen.start({spawn: 120});

    Use this for localized ambient effects in a region.

    const zone = new PIXI.Rectangle(900, 700, 600, 400);
    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: false,
    count: 250,
    area: zone,
    constraintMode: "wrap",
    constraintArea: zone,
    textures: ["worlds/shared_assets/particles/dust1.png"],
    lifetime: [1200, 2200],
    fade: {in: 0.2, out: 0.2},
    velocity: {x: [-20, 20], y: [-10, 10]},
    alpha: [0.1, 0.25],
    scale: [0.4, 0.9]
    });
    gen.start({spawn: 250});

    Great for “energy balls” in a box or magic motes in a bounded area.

    const zone = new PIXI.Rectangle(900, 700, 600, 400);
    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: false,
    count: 80,
    area: zone,
    constraintMode: "bounce",
    constraintArea: zone,
    restitution: 0.9,
    textures: ["worlds/shared_assets/particles/mote.png"],
    lifetime: [2000, 4000],
    fade: {in: 0.15, out: 0.25},
    velocity: {x: [-120, 120], y: [-120, 120]},
    alpha: [0.25, 0.6],
    scale: [0.5, 1.0],
    blend: PIXI.BLEND_MODES.SCREEN
    });
    gen.start({spawn: 80});

    Use this when you need a strict rectangle cutout.

    const clipRect = new PIXI.Rectangle(900, 700, 600, 400);
    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: false,
    count: 300,
    area: clipRect,
    clip: clipRect,
    textures: ["worlds/shared_assets/particles/fog.png"],
    lifetime: [2000, 4000],
    fade: {in: 0.2, out: 0.2},
    velocity: {x: [-10, 10], y: [-10, 10]},
    alpha: [0.08, 0.18],
    scale: [0.8, 1.6],
    blend: PIXI.BLEND_MODES.SCREEN
    });
    gen.start({spawn: 300});

    Use this for “sparkly” looks with layered variation.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: false,
    count: 150,
    area: {x: 1200, y: 900, radius: 250},
    textures: ["mote1.png", "mote2.png", "mote3.png"],
    blend: PIXI.BLEND_MODES.SCREEN,
    lifetime: [1500, 2600],
    fade: {in: 0.2, out: 0.35},
    velocity: {speed: [5, 20], angle: [0, 360]},
    rotationSpeed: 45,
    alpha: [0.15, 0.45],
    scale: [0.3, 0.8]
    });
    gen.start({spawn: 150});

    Useful when you want a rare “special” particle occasionally.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: true,
    textures: ["a.png", "b.png"]
    });
    gen.start();
    gen.spawnParticle({texture: "special.png", position: {x: 1200, y: 900}});

    Use this to reuse one generator for multiple nearby spawns without rebuilding it.

    const gen = new foundry.canvas.animation.ParticleGenerator({
    mode: "effect",
    manual: true,
    textures: ["dust1.png"],
    lifetime: [500, 900]
    });

    // Start the generator
    gen.start();

    const r1 = new PIXI.Rectangle(900, 700, 200, 200);
    const r2 = new PIXI.Rectangle(1200, 700, 200, 200);
    gen.spawnParticles(50, {area: r1});
    gen.spawnParticles(50, {area: r2});

    // Stop the generator (soft stop by default)
    gen.stop()
    Index

    Constructors

    Properties

    adjustedMaxParticles: number = 0

    The computed target particle count based on visible area (ambient mode) or the configured budget (effect mode).

    alphaRange: { max: number; min: number }

    The maximum alpha range for particles.

    An optional anchor used to attach areas and behaviors.

    anchorOffset: { x: number; y: number } | null

    A fixed offset (scene pixels) applied to the anchor.

    Which point to use when anchoring.

    blendMode: BLEND_MODES

    The blend mode used to render particles.

    blurOptions:
        | { enabled: boolean; intensity: number; quality: number
        | undefined }
        | null

    Optional blur filter options applied to the internal container.

    clip: { enabled: boolean | null; rect: Rectangle | null }

    Clip (mask) options.

    constraints: {
        area: Rectangle | "world" | "view" | "budget" | null;
        mode: ParticleGeneratorConstraintMode;
        restitution: number;
    }

    Out-of-bounds constraint configuration.

    container: Container<DisplayObject>

    The parent container which receives the internal particle container.

    drift: { enabled: boolean; intensity: number }

    Optional random drift configuration.

    elevation: number

    The elevation for the particle container.

    fadeInDuration: number

    The fade-in duration in milliseconds, or a fraction of lifetime if 0 < value < 1.

    fadeOutDuration: number

    The fade-out duration in milliseconds, or a fraction of lifetime if 0 < value < 1.

    Follow behavior options.

    initialBatch: number

    The initial proportion (0..1) of the computed target particle count to spawn on start.

    manualSpawning: boolean

    If true, particles are never spawned automatically.

    maxParticles: number

    The target particle count.

    maxParticlesPerFrame: number

    The maximum number of particles that may be spawned per frame (auto-spawn mode).

    The runtime mode.

    onDeath: Function | null

    An optional callback called when a particle is recycled.

    onSpawn: Function | null

    An optional callback called after the particle has been placed and configured.

    onTick: Function | null

    An optional callback called one time per frame (not per particle!).

    Orbit behavior options.

    particleLifetime: number | number[] | { max: number; min: number }

    The particle lifetime configuration in milliseconds.

    particlePool: ParticleMesh[] = []

    A pool of recycled particles ready to be reused.

    particles: ParticleMesh[] = []

    The currently active particle instances.

    positionTest: Function | null

    An optional spawn validator.

    rotationSpeed: number

    The rotation speed of particles in radians per second.

    scaleRange: { max: number; min: number }

    The scale range for particles.

    sort: number

    The sorting key for the particle container.

    spawnProbability: number

    The chance (0..1) that a spawn attempt actually creates a particle.

    textures: Texture<Resource>[]

    The configured particle textures.

    The velocity configuration used to generate per-particle movement.

    viewport: {
        newlyVisible: boolean;
        padding: number;
        randomizeAgeInPadding: boolean;
    }

    Viewport-related behavior (used primarily in ambient mode).

    _anchorLocal: Point = ...

    The current anchor position in local coordinates.

    _anchorScene: Point = ...

    The current anchor position in scene coordinates.

    _behavior: ParticleGeneratorBehavior | null = null

    The active behavior implementation.

    _behaviorContext: object

    A cached context object passed to behavior hooks.

    _blurFilter: Filter | null = null

    The blur filter applied to the internal container, if any.

    _bounds: Rectangle

    Generator bounds in scene coordinates.

    _brLocal: Point = ...

    Temp point used to avoid per-frame allocations.

    _brScreen: Point = ...

    Temp point used to avoid per-frame allocations.

    _budgetRectLocal: Rectangle = ...

    The local-space viewport rectangle with padding.

    _constraintRect: Rectangle | null = null

    Optional custom constraint rectangle in local coordinates.

    _debug: ParticleGeneratorDebugOptions | null = null

    Normalized debug options.

    _debugProfile: boolean = false

    Whether profiling is enabled.

    _debugStats: ParticleGeneratorDebugStats | null = null

    Debug statistics and profiling output. Null when debug stats are disabled.

    _debugTint: { mode: ParticleGeneratorDebugTintMode; palette: number[] } | null = null

    Cached debug tint options.

    _debugTintByTexture: WeakMap<Texture<Resource>, number> | null = null

    A mapping from textures to deterministic tint values.

    _generateMovementSpeed: Function

    A function which generates per-particle movement speed vectors.

    _hasOldBudgetRectLocal: boolean = false

    Whether the previous-frame budget rectangle is initialized.

    _initialized: boolean = false

    Whether the generator has spawned its initial batch.

    _mask: Graphics | null = null

    The mask graphics used to clip rendering.

    _newlyVisibleAreaPool: Rectangle[] = ...

    A fixed pool of rectangles used to describe newly visible areas.

    _newlyVisibleAreas: Rectangle[] = []

    The list of newly visible areas for the current frame in local coordinates.

    _oldBudgetRectLocal: Rectangle = ...

    The previous-frame budget rectangle.

    _particlesContainer: PrimaryCanvasParticleContainer | null = null

    The internal container which holds all particles.

    _spawnArea: ParticleGeneratorArea | null = null

    The configured default spawn area (effect mode). This area is defined in scene coordinates and may be interpreted relative to an anchor.

    _stopped: boolean = true

    Whether the generator is soft-stopped.

    _tickerAttached: boolean = false

    Whether the update callback is attached to the ticker.

    _tlLocal: Point = ...

    Temp point used to avoid per-frame allocations.

    _tlScreen: Point = ...

    Temp point used to avoid per-frame allocations.

    _viewRectLocal: Rectangle = ...

    The local-space viewport rectangle without padding.

    _worldRectLocal: Rectangle = ...

    The local-space generator bounds.

    DEFAULT_OPTIONS: ParticleGeneratorConfiguration = ...

    Default generator config.

    Accessors

    • get budgetRectLocal(): Rectangle

      The current padded viewport rectangle used for budget/spawning in ambient mode.

      Returns Rectangle

    • get viewRectLocal(): Rectangle

      The current unpadded viewport rectangle in the generator's local space.

      Returns Rectangle

    Methods

    • Spawn the initial batch.

      Returns void

    • Initialize/refresh base particle properties.

      Parameters

      Returns void

    • Spawn a single particle. In "ambient" mode, the default spawn area is the current padded viewport rectangle. In "effect" mode, the default spawn area is the configured ParticleGeneratorArea.

      Parameters

      • Optionaloptions: {
            area?: ParticleGeneratorArea | null;
            position?: Point | { x: number; y: number } | null;
            texture?: string | Texture<Resource>;
        } = {}
        • Optionalarea?: ParticleGeneratorArea | null

          An optional spawn area override. Interpreted the same as the configured area (scene coordinates, or relative-to-anchor when anchored).

        • Optionalposition?: Point | { x: number; y: number } | null

          An optional explicit spawn position (scene coordinates).

        • Optionaltexture?: string | Texture<Resource>

          A texture (or texture source string) to force for this particle.

      Returns ParticleMesh | null

    • Spawn multiple particles.

      Parameters

      • count: number

        The number of particles to spawn.

      • Optionaloptions: {
            area?: ParticleGeneratorArea | null;
            position?: IPointData | null;
            texture?: string | Texture<Resource>;
        } = {}
        • Optionalarea?: ParticleGeneratorArea | null

          An optional spawn area override. Interpreted the same as the configured area (scene coordinates, or relative-to-anchor when anchored).

        • Optionalposition?: IPointData | null

          An optional explicit spawn position (scene coordinates).

        • Optionaltexture?: string | Texture<Resource>

          A texture (or texture source string) to force for this burst.

      Returns number

      The number of successfully spawned particles.

    • Start the generator, create the update loop and optionally spawn an initial batch.

      Parameters

      • Optionaloptions: { spawn?: number }
        • Optionalspawn?: number

          Spawn this many particles immediately after starting. If ParticleGenerator#manualSpawning is false, this is capped to the remaining budget (target - active).

      Returns void

    • Stop the generator.

      Parameters

      • Optionaloptions: { hard?: boolean } = {}
        • Optionalhard?: boolean

          If true, detach the update loop and destroy internal resources. If false, stop spawning and let existing particles expire naturally.

      Returns void

    • Protected

      Apply a random drift vector to a particle.

      Parameters

      Returns void

    • Protected

      Spawn particles to move toward the current target count.

      Returns void

    • Protected

      Compute the current viewport rectangles and target particle count. All rectangles are in the local coordinate space of the internal container.

      Returns void

    • Protected

      Compute the portions of newRect that were not visible in oldRect. This method reuses a fixed pool of rectangles to avoid per-frame allocations.

      Parameters

      • oldRect: Rectangle
      • newRect: Rectangle

      Returns void

    • Protected

      Configure optional debug helpers. This feature set is fully opt-in and is designed to have near-zero overhead when disabled.

      Parameters

      Returns void

    • Protected

      Create a new particle instance.

      Parameters

      • texture: Texture<Resource>

      Returns ParticleMesh

    • Protected

      Get default bounds from the current scene dimensions.

      Returns Rectangle

    • Protected

      Get a random texture from the configured set.

      Returns Texture<Resource> | null

    • Protected

      Ticker callback.

      Returns void

    • Protected

      Recycle a particle to the pool.

      Parameters

      Returns void

    • Protected

      Update all active particles.

      Parameters

      • dt: number

        Delta time in milliseconds.

      Returns void

    • Protected

      Update particles without constraints.

      Parameters

      • dt: number

        Delta time in milliseconds.

      • ds: number

        Delta time in seconds.

      • particles: object[]
      • behavior: ParticleGeneratorBehavior | null
      • bctx: object | null

      Returns void