One particle is a dot. A thousand particles are fire, smoke, sparks, and magic.
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.
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 particle | Particle system |
|---|---|
| Has location, velocity, acceleration | Contains many particles in an array |
| Has a lifespan (fades and dies) | Continuously creates new particles |
| Responds to forces | Applies forces to all particles |
| Drawn as a small shape | Collectively forms fire, smoke, etc. |
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
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:
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.
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)
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.
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()
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.
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()
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.
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
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.
A firework is a particle system of particle systems. Here's how it works:
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()
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.
Click/tap to launch fireworks. Each rocket rises, explodes at its peak, and sparks fall under gravity. Multiple fireworks can be active at once.
| Concept | Key Idea |
|---|---|
| Particle | A Mover with a lifespan that fades and dies |
| Lifespan | Counts down each frame; doubles as alpha for fading |
| Dynamic array | Particles are added and removed every frame |
| Emitter | The source and manager of all particles |
| Forces | Applied to each particle individually (gravity, wind, repellers) |
| Polymorphism | Different particle types in one array, each drawing itself |
| Fireworks | Nested systems: rocket particle + explosion particle system |
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.