iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Particles

A particle system spawns short-lived sprites with velocity, acceleration, and a lifetime — perfect for fire, smoke, dust, blood, spell effects. The trick is pooling: never allocate per particle in the hot path.

A pooled particle system

EXAMPLE
// One emitter, hundreds of particles, zero per-frame allocation
class Particle {
    constructor() {
        this.x = 0; this.y = 0;
        this.vx = 0; this.vy = 0;
        this.life = 0; this.maxLife = 0;
        this.size = 0; this.colour = '#fff';
        this.alive = false;
    }
}

class ParticleSystem {
    constructor(capacity = 1000) {
        this.pool = Array.from({ length: capacity }, () => new Particle());
        this.next = 0;
    }

    emit({ x, y, vx, vy, life, size, colour }) {
        const p = this.pool[this.next];
        this.next = (this.next + 1) % this.pool.length;

        p.x = x; p.y = y;
        p.vx = vx; p.vy = vy;
        p.life = life; p.maxLife = life;
        p.size = size; p.colour = colour;
        p.alive = true;
    }

    update(dt, gravity = 800) {
        for (const p of this.pool) {
            if (!p.alive) continue;
            p.vy += gravity * dt;
            p.x  += p.vx * dt;
            p.y  += p.vy * dt;
            p.life -= dt;
            if (p.life <= 0) p.alive = false;
        }
    }

    render(ctx) {
        for (const p of this.pool) {
            if (!p.alive) continue;
            const alpha = p.life / p.maxLife;
            ctx.globalAlpha = alpha;
            ctx.fillStyle = p.colour;
            ctx.fillRect(p.x - p.size / 2, p.y - p.size / 2, p.size, p.size);
        }
        ctx.globalAlpha = 1;
    }
}

// Emit on demand
function explode(particles, x, y) {
    for (let i = 0; i < 60; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = 200 + Math.random() * 300;
        particles.emit({
            x, y,
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed,
            life: 0.6 + Math.random() * 0.4,
            size: 2 + Math.random() * 4,
            colour: ['#ff5151', '#ffd23f', '#fff'][Math.floor(Math.random() * 3)],
        });
    }
}

Why it matters

Pool particles. Allocating a new {x, y, vx, vy} per spawn looks innocent and is the #1 reason 60fps drops to 30 when explosions happen. Same trick applies to bullets and enemies.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Pool short-lived sprites with velocity + lifetime to fake fire, dust, explosions.
class Particle {
    constructor(x, y, vx, vy, life) { Object.assign(this, { x, y, vx, vy, life }); }
    update(dt) { this.x += this.vx*dt; this.y += this.vy*dt; this.life -= dt; }
}
Try it Yourself »

Discussion

Loading…