Shiffman, Chapter 4

Particle Systems

One particle is a dot. A thousand particles are fire, smoke, sparks, and magic.

Prerequisites: Chapters 1-3. Vectors, forces, and a dash of trig.
10
Chapters
2
Simulations
10
Quizzes

Chapter 0: Why Particles?

In 1982, researcher William T. Reeves at Lucasfilm coined the term "particle system" while working on Star Trek II: The Wrath of Khan. The Genesis Device's wall of fire rippling over a planet was too complex for traditional geometry. So Reeves created it from thousands of tiny, short-lived particles — each one simple, but together forming something organic and alive.

The core idea: A particle system is a collection of many small, simple objects (particles) that together represent something fuzzy, organic, or chaotic: fire, smoke, waterfalls, sparks, dust, bubbles, fireworks. Each particle is born, moves, ages, and dies.

This chapter is as much about code organization as it is about physics. How do we manage hundreds or thousands of objects? How do we create them, track them, and remove them when they die? The answers involve arrays, object-oriented design, and a pattern called the emitter.

Individual particleParticle system
Has location, velocity, accelerationContains many particles in an array
Has a lifespan (fades and dies)Continuously creates new particles
Responds to forcesApplies forces to all particles
Drawn as a small shapeCollectively forms fire, smoke, etc.
What is a particle system?

Chapter 1: A Single Particle

Before we build a system, let's build one particle. It's just a Mover from Chapters 1-2, but with one addition: a lifespan.

pseudocode
class Particle:
  location     # vector
  velocity     # vector (random initial direction)
  acceleration # vector
  lifespan     # starts at 255, decreases each frame

The particle is born at some location with a random velocity (perhaps slightly upward with some horizontal spread). Each frame, it follows the standard motion algorithm: acceleration changes velocity, velocity changes location. And each frame, its lifespan ticks down.

pseudocode
function update():
  velocity = velocity + acceleration
  location = location + velocity
  lifespan = lifespan - 2

function isDead():
  return lifespan < 0
Lifespan as alpha: A neat trick — use the lifespan value directly as the alpha (opacity) of the particle's fill color. At lifespan 255, the particle is fully opaque. As it ages, it fades. When it hits 0, it's invisible and can be removed. This gives a natural fade-out effect for free.
What property does a Particle have that a basic Mover doesn't?

Chapter 2: Lifespan

The lifespan is what makes particle systems work. Without it, particles would accumulate forever and our simulation would grind to a halt (or fill the entire screen with dots).

The typical lifecycle of a particle:

Birth
Created at emitter location, lifespan = 255
Life
Moves, responds to forces, lifespan decreases
Death
Lifespan ≤ 0, removed from array

This birth-life-death cycle creates a steady state: new particles are born at the same rate old ones die. The system stays visually consistent without growing unbounded.

Varying lifespan: You can make particles die at different rates by randomizing the initial lifespan or the rate of decrease. Fire particles might live 30 frames (fast burn). Smoke particles might live 200 frames (slow drift). This creates layered effects where fire sparks briefly and smoke lingers.
Why is lifespan essential for particle systems?

Chapter 3: The Array

A particle system needs a dynamic array (ArrayList in Java, Array in JavaScript) to store its particles. Why dynamic? Because particles are constantly being added and removed.

Each frame, the loop looks like this:

pseudocode
# Add new particle(s)
particles.push(new Particle(emitterX, emitterY))

# Update and display all
for p in particles:
  p.update()
  p.display()

# Remove dead ones (iterate backwards!)
for i = particles.length - 1 down to 0:
  if particles[i].isDead():
    particles.splice(i, 1)
Why iterate backwards? When you remove an element from an array, all subsequent elements shift down. If you iterate forward, you'll skip the element that moved into the removed slot. Iterating backward avoids this problem because removals only affect indices you've already processed.

Alternatively, in JavaScript you can use filter: particles = particles.filter(p => !p.isDead()). This creates a new array each frame, which is clean but slightly less efficient.

When removing dead particles from an array, why should you iterate backwards?

Chapter 4: The Emitter

An emitter (or particle emitter) is the source of particles. It knows where to create them and manages the entire lifecycle. The emitter is the "system" in particle system.

pseudocode
class ParticleSystem:
  origin       # where particles are born
  particles[]  # the array of active particles

  function addParticle():
    particles.push(new Particle(origin))

  function run():
    for each p in particles:
      p.applyForce(gravity)
      p.update()
      p.display()
    removeDeadParticles()
Multiple emitters: You can have many emitters, each spawning different particle types. One emitter at a candle wick for flame. Another below it for smoke. Another behind a rocket for exhaust. Each emitter is an independent system with its own origin and particle properties.

The beauty of this design is separation of concerns: the main program just calls system.addParticle() and system.run(). The system handles all the messy array management internally.

What is the role of a particle emitter?

Chapter 5: Forces on Particles

Particles are just Movers, so they can receive forces via applyForce(). The most common force is gravity — particles fall downward. But you can apply anything: wind, attraction to a point, repulsion.

pseudocode
gravity = (0, 0.05)
wind    = (0.01, 0)

function run():
  for each p in particles:
    p.applyForce(gravity)
    p.applyForce(wind)
    p.update()
    p.display()
Per-particle variation: You can also apply forces that depend on the particle's state. A repeller object at a fixed location might push particles away. Compute the force from the repeller to each particle (like inverse-square gravity, but repulsive) and watch particles stream around it. This creates beautiful flow patterns.

Forces make particle systems feel physical. Without gravity, particles just drift randomly. With gravity, they arc and fall. With wind, they drift sideways. With drag, they slow over time. Layer these for realism.

How do you apply forces to a particle system?

Chapter 6: Inheritance

What if you want different particle types? Some round, some square, some with trails? Object-oriented inheritance (or polymorphism) lets us define a base Particle class and create variations.

pseudocode
class Particle:
  # base class with location, velocity, lifespan...
  function display(): # draw a circle

class Confetti extends Particle:
  function display(): # draw a spinning rectangle

class Spark extends Particle:
  function display(): # draw a short line in velocity direction
Polymorphism in action: The ParticleSystem stores all particle types in a single array. When it calls p.display(), each particle draws itself according to its own type. The system doesn't need to know what kind of particle it's dealing with. This is the power of polymorphism — one interface, many behaviors.

In JavaScript, you can achieve this without formal classes by simply giving each particle a different display function. But the pattern is the same: the emitter creates varied particles, and each draws itself.

What OOP concept lets a particle system hold different particle types in one array?

Chapter 7: Fireworks!

A firework is a particle system of particle systems. Here's how it works:

Launch
A single "rocket" particle shoots upward
Explode
When the rocket slows (or after a timer), it dies and spawns a burst of particles
Fall & Fade
Explosion particles scatter outward, then fall under gravity, fading as they die
pseudocode
class Firework:
  rocket     # a single particle going up
  explosion  # an array of particles (empty until rocket dies)
  exploded   # boolean

  function update():
    if not exploded:
      rocket.applyForce(gravity)
      rocket.update()
      if rocket.velocity.y >= 0:  # apex reached
        explode()
    else:
      for each p in explosion:
        p.applyForce(gravity)
        p.update()
      removeDeadParticles()
The explosion: When the rocket dies, create 50-100 particles at the rocket's location, each with a random velocity pointing outward in all directions (random angle, random speed). Add gravity so they arc downward. Each explosion particle fades with its lifespan. The result: a gorgeous burst.
When does the firework's rocket "explode"?

Chapter 8: Particle Playground

Click or tap anywhere to launch a firework from that position. Watch the rocket rise, explode, and the sparks scatter under gravity. Each firework uses a random hue.

Firework Particle System

Click/tap to launch fireworks. Each rocket rises, explodes at its peak, and sparks fall under gravity. Multiple fireworks can be active at once.

Layer effects: Real firework shows layer multiple systems: bright flash sparks, trailing tails, smoke clouds. Each is its own particle system with different particle types, lifespans, and forces. What you see here is the simplest version — one burst per rocket — but the architecture supports arbitrary complexity.
A firework is best described as what kind of structure?

Chapter 9: Summary

ConceptKey Idea
ParticleA Mover with a lifespan that fades and dies
LifespanCounts down each frame; doubles as alpha for fading
Dynamic arrayParticles are added and removed every frame
EmitterThe source and manager of all particles
ForcesApplied to each particle individually (gravity, wind, repellers)
PolymorphismDifferent particle types in one array, each drawing itself
FireworksNested systems: rocket particle + explosion particle system
The pattern: Create. Update. Display. Remove dead. Repeat. This simple loop, combined with forces and randomness, produces effects that look organic and alive.

What we covered:

• Single particle with lifespan
• Dynamic arrays for particle management
• The emitter pattern
• Applying forces to particles
• Inheritance and polymorphism
• Firework system-of-systems

What comes next:

Chapter 5: Physics Libraries. We've built physics from scratch. Now we'll see how libraries like Box2D handle rigid bodies, collisions, joints, and constraints — the heavy lifting of physics simulation.

What is the main loop of a particle system?