Chapter 4. Particle Systems

“That is wise. Were I to invoke logic, however, logic clearly dictates that the needs of the many outweigh the needs of the few.”

— Spock

In 1982, William T. Reeves, a researcher at Lucasfilm Ltd., was working on the film Star Trek II: The Wrath of Khan. Much of the movie revolves around the Genesis Device, a torpedo that when shot at a barren, lifeless planet has the ability to reorganize matter and create a habitable world for colonization. During the sequence, a wall of fire ripples over the planet while it is being “terraformed.” The term particle system, an incredibly common and useful technique in computer graphics, was coined in the creation of this particular effect.

“A particle system is a collection of many many minute particles that together represent a fuzzy object. Over a period of time, particles are generated into a system, move and change from within the system, and die from the system.”

—William Reeves, "Particle Systems—A Technique for Modeling a Class of Fuzzy Objects," ACM Transactions on Graphics 2:2 (April 1983), 92.

Since the early 1980s, particle systems have been used in countless video games, animations, digital art pieces, and installations to model various irregular types of natural phenomena, such as fire, smoke, waterfalls, fog, grass, bubbles, and so on.

This chapter is dedicated to looking at implementation strategies for coding a particle system. How do you organize your code? Where do you store information related to individual particles versus information related to the system as a whole? The examples I’ll cover will focus on managing the data associated with a particle system. They’ll use simple dots for the particles and apply only the most basic behaviors (such as gravity). However, by building on this framework and adding more creative ways to render the particles and compute behaviors, you can achieve a variety of effects.

4.1 Why You Need Particle Systems

I’ve defined a particle system to be a collection of independent objects, often represented by a simple shape or dot. Why does this matter? Certainly, the prospect of modeling some of the phenomena listed (waterfalls!) is attractive and potentially useful. But really, there’s an even better reason to explore particle systems. If you want to get anywhere in this nature of code life, you’re likely to find yourself developing systems of many things–balls bouncing, birds flocking, ecosystems evolving, all sorts of things in plural.

Just about every chapter after this one is going to deal with a list of objects. Yes, I’ve dipped my toe in the array waters in some of the first vector and forces examples. But now it‘s time to go where no array has gone before.

First, I’m going to want to deal with flexible quantities of elements. Some examples will have zero things, sometimes one thing, sometimes ten things, and sometimes ten thousand things. Second, I’m going to want to take a more sophisticated object-oriented approach. Instead of writing a class to describe a single particle, I’m also going to want to write a class that describes the collection of particles—the particle system itself. The goal here is to be able to write a sketch that looks like the following:

// Ah, isn’t this main program so simple and lovely?
let system;

function setup() {
  createCanvas(640, 360);
  system = new ParticleSystem();
}

function draw() {
  background(255);
  system.run();
}

No single particle is referenced in the above code, yet the result will be full of particles flying all over the canvas. Getting used to writing sketches with multiple classes, and classes that keep lists of instances of other classes, will prove very useful as you get to later chapters in this book.

Finally, working with particle systems is also a good excuse to tackle two other object-oriented programming techniques: inheritance and polymorphism. With the examples you’ve seen up until now, I’ve always used an array of a single type of object, like "movers" or “oscillators.” With inheritance (and polymorphism), I’ll demonstrate a convenient way to store a single list containing objects of different types. This way, a particle system need not only be a system of a one kind of particle.

Though it may seem obvious to you, I’d also like to point out that my examples are modeled after conventional implementations of particle systems, and that’s where I will begin in this chapter. However, the fact that the particles in this chapter look or behave a certain way should not limit your imagination. Just because particle systems tend to look sparkly, fly forward, and fall with gravity doesn’t mean that those are the characteristics yours should have.

The focus here is on how to keep track of a system of many elements. What those elements do and how those elements look is up to you.

4.2 A Single Particle

Before I can get rolling on coding the system itself, I need to write the class to describe a single particle. The good news: I’ve done this already! The Mover class from Chapter 2 serves as the perfect template. A particle is an independent body that moves about the canvas. It has position, velocity, and acceleration, a constructor to initialize those variables, and functions to display() itself and update() its position.

class Particle {
  //{!5} A “Particle” object is just another name for our “Mover.” It has position, velocity, and acceleration.
  Particle(x, y) {
    this.position = createVector(x, y);
    this.acceleration = createVector();
    this.velocity = createVector();
  }

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
  }

  show() {
    stroke(0);
    fill(175);
    circle(this.position.x, this.position.y, 8);
  }
}

This is about as simple as a particle can get. From here, I could take the particle in several directions. I could add the applyForce() function to affect the particle’s behavior (I’ll do precisely this in a future example). I could also add variables to describe color and shape, or load a p5.Image to draw the particle. For now, however, I’ll focus on adding just one additional detail: lifespan.

Some particle systems involve something called an emitter. The emitter is the source of the particles and controls the initial settings for the particles: position, velocity, and more. An emitter might emit a single burst of particles, or a continuous stream of particles, or both. The new feature here is that a particle born at the emitter does not live forever. If it were to live forever, the p5.js sketch would eventually grind to a halt as the amount of particles increases to an unwieldy number over time. As new particles are born, old particles need to be removed. This creates the illusion of an infinite stream of particles, and the performance of the sketch does not suffer. There are many different ways to decide when a particle is ready to be removed. For example, it could come into contact with another object, or it could leave the canvas. For this first Particle class, I’ll choose to add a lifespan variable that acts like a countdown timer. The timer will start at 255 and count down to 0, when the particle will be considered “dead.” The code for this in the Particle class as:

class Particle {

  constructor(x, y) {
    this.position = createVector(x, y);
    this.acceleration = createVector();
    this.velocity = createVector();
    //{!1 .bold} A new variable to keep track of how long the particle has been “alive”. We start at 255 and count down for convenience.
    this.lifespan = 255;
  }

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    //{!1 .bold}  Lifespan decreases
    this.lifespan -= 2.0;
  }

  show() {
    //{!2 .bold}  Since the life ranges from 255 to 0 it can be used also for alpha
    stroke(0, this.lifespan);
    fill(175, this.lifespan);
    circle(this.position.x, this.position.y, 8);
  }
}

The reason I chose to start the lifespan at 255 and count down to 0 is for convenience. With those values, I can assign lifespan as the alpha transparency for the circle as well. When the particle is “dead” it will also have faded away.

With the addition of the lifespan property, I’ll need one additional function—a function that can be queried (for a true or false answer) as to whether the particle is alive or dead. This will come in handy when writing the a class to manage the list of particles themselves. Writing this function is pretty easy. I just need to check whether the value of lifespan is less than 0. If it is, return true; otherwise, return false.

  isDead() {
    //{!5} Is the particle still alive?
    if (this.lifespan < 0.0) {
      return true;
    } else {
      return false;
    }
  }

Or more simply, I can return the result of the boolean expression itself!

    isDead() {
      //{!1} Is the particle still alive?
      return (this.lifespan < 0.0);
    }

Before I get to the next step of making many particles, it’s worth taking a moment to make sure the particle works correctly and create a sketch with one single Particle object. Here is the full code below, with one small addition–giving the particle a random initial velocity as well as adding applyForce() to simulate gravity.

Example 4.1: A single particle

let particle;

function setup() {
  createCanvas(640, 360);
  particle = new Particle(width / 2, 10);
}

function draw() {
  background(255);
  //{!2} Operating the single Particle
  particle.update();
  particle.show();

  //{!2} Applying a gravity force
  let gravity = createVector(0, 0.1);
  particle.applyForce(gravity);

  //{!4} Checking the particle's state and making a new particle
  if (p.isDead()) {
    particle = new Particle(width / 2, 20);
    console.log("Particle dead!");
  }
}

class Particle {

  constructor(x,y) {
    this.position =  createVector(x, y);
    //{!1 .offset-top} For demonstration purposes the Particle has a random velocity.
    this.velocity = createVector(random(-1, 1), random(-2, 0));
    this.acceleration = createVector(0, 0);
    this.lifespan = 255.0;
  }

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    this.lifespan -= 2.0;
    this.acceleration.mult(0);
  }

  applyForce(force) {
    this.acceleration.add(force);
  }

  show() {
    stroke(0, this.lifespan);
    fill(0, this.lifespan);
    circle(this.position.x, this.position.y, 10);
  }

  //{!3} Keeping the same physics model as with previous chapters
  applyForce(force) {
    this.acceleration.add(force);
  }

  //{!3} Is the Particle alive or dead?
  isDead() {
    return (this.lifespan < 0.0);
  }
}

Exercise 4.1

Create a run() function in the Particle class that handles update(), show(), and applyForce(). What are the pros and cons of this approach?

Exercise 4.2

Add angular velocity (rotation) to the particle. Create your own non-circle particle design.

Now that I have a class to describe a single particle, it’s time for the next big step. How do you keep track of many particles, not knowing exactly how many particles you might have at any given time?

4.3 The Array

Thankfully, the wonderful JavaScript Array has all the functionality we need for managing a list of Particle objects. The the built-in JavaScript functions available in the JavaScript class Array allow for adding and removing particles and manipulating the arrays in all sorts of powerful ways. Although there are some cons to this approach, in order to keep the subsequent code examples more concise, I'm using a solution to Exercise 4.1 and assume a run() method that handles all of the particle's functionality. JavaScript Array Documentation.

let total = 10;
//{!1} Starting with an empty array
let particles = [];

function setup() {
  //{!3} This is what you’re probably used to, accessing elements on the array via an index and brackets—[].
  for (let i = 0; i < total; i++) {
    particles[i] = new Particle(width/2, height/2);
  }
}

function draw() {
  for (let i = 0; i < particles.length; i++) {
    let particle = particles[i];
    particle.run();
  }
}

This last for loop demonstrates how to call functions on every element of an array by accessing each index. I initialize a variable i with value 0 and count up by 1, accessing each element of the array until I reach the end. However, this is a good time to mention the JavaScript for of loop, which is a bit more concise. The for of loop works with arrays as follows:

function draw() {
  for (let particle of particles) {
    particle.run();
  }
}

Let’s translate that. Say “each” instead of “let” and “in” instead of “of”:

“For each particle in particles, update and display that particle!”

I know. You cannot contain your excitement. I can’t. I know it’s not necessary, but I just have to type that again.

for (let particle of particles) {
  particle.run();
}

Simple, elegant, concise, lovely. Take a moment. Breathe. I have some bad news. Yes, I may love that for of loop and I will get to use it in examples. But not just yet.

The code I’ve written above doesn’t take advantage of the JavaScript's ability to remove elements from an array. I need to build an example that fits with the particle system scenario, where a continuous stream of particles are emitted, adding one new particle with each cycle through draw(). I’ll skip rehashing the Particle class code here, as it doesn’t need to change. What I have so far is:

let particles = [];

function setup() {
  createCanvas(640, 360);
}

function draw() {
  background(255);
  //{!1 .offset-top} A new Particle object is added to the array every cycle through draw().
  particles.push(new Particle(width / 2, 50));

  for (let particle of particles) {
    particle.run();
  }
}

Run the above code for a few minutes and you’ll start to see the frame rate slow down further and further until the program grinds to a halt (my tests yielded horrific performance after fifteen minutes). The issue of course is that I am adding more and more particles without removing any.

Fortunately, particles can be removed from the array referencing the index position of the particle to be removed. This is why I cannot use the enhanced for of loop; this loop provides no means for deleting elements while iterating. Instead, I can use the Array splice() method. (Yes, an array in JavaScript is actually an object created from the class Array with many methods!) The splice() method removes one or more elements from an array starting from a given index.

  for (let i = 0; i < particles.length; i++) {
    let particle = particles[i];
    particle.run();
    if (particle.isDead()) {
      //{!1} Remove one particle at index i
      particles.splice(i, 1);
    }
  }

Although the above code will run just fine (and the program will never grind to a halt), I have opened up a medium-sized can of worms. Whenever you manipulate the contents of an array while iterating through that very array, you can get into trouble. Take, for example, the following code.

  for (let i = 0; i < particles.length; i++) {
    let particle = particles[i];
    particle.run();
    //{!1 .offset-top} Adding a new Particle to the list while iterating?
    particles.push(new Particle(width / 2, 50));
  }

This is a somewhat extreme example (with flawed logic), but it proves the point. In the above case, for each particle in the list, I add a new particle to the list (and the length of the array increases). This will result in an infinite loop, as I can never increment past the size of the array!

While removing elements from the array during a loop doesn’t cause the sketch to crash (as it would with adding), the problem is perhaps more insidious in that it leaves no evidence. To discover the flaw I must first establish an important fact. When an element is removed from the array with splice(), all subsequent elements are shifted to the left. Figure 4.1 shows what happens when particle C (index 2) is removed. Particles A and B keep the same index, while particles D and E shift from 3 and 4 to 2 and 3, respectively.

Figure 4.1: When an element is removed from an array, the elements shift to the left fill the empty spot.
Figure 4.1: When an element is removed from an array, the elements shift to the left fill the empty spot.

Let’s consider a counter i iterating over the elements of the array.

Notice the problem? Particle D was never checked! When C was deleted from slot #2, D moved into slot #2, but i has already moved on to slot #3. This is not a total disaster, since particle D will get checked the next time around through draw(). Still, the expectation is that the code should iterate through every single element of the array. Skipping one is unacceptable!

There are two solutions to this problem. The first solution is to iterate through the array backwards. If you are sliding elements from right to left as elements are removed, it’s impossible to skip an element. Here’s how the code looks:

  //{!1 .bold} Looping through the list backwards
  for (let i = particles.length - 1; i >= 0; i--) {
    let particle = particles[i];
    particle.run();
    if (particle.isDead()) {
      particles.splice(i, 1);
    }
  }

A second solution is to use something known as a “higher-order” function. A higher-order function is one that receives another function as an argument (or returns a function). In the case of JavaScript arrays there are many higher-order functions. A common one is sort() which takes as its argument a function that defines how to compare two elements of the array and then sorts the array according to that comparison. With the array of particles, I can make use of the higher order function filter(). filter() checks each item in the specified array and keeps only the item(s) where the given condition is true (removing those that return false).

  particles = particles.filter(function(particle) {
    //{!1} Keep particles that are not dead!
    return !particle.isDead();
  });

This is more commonly written using JavaScript's arrow notation. (To learn more, you can watch this higher-order functions and arrow notation video tutorial.)

  particles = particles.filter(particle => !particle.isDead());

For the purposes of this book, I am going to stick with the splice() method, but I encourage you to explore writing your code with higher-order functions and arrow notation.

Example 4.2: Array of particles

let particles = [];

function setup() {
  createCanvas(640, 240);
}

function draw() {
  background(255);

  particles.push(new Particle(width / 2, 50);

  //{!1} Looping through the array backwards for deletion
  for (let i = particles.length - 1; i >= 0; i--) {
    let particle = particles[i];
    particle.run();
    if (particle.isDead()) {
      particles.splice(i, 1);
    }
  }
}

4.4 A Particle Emitter

OK. Now I’ve done two things. I’ve written a class to describe an individual Particle object. I’ve conquered the array and used it to manage a list of many particles (with the ability to add and delete at will).

I could stop here. However, one additional step I can and should take is to write a class to describe the list of Particle objects itself. I’m going to call this classEmitter, named for its functionality of “emitting” particles. The Emitter class will allow me to remove the bulky logic of looping through all particles from draw(), as well as open up the possibility of having multiple particle emitters.

If you recall the goal I set at the beginning of this chapter was to write code like:

//{!1} Just one Particle Emitter!
let emitter;

function setup() {
  createCanvas(640, 360);
  system = new Emitter();
}

function draw() {
  background(255);
  emitter.run();
}

Let’s take the code from Example 4.2 and review a bit of object-oriented programming, looking at how each piece setup() and draw() can fit into the Emitter class.

Array in setup() and draw()

let particles = [];

function setup() {
  createVector(640, 360);
}

function draw() {
  background(255);

  particles.push(new Particle());

  for (let i = particles.length - 1; i >= 0; i--) {
    let particles = particles[i];
    particle.run();
    if (particle.isDead()) {
      particles.splice(i, 1);
    }
  }
}

Array in the Emitter class

class Emitter {

  constructor() {
    this.particles = [];
  }

  addParticle() {
    this.particles.push(new Particle());
  }

  run() {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      let particle = this.particles[i];
      particle.run();
      if (particle.isDead()) {
        this.particles.splice(i, 1);
      }
    }
  }
}

I could also add new features to the particle system itself. For example, it might be useful for the Emitter class to keep track of an origin point where particles are born. The origin point could be initialized in the constructor.

Example 4.3: Single Particle Emitter

class Emitter {

  Emitter(x, y) {
    //{!1 .bold} This particular ParticleSystem implementation includes an origin point where each Particle begins.
    this.origin = createVector(x, y);
    this.particles = [];
  }

  addParticle() {
    //{!1 .bold}  The origin is passed to each Particle when it is added.
    this.particles.add(new Particle(origin.x, origin.y));
  }

Exercise 4.3

What if the emitter moves? Can you emit particles from the mouse position or use the concepts of velocity and acceleration to move the system autonomously.

Exercise 4.4

Building off Chapter 3’s “Asteroids” example, use a particle system to emit particles from the ship’s “thrusters” whenever a thrust force is applied. The particles’ initial velocity should be related to the ship’s current direction.

4.5 A System of Emitters

Let’s take a moment to recap what I’ve covered so far. I described an individual Particle object. I also described a system of Particle objects, and this we call a “particle system” and organized the code into an Emitter class. I’ve defined a particle system as a collection of independent objects. But isn’t a particle system itself an object? If that’s the case (which it is), there’s no reason why I couldn’t also build a collection of many particle emitters, i.e. a system of systems.

This line of thinking could take you even further, and you might lock yourself in a basement for days sketching out a diagram of a system of systems of systems of systems of systems of systems. Of systems. After all, I could create a description of the world in this way. An organ is a system of cells, a human body is a system of organs, a neighborhood is a system of human bodies, a city is a system of neighborhoods, and so on and so forth. While this is an interesting road to travel down, it’s a bit beyond where I’d like to be right now. It is, however, quite useful to know how to write a sketch that keeps track of many particle systems, each of which keep track of many particles. Take the following scenario.

You start with a blank screen. 

Click the mouse to create new emitters

You click the mouse and generate a particle system at the mouse’s position.

Click the mouse to create new emitters

Each time you click the mouse, a new particle system is created at the mouse’s position.

Click the mouse to create new emitters

In Example 4.3, I stored a single reference to an Emitter object in the variable emitter.

let emitter;

function setup() {
  createCanvas(640, 240);
  emitter = new ParticleSystem(width/2, 50);
}

function draw() {
  background(255);
  emitter.addParticle();  
  emitter.run();
}

For this new example, what I want to do instead is create an Array to keep track of multiple instances of emitters themselves. When the sketch begins, i.e. in setup(), the Array is empty.

Example 4.4: System of Systems

Click the mouse to create new emitters
//{!1} This time, the type of thing we are putting in the array is a particle emitter itself!
let emitter = [];

function setup() {
  createCanvas(640, 240);
}

Whenever the mouse is pressed, a new Emitter object is created and placed into the Array.

function mousePressed() {
  emitters.push(new Emitter(mouseX, mouseY));
}

And in draw(), instead of referencing a single Emitter object, I can now iterate over all the emitters and call run() on each of them.

function draw() {
  background(255);
  //{!4} Since we aren’t deleting elements, we can use the for of loop!
  for (let emitter of emitters) {
    emitter.run();
    emitter.addParticle();
  }
}

Exercise 4.5

Rewrite Example 4.4 so that each particle system doesn’t live forever. When a particle system is empty (i.e. has no particles left), remove it from the array emitters.

Exercise 4.6

Create a simulation of an object shattering into many pieces. How can you turn one large shape into many small particles? Can you create several large shapes on the screen that each shatter when clicked on?

4.6 Inheritance and Polymorphism: An Introduction

You may have encountered the terms inheritance and polymorphism in your programming life before this book. After all, they are two of the three fundamental principles behind the theory of object-oriented programming (the other being encapsulation). If you’ve read other programming books, chances are it’s been covered. My beginner text, Learning Processing, has close to an entire chapter (#22) dedicated to these two topics.

Still, perhaps you’ve only learned about it in the abstract sense and never had a reason to really use inheritance and polymorphism. If this is true, you’ve come to the right place. Without these concepts, your ability to program a variety of particles and particle systems is extremely limited. (In the chapter 6, I’ll also demonstrate how understanding these topics will help you use physics libraries.)

Imagine the following. It’s a Saturday morning, you’ve just gone out for a lovely jog, had a delicious bowl of cereal, and are sitting quietly at your computer with a cup of warm chamomile tea. It’s your old friend So and So’s birthday and you’ve decided you’d like to make a greeting card with p5. How about some confetti for a birthday? Purple confetti, pink confetti, star-shaped confetti, square confetti, fast confetti, fluttery confetti, etc. All of these pieces of confetti with different appearances and different behaviors explode onto the screen at once.

What you’ve got is clearly a particle system—a collection of individual pieces of confetti (i.e. particles). You might be able to cleverly design a Particle class to have variables that store color, shape, behavior, and more. To create a variety of particles, you might initialize those variables with random values. But what if your particles are drastically different? This could become very messy, having all sorts of code for different ways of being a particle in the same class. Another option might be to do the following:

class HappyConfetti {

}

class FunConfetti {

}

class WackyConfetti {

}

This is a nice solution: create three different classes to describe the different kinds of pieces of confetti that are part of your particle system. An Emitter constructor could then have some code to pick randomly from the three classes when filling the array. (Note that this probabilistic method is the same one I employed in the random walk examples in the Introduction.)

class Emitter {
  constructor(num) {
    this.particles = [];

    for (let i = 0; i < num; i++) {
      let r = random(1);
      //{!7} Randomly picking a "kind" of particle
      if (r < 0.33) {
        this.particles.add(new HappyConfetti());
      } else if (r < 0.67) {
        this.particles.add(new FunConfetti());
      } else {
        this.particles.add(new WackyConfetti());
      }
    }
  }

OK, I need to pause for a moment. You’ve done nothing wrong. All you wanted to do was wish your friend a happy birthday and enjoy writing some code. But while the reasoning behind the above approach is quite sound, there’s a problem.

Aren’t you going to be copying/pasting a lot of code between the different “confetti” classes?

Yes. Even though the kinds of particles are different enough to merit breaking them out into separate classes, there is still a ton of code that they will likely share. They’ll all have vectors to keep track of position, velocity, and acceleration; an update() function that implements the motion algorithm; and more.

This is where inheritance comes in. Inheritance allows you to write a class that inherits variables and functions from another class, all the while implementing its own custom features.

Let’s look at this concept in more detail and then create a particle system example that implements inheritance.

4.7 Inheritance Basics

Let’s take a different example, the world of animals: dogs, cats, monkeys, pandas, wombats, and sea nettles. I’ll start by coding a Dog class. A Dog object will have an age variable (an integer), as well as eat(), sleep(), and bark() functions.

class Dog {

  constructor() {
    this.age = 0;
  }

  eat() {
    print("Yum!");
  }

  sleep() {
    print("Zzzzzz");
  }

  bark() {
    print("WOOF");
  }
}

Now, let’s move on to cats.

class Cat {

  // Dogs and cats have the same variables (age) and functions (eat, sleep).
  constructor() {
    this.age = 0;
  }

  eat() {
    print("Yum!");
  }

  sleep() {
    print("Zzzzzz");
  }

  //{!3} No bark(), instead a unique function for meowing.
  meow() {
    print("MEOW!");
  }
}

As I move on to rewriting the same code for fish, horses, koalas, and lemurs, this process will become rather tedious. A better solution is to develop a generic Animal class that can describe any type of animal. All animals eat and sleep, after all. I could then say:

Inheritance makes this all possible. With inheritance, classes can inherit properties (variables) and functionality (methods) from other classes. A Dog class is a child (subclass) of an Animal class. Children will automatically inherit all variables and functions from the parent (superclass), but can also include functions and variables not found in the parent. Like a phylogenetic "tree of life," inheritance follows a tree structure. Dogs inherit from mammals, which inherit from animals, and so on.

Figure 4.2
Figure 4.2

Here is how the syntax works with inheritance.

//{!1} The Animal class is the parent (or super) class.
class Animal {

  constructor() {
    //{!1} Dog and Cat will inherit the variable age.
    this.age = 0;
  }

  //{!7} Dog and Cat will inherit the functions eat() and sleep().
  eat() {
    print("Yum!");
  }

  sleep() {
    print("Zzzzzz");
  }
}

//{!1 .bold} The Dog class is the child (or sub) class, indicated by the code "extends Animal".
class Dog extends Animal {
  constructor() {
    //{!1 .bold} super() executes code found in the parent class.
    super();
  }
  //{!3} bark() is defined in the child class, since it isn't part of the parent class.
  bark() {
    print("WOOF!");
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }
  meow() {
    print("MEOW!");
  }
}

This brings up two new terms:

A subclass can be expanded to include additional functions and properties beyond what is contained in the superclass. For example, let’s assume that a Dog object has a “hair color” variable in addition to age. The class would now look like this:

class Dog extends Animal {

  constructor() {
    super();
    //{!1} A child class can introduce new variables not included in the parent.
    this.haircolor = color(210, 105, 30);
  }

  bark() {
    print("WOOF!");
  }
}

Note how the parent constructor is called via super(), which sets the age to 0, but haircolor is set inside the Dog constructor itself. If a Dog object eats differently than a generic Animal object, parent functions can be overridden by rewriting the function inside the subclass.

class Dog extends Animal {

  constructor() {
     super();
     this.haircolor = color(random(255));
  }

  // A child can override a parent function if necessary.
  eat() {
    //{!1} A Dog's specific eating characteristics
    print("Woof! Woof! Slurp.")
  }

  bark() {
    print("WOOF!");
  }
}

But what if a dog eats the same way as a generic animal, just with some extra functionality? A subclass can both run the code from a parent class and incorporate custom code.

class Dog extends Animal {

   constructor() {
     super();
     this.haircolor = color(random(255));
   }

   eat() {
     //{!1 .bold} Call eat() from Animal. A child can execute a function from the parent while adding its own code.
     super.eat();
     //{!1 .bold} Add some additional code for a Dog's specific eating characteristics.
     print("Woof!!!");
   }

   bark() {
    print("WOOF!");
  }
}

4.8 Particles with Inheritance

Now that I’ve covered the theory of inheritance and its syntax, it’s time write a working example in p5.js based on the Particle class.

Let’s review a basic Particle implementation, adapted from Example 4.1:

class Particle {

  Particle(x, y) {
    this.acceleration = createVector(0, 0);
    this.velocity = createVector(random(-1, 1), random(-2, 0));
    this.position = createVector(x, y);
  }

  run() {
    let gravity = createVector(0, 0.05);
    this.applyForce(gravity);
    this.update();
    this.display();
  }

  update() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
  }

  show() {
    fill(0);
    circle(this.position.x, this.position.y, 8);
  }
}

Next, I‘ll create a subclass that extends Particle (I’ll call it Confetti). It will inherit all the instance variables and methods from Particle. I‘ll also include a constructor and execute the code from the parent class with super().

class Confetti extends Particle {

  constructor(x, y) {
    super(x, y);
    // I could add variables for only Confetti here.
  }

  //{inline} There is no code here because update() is inherited from the parent.

  //{!6} Override the show method.
  show() {
    rectMode(CENTER);
    fill(0);
    square(this.position.x, this.position.y, 8);
  }
}

Let’s make this a bit more sophisticated. Let’s say I want to have the Confetti particle rotate as it flies through the air. One option is to model angular velocity and acceleration as described in Chapter 3. For ease, however, I’ll implement something less formal.

I know a particle has an x position somewhere between 0 and the width of the canvas. What if I said: when the particle’s x position is 0, its rotation should be 0; when its x position is equal to the width, its rotation should be equal to 2\pi? Does this ring a bell? Whenever a value has one range that you want to map to another range, you can use the map() function (see the Introduction!)

let angle = map(this.position.x, 0, width, 0, TWO_PI);

And just to give it a bit more spin, I can actually map the angle’s range from 0 to TWO_PI * 2. Here’s how this code fits into the show() function.

  show() {
    let angle = map(this.position.x, 0, width, 0, TWO_PI * 2);

    rectMode(CENTER);
    fill(0, this.lifespan);
    stroke(0, this.lifespan);
    //{!6} To rotate() a shape in p5, transformations are necessary. For more, visit: http://p5.org/learning/transform2d/
    push();
    translate(this.position.x, this.position.y);
    rotate(angle);
    rectMode(CENTER);
    square(0, 0, 8);
    pop();
  }

Exercise 4.7

Instead of using map() to calculate theta, try modeling angular velocity and acceleration?

Now that I have a Confetti subclass that extends the base Particle class, the next step is to also add Confetti objects to the array of particles defined in the Emitter class.

4.10 Particle Systems with Inheritance

Example 4.5: Particle system with inheritance

class Emitter {

  constructor(x, y) {
    this.origin = createVector(x, y);
    //{!1 .bold} One list, for anything that is a Particle or extends Particle
    this.particles = [];
  }

  addParticle() {
    let r = random(1);
    //{!5 .bold .code-wide} A 50% chance of adding each kind of particle.
    if (r < 0.5) {
      this.particles.add(new Particle(this.origin));
    } else {
      this.particles.add(new Confetti(this.origin));
    }
  }

  run() {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      let p = this.particles[i];
      p.run();
      if (p.isDead()) {
        this.particles.splice(i, 1);
      }
    }
  }
}

Exercise 4.8

Create a particle system with more than two “kinds” of particles. Try varying the behavior of the particles in addition to the design.

4.11 Particle Systems with Forces

So far in this chapter, I’ve focused on structuring code in an object-oriented way to manage a collection of particles. While I did keep theapplyForce() function, I took a couple shortcuts to keep things simple. Let’s now add back in a mass property changing the constructor and applyForce() function. (The rest of the class stays the same)

class Particle {

  constructor(x, y) {
    //{!1} Now start with acceleration of 0,0.
    this.position = createVector(x, y);
    this.velocity = createVector(random(-1, 1),random(-2, 0));
    this.acceleration = createVector(0, 0);
    this.lifespan = 255.0;
    //{!1} Add a mass property. Trying varying mass for different interesting results!
    this.mass = 1;
  }

  applyForce(force) {
    // {!2} Dividing force by mass
    const f = force.copy();
    f.div(this.mass);
    this.acceleration.add(f);
}

Now that the Particle class is complete, I have a very important question to ask. Where should I call the applyForce() function? Where in the code is it appropriate to apply a force to a particle? In my view there’s no right or wrong answer; it really depends on the exact functionality and goals of a particular p5.js sketch. My “quick and dirty” solution in the previous examples was to just create a gravity force in the run() function itself of each particle.

  run() {
    // {!2} Create a hard-coded vector and apply as a force
    let gravity = createVector(0, 0.05);
    this.applyForce(gravity);
    this.update();
    this.show();
  }

I’d like to consider a more generic solution that would likely apply to more broadly and allow different forces to be applied to individual particles in a system. For example. what if I were to apply a force globally every time through draw() to all particles.

function draw() {
  background(100);
	
	//{inline} Apply a force to all particles?
	
  emitter.addParticle();
  emitter.run();
}

Well, it seems there‘s a small problem. applyForce() is a method written inside the Particle class, but there is no reference to the individual particles themselves, only the Emitter object emitter. Since I want all particles to receive the force, however, I can pass the force to the emitter which is responsible for managing all the individual particles.

function draw() {
  background(255);

  let gravity = createVector(0, 0.1);
  //{!1} Applying a force to the emitter
  emitter.applyForce(gravity);
  emitter.addParticle();
  emitter.run();
}

Of course, if I call a new function on the Emitter object in draw(), I then have to define that function in the Emitter class. Let’s describe the job that function needs to perform: receive a force as a p5.Vector and apply that force to all the particles.

Now in code:

  applyForce(force) {
    for (let particle of this.particles) {
      particle.applyForce(force);
    }
  }

It almost seems silly to write this function. What the code says is “apply a force to a particle system so that the system can apply that force to all of the individual particles.” Nevertheless, it’s actually quite reasonable. After all, the emitter is in charge of managing the particles, so if you want to talk to the particles, you’ve got to talk to them through their manager. (Also, here’s a chance for the enhanced loop since no particles are being deleted!)

Here is the full example (assuming the existence of the Particle class written above; no need to include it again since nothing has changed):

Example 4.6: Particle system with forces

let emitter;

function setup() {
  createCanvas(640, 240);
  emitter = new ParticleSystem(createVector(width / 2, 50));
}

function draw() {
  background(255);

  //{!2 .bold} Apply a force to all particles.
  let gravity = createVector0, 0.1);
  emitter.applyForce(gravity);

  emitter.addParticle();
  emitter.run();
}


class Emitter {

  constructor(x, y) {
    this.origin = createVector(x, y);
    this.particles = [];
  }

  addParticle() {
    this.particles.push(new Particle(this.origin.x, this.origin.y));
  }

  applyForce(force) {
    //{!3} Using a for of loop to apply the force to all particles
    for (let particle of this.particles) {
      particle.applyForce(force);
    }
  }

  run() {
    //{!7} Can’t use the enhanced loop because checking for particles to delete.
    for (let i = this.particles.length - 1; i >= 0; i--) {
      const particle = this.particles[i];
      particle.run();
      if (particle.isDead()) {
        this.particles.splice(i, 1);
      }
    }
  }
}

4.12 Particle Systems with Repellers

What if I wanted to take this example one step further and add a Repeller object—the inverse of the Attractor object covered in Chapter 2 that pushes any particles away that get close? This requires a bit more sophistication because, unlike the gravity force, each force an attractor or repeller exerts on a particle must be calculated for each particle.

Figure 4.3: Gravity force—vectors are all identical 
Figure 4.4: Attractor force—vectors are all different 
Figure 4.3: Gravity force—vectors are all identical  Figure 4.4: Attractor force—vectors are all different 

Let’s start by examining how I might incorporate a new Repeller object into a particle system example. I’m going to need two major additions to the code:

  1. A Repeller object (declared, initialized, and displayed).
  2. A function that passes the Repeller object into the particle emitter so that it can apply a force to each particle object.
let emitter;
//{!1 .bold} New thing: we declare a Repeller object.
let repeller;

function setup() {
  createCanvas(640, 240);
  emitter = new ParticleSystem(createVector(width / 2, 50));
  //{!1 .bold} New thing: we initialize a Repeller object.
  repeller = new Repeller(width / 2 - 20, height / 2);
}

function draw() {
  background(255);
  emitter.addParticle();

  let gravity = createVector(0, 0.1);
  emitter.applyForce(gravity);

  //{!1 .bold} New thing: a function to apply a force from a repeller.
  emitter.applyRepeller(repeller);

  emitter.run();
  //{!1 .bold} New thing: display the Repeller object.
  repeller.show();
}

Making a Repeller object is quite easy; it’s a duplicate of the Attractor class from Chapter 2, Example 2.6.

class Repeller {

  constructor(x, y)  {
    //{!1 .bold} A Repeller doesn’t move, so you just need position.
    this.position = createVector(x, y);
    this.r = 10;
  }

  show() {
    stroke(0);
    fill(127);
    circle(this.position.x, this.position.y, this.r * 2);
  }
}

The more difficult question is, how do I write the applyRepeller() function? Instead of passing a p5.Vector into a function like with applyForce(), I need to instead pass a Repeller object into applyRepeller() and ask that function to do the work of calculating the force between the repeller and all particles. Let’s look at both of these functions side by side.

applyForce(force)

applyForce(force) {
  for (let particle of this.particles) {
    particle.applyForce(force);
  }
}

applyRepeller(repeller)

applyRepeller(repeller) {
  for (let particle of this.particles) {
    let force = repeller.repel(particle);
    particle.applyForce(force);
  }
}

The functions are almost identical. There are only two differences. One I mentioned before—a Repeller object is the argument, not a p5.Vector. The second difference is the more important one. I must calculate a custom p5.Vector force for each and every particle and apply that force. How is that force calculated? In a function called repel(), the inverse of the attract() function from the Attractor class.

  // All the same steps to calculate an attractive force, only pointing in the opposite direction.
  repel(particle) {
    // 1) Get force direction.
    let force = p5.Vector.sub(this.position, particle.position);
    //{!2} 2) Get distance (constrain distance).
    let distance = force.mag();
    distance = constrain(distance, 5, 50);
    // 3) Calculate magnitude, use a "power" variable for G
    let strength = -1 * this.power / (distance * distance);
    // 4) Make a vector out of direction and magnitude.
    force.setMag(strength);
    return force;
  }

Notice how throughout this entire process of adding a repeller to the environment, I never once considered editing the Particle class itself. A particle doesn’t actually have to know anything about the details of its environment; it simply needs to manage its position, velocity, and acceleration, as well as have the ability to receive an external force and act on it.

Now look at this example in its entirety, again leaving out the Particle class, which hasn’t changed.

Example 4.7: ParticleSystem with repeller

// One ParticleSystem
let emitter;
//{!1} One repeller
let repeller;

function setup() {
  createCanvas(640, 240);
  emitter = new ParticleSystem(width / 2, 40);
  repeller = new Repeller(width / 2, 200);
}

function draw() {
  background(100);
  emitter.addParticle();
  // We’re applying a universal gravity.
  let gravity = createVector(0, 0.1);
  emitter.applyForce(gravity);
  //{!1} Applying the repeller
  emitter.applyRepeller(repeller);
  emitter.run();

  repeller.show();
}

//{!1} The Emitter manages all the particles.
class Emitter {

  constructor(x, y) {
    this.origin = createVector(x, y);
    this.particles = [];
  }

  addParticle() {
    this.particles.push(new Particle(this.origin.x, this.origin.y));
  }

  applyForce(force) {
    //{!3} Applying a force as a p5.Vector
    for (let particle of this.particles) {
      particle.applyForce(force);
    }
  }

  applyRepeller(repeller) {
    //{!4} Calculating a force for each Particle based on a Repeller
    for (let particle of this.particles) {
      let force = repeller.repel(particle);
      particle.applyForce(force);
    }
  }

  run() {
    for (let i = this.particles.length - 1; i >= 0; i--) {
      const particle = this.particles[i];
      particle.run();
      if (particle.isDead()) {
        this.particles.splice(i, 1);
      }
    }
  }
}

class Repeller {
  constructor(x, y) {    
    this.position = createVector(x, y);
    //{!1} How strong is the repeller?
    this.power = 150;
  }

  show() {
    stroke(0);
    strokeWeight(2);
    fill(127);
    circle(this.position.x, this.position.y, 32);
  }

  repel(particle) {
    //{!6 .code-wide} This is the same repel algorith from Chapter 2: forces based on gravitational attraction.
    let force = p5.Vector.sub(this.position, particle.position);
    let distance = force.mag();
    distance = constrain(distance, 5, 50);
    let strength = -1 * this.power / (distance * distance);
    force.setMag(strength);
    return force;
  }
}

Exercise 4.9

Expand the above example to include more than one repeller (using an array).

Exercise 4.10

Create a particle system in which each particle responds to every other particle. (Note that I’ll be going through this in detail in Chapter 6.)

4.13 Image Textures and Additive Blending

Even though this book is almost exclusively focused on behaviors and algorithms rather than computer graphics and design, I don’t think I would be able to live with myself if I finished a discussion of particle systems and never once looked at an example that involves texturing each particle with an image. The way you a particle is rendered is a key piece of the puzzle in designing certain types of visual effects.

Consider a smoke simulation. Take a look at the following two images:

White circles 
Fuzzy images with transparency 
White circles  Fuzzy images with transparency 

Both of these images were generated from identical algorithms. The only difference is that a white circle is drawn in image A for each particle and a “fuzzy” blob is drawn for each in B.

Figure 4.5 Two image textures, one all white circle, one fuzzy circle that fades out towards the edges.
Figure 4.5 Two image textures, one all white circle, one fuzzy circle that fades out towards the edges.

The good news here is that you get a lot of bang for very little buck. Before you write any code, however, you’ve got to make your image texture! I recommend using PNG format, as p5.js will retain the alpha channel (i.e. transparency) when drawing the image, which is needed for blending the texture as particles layer on top of each other. Once you’ve made a PNG and deposited it in your sketch’s “data” folder, you are on your way with just a few lines of code.

Example 4.8: Image texture particle system

First, we’ll need to a variable to store the image and load it in preload().

let img;

Load the image in preload().

function preload() {
  //{!1} Loading the PNG
  img = loadImage("texture.png");
}

And when it comes time to draw the particle, use the image variable instead of drawing an ellipse or rectangle.

  show() {
    imageMode(CENTER);
    //{!1} Note how tint() is the image equivalent of shape’s fill().
    tint(255, this.lifespan);
    image(img, this.position.x, this.position.y);
  }

Incidentally, this smoke example is a nice excuse to revisit the Gaussian distributions from the Introduction. To make the smoke appear a bit more realistic, instead of launching allow the particles in a purely random direction initial velocity vectors mostly around a mean value (with a lower probability of outliers) can produce an effect that appears less fountain-like and more like smoke (or fire).

Using randomGaussian() velocities can be initialized as follows:

    let vx = randomGaussian(0, 0.3);
    let vy = randomGaussian(-1, 0.3);
    let vel = createVector(vx, vy);

Finally, in this example, a wind force is applied to the smoke mapped from the mouse’s horizontal position.

function draw() {
  background(0);

  //{!2} Wind force direction based on mouseX.
  let dx = map(mouseX, 0, width, -0.2, 0.2);
  let wind = createVector(dx, 0);
  emitter.applyForce(wind);
  emitter.run();
  //{!3} Two particles are added each cycle through draw().
  for (let i = 0; i < 2; i++) {
    emitter.addParticle();
  }
}

Exercise 4.11

Try creating your own textures for different types of effects. Can you make it look like fire, instead of smoke?

Exercise 4.12

Use an array of images and assign each Particle object a different image. Even though single images are drawn by multiple particles, make sure you don’t call loadImage() any more than you need to (once for each image file!)

Finally, it’s worth noting that there are many different algorithms for blending colors in computer graphics. These are often referred to as “blend modes.” By default, when you draw something on top of something else in p5, you only see the top layer—this is the default “blend” behavior. When pixels have alpha transparency (as they do in the smoke example), p5.js uses an alpha compositing algorithm that combines a percentage of the background pixels with the new foreground pixels based on those alpha values themselves.

However, it’s possible to draw using other blend modes, and a much loved blend mode for particle systems is “additive.” Additive blending in Processing with particle systems was pioneered by Robert Hodgin in his famous particle system and forces exploration, Magnetosphere, which later became the iTunes visualizer. For more see: Magnetosphere.

Additive blending is a simple blend algorithm that involves adding the pixel values of one layer to another, capping all values at 255. This results in a space-age glow effect, as the colors get brighter and brighter the more layers that add together.

Example 4.9: Additive blending

function setup() {
  createCanvas(640, 240);
}

Then, before you go to draw anything, you set the blend mode using blendMode():

function draw() {
  //{!1} Additive blending
  blendMode(ADD);

  //{!1} Also need to clear() since the background is added and does not cover what was previously drawn.
  clear();

  // Also note that the “glowing” effect of additive
  // blending will not work with a white
  // (or very bright) background.
  background(0);

  // {inline} All other particle stuff goes here.

}

Exercise 4.13

Use tint() in combination with additive blending to create a rainbow effect.

Exercise 4.14

Try blending with other modes, such as SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, or MULTIPLY.

The Ecosystem Project

Step 4 Exercise:

Take your creature from Step 3 and build a system of creatures. How can they interact with each other? Can you use inheritance and polymorphism to create a variety of creatures, derived from the same code base? Develop a methodology for how they compete for resources (for example, food). Can you track a creature’s “health” much like a particle’s lifespan, removing creatures when appropriate? What rules can you incorporate to control how creatures are born into the system?

(Also, you might consider using a particle system itself in the design of a creature. What happens if an emitter is tied to the creature’s position?)