Chapter 2. Forces

“Don’t underestimate the Force.”

— Darth Vader

In the final example of Chapter 1, I demonstrated how to calculate a dynamic acceleration based on a vector pointing from a circle on the canvas to the mouse position. The resulting motion resembled a magnetic attraction between shape and mouse, as if some force was pulling the circle in towards the mouse. In this chapter I will detail the concept of a force and its relationship to acceleration. The goal, by the end of this chapter, is to build a simple physics engine and understand how objects move around a canvas responding to a variety of environmental forces.

A physics engine is defined as a computer program (or code library) that simulates the behavior of objects in a physical environment. In our case, the objects are two-dimensional shapes and the environment is a rectangular canvas. Physics engines can be developed to be highly precise (requiring high performance computing) or real-time (using simple and fast algorithms).

2.1 Forces and Newton’s Laws of Motion

Let’s begin by taking a conceptual look at what it means to be a force in the real world. Just like the word “vector,” the term “force” can have a variety of meanings. It can indicate a powerful physical intensity, as in “They pushed the boulder with great force” or a powerful influence, as in “They are a force to be reckoned with!” The definition of force that I am interested in for this chapter about is more formal and comes from Sir Isaac Newton’s three laws of motion:

A force is a vector that causes an object with mass to accelerate.

Hopefully you recognize the first part of the definition: a force is a vector. Thank goodness you just spent a whole chapter learning what a vector is and how to program with vectors! We’ll start from there, and build up an understanding of the rest of the definition as we go.

Let’s define Newton’s three laws of motion in relation to the concept of a force.

Newton’s First Law

Newton’s first law is commonly stated as:

An object at rest stays at rest, and an object in motion stays in motion.

However, this is missing an important element related to forces. I could expand the definition by stating:

An object at rest stays at rest, and an object in motion stays in motion at a constant speed and direction unless acted upon by an unbalanced force.

When Newton came along, the prevailing theory of motion—formulated by Aristotle—was nearly two thousand years old. It stated that if an object is moving, some sort of force is required to keep it moving. Unless that moving thing is being pushed or pulled, it will slow down or stop. This theory was borne out through observation of the world. For example, if you toss a ball, it falls to the ground and eventually stops moving, seemingly because the force of the toss is no longer being applied.

This older theory, of course, is not true. As Newton established, in the absence of any forces, no force is required to keep an object moving. When an object (such as the aforementioned ball) is tossed in the earth’s atmosphere, its velocity changes because of unseen forces such as air resistance and gravity. An object’s velocity will only remain constant in the absence of any forces or if the forces that act on it cancel each other out, meaning the net force adds up to zero. This is often referred to as equilibrium (see Figure 2.1). The falling ball will reach a terminal velocity (that stays constant) once the force of air resistance equals the force of gravity.

Figure 2.1: The toy mouse doesn't move because all the forces cancel each other out (add up to a net force of zero). 
Figure 2.1: The toy mouse doesn't move because all the forces cancel each other out (add up to a net force of zero). 

Considering a p5.js canvas, I could restate Newton’s first law as follows:

An object’s velocity vector will remain constant if it is in a state of equilibrium.

In other words, in a Mover class, the update() function should not apply any mathematical operations on the velocity vector unless there is a non-zero net force present!

Newton’s Third Law

Skipping Newton’s second law (arguably the most important law for the purposes of this book) for a moment, let’s move on to the third law. This law is often stated as:

For every action there is an equal and opposite reaction.

This law frequently causes confusion in the way that it is stated. For one, it sounds like one force causes another. Yes, if you push someone, that someone may actively decide to push you back. But this is not the action and reaction referred to in Newton’s third law.

Let’s say you push against a wall. The wall doesn’t actively decide to push back on you. There is no “origin” force. Your push simply includes both forces, referred to as an “action/reaction pair.”

A better way of stating the law might be:

Forces always occur in pairs. The two forces are of equal strength, but in opposite directions.

This still causes confusion because it sounds like these forces would always cancel each other out. This is not the case. Remember, the forces act on different objects. And just because the two forces are equal, it doesn’t mean that the objects’ movements are equal (or that the objects will stop moving).

Consider pushing on a stationary truck. Although the truck is far more powerful than you, unlike a moving one, a stationary truck will never overpower you and send you flying backwards. The force you exert on it is equal and opposite to the force exerted on your hands. The outcome depends on a variety of other factors. If the truck is a small truck pointed down an icy hill, you’ll probably be able to get it to move. On the other hand, if it’s a very large truck on a dirt road and you push hard enough (maybe even take a running start), you could injure your hand.

And what if, as in Figure 2.2, you are wearing roller skates when you push on that truck?

Figure 2.2: Demonstrating Newton’s third law of motion by pushing a heavy truck wearing roller skates.
Figure 2.2: Demonstrating Newton’s third law of motion by pushing a heavy truck wearing roller skates.

You’ll accelerate away from the truck, sliding along the road while the truck stays put. Why do you slide but not the truck? For one, the truck has a much larger mass (which I’ll get into with Newton’s second law). There are other forces at work too, namely the friction of the truck’s tires and your roller skates against the road.

Considering p5.js again, I could restate Newton’s third law as follows:

If you calculate a p5.Vector f that is a force of object A on object B, you must also apply the opposite force that B exerts on object A. You can calculate this other force as p5.Vector.mult(f, -1).

You’ll soon see that in the world of coding simulation, it’s often not necessary to stay true to Newton’s third law. Sometimes, such as in the case of gravitational attraction between bodies, I’ll want to model equal and opposite forces in my example code. Other times, such as a scenario where I‘ll say, “Hey, there’s some wind in the environment,” I’m not going to bother to model the force that a body exerts back on the air. In fact, I’m not going to bother modeling the air at all! Remember, the examples in this book are taking inspiration from the physics of the natural world for the purposes of creativity and interactivity and do not require perfect precision.

Newton’s Second Law

Now it‘s time for most important law for you, the p5.js coder, Newton’s second law, stated as:

Force equals mass times acceleration.

Or:

\vec{F} = M \times \vec{A}

Why is this the most important law for this book? Well, let’s write it a different way.

\vec{A} = \vec{F} / M

Acceleration is directly proportional to force and inversely proportional to mass. Consider what this means if you are pushed. The harder you’re pushed, the faster you’ll move (accelerate). On the other hand, the bigger you are, the slower you’ll accelerate.

Weight vs. Mass

Mass in not to be confused with weight. The mass of an object is a measure of the amount of matter in the object (measured in kilograms). An object that has a mass of 1 kilogram on earth would have a mass of 1 kilogram on the moon.

Weight, though often mistaken for mass, is technically the force of gravity on an object. From Newton’s second law, we can calculate weight as mass times the acceleration of gravity (w = m * g). Weight is measured in newtons. Because weight is tied to gravity, an object on the moon weighs one-sixth as much as it does on earth.

Related to mass is the concept of density, which is defined as the amount of mass per unit of volume (grams per cubic centimeter, for example).

In the world of p5.js, what is mass anyway? Aren’t we dealing with pixels? Let’s start simple and say that in a pretend pixel world, all objects have a mass equal to 1. F / 1 = F. And so:

\vec{A} = \vec{F}

The acceleration of an object is equal to force. This is great news. After all, in Chapter 1 I described acceleration as the key to controlling the movement of objects in a canvas. I said that position changes according to velocity, and velocity according to acceleration. Acceleration seemed to be where it all began. Now you can see that force is truly where it all begins.

Let’s take the Mover class, with position, velocity, and acceleration.

class Mover {
  constructor(){
    this.position = createVector();
    this.velocity = createVector();
    this.acceleration = createVector();
  }
}

Now the goal is to be able to add forces to this object, with code like:

mover.applyForce(wind);

or:

mover.applyForce(gravity);

where wind and gravity are p5.Vector objects. According to Newton’s second law, I could implement this function as follows.

applyForce(force) {
  //{!1} Newton's second law at its simplest.
  this.acceleration = force;
}

This looks pretty good. After all, acceleration = force is a literal translation of Newton’s second law (in a world without mass). Nevertheless, there’s a pretty big problem here which I’ll quickly encounter when I return to my original goal: creating an object that responds to wind and gravity forces. Consider this code:

mover.applyForce(wind);
mover.applyForce(gravity);
mover.update();

Imagine you’re the computer for a moment. First, you call applyForce() with wind, and so the Mover object’s acceleration is now assigned the vector wind. Second, you call applyForce() with gravity. Now the Mover object’s acceleration is set to the gravity vector. Finally, you call update(). What happens in update()? Acceleration is added to velocity.

this.velocity.add(this.acceleration);

If you run this code, you will not see an error in the console, but zoinks! There’s a major problem. What is the value of acceleration when it is added to velocity? It is equal to the gravity vector. Wind has been left out! Anytime applyForce() is called, acceleration is overwritten. How can I handle more than one force?

2.3 Force Accumulation (”Net” Force)

The answer is that the forces must accumulate, or be added together. This is actually stated in the full definition of Newton’s second law itself which I now to confess to having simplified. Here’s a more accurate way to put it:

Net force equals mass times acceleration.

In other words, acceleration is equal to the sum of all forces divided by mass. At any given moment, there might be 1, 2, 6, 12, or 303 forces acting on an object. As long as the object knows how to add them together (accumulate them), it doesn’t matter how many forces there are. The sum total will give you the object’s acceleration (again, ignoring mass). This makes perfect sense. After all, as you saw in Newton’s first law, if all the forces acting on an add up to zero, the object experiences an equilibrium state (that is, no acceleration).

I can now revise the applyForce() method to take force accumulation into account.

applyForce(force) {
  //{!1} Newton's second law, but with force accumulation, adding all input forces to acceleration.
  this.acceleration.add(force);
}

I’m not finished just yet, though. Force accumulation has one more piece. Since I’m adding all the forces together at any given moment, I have to make sure that I clear acceleration (set it to zero) before each time update() is called. Consider a wind force for a moment. Sometimes wind is very strong, sometimes it’s weak, and sometimes there’s no wind at all. For example, you might write code that creates a gust of wind when holding down the mouse.

if (mouseIsPressed) {
  let wind = createVector(0.5, 0);
  mover.applyForce(wind);
}

When the mouse is released, the wind should stop, and according to Newton’s first law, the object should continue moving at a constant velocity. However, if I forget to reset acceleration to zero, the gust of wind will still be in effect. Even worse, it will add onto itself from the previous frame! Acceleration, in a time-based physics simulation, has no memory; it is calculated based on the environmental forces present at any given moment (frame) in time. This is different than, say, position. An object must remember its previous location in order to move properly to the next.

One way to implement clearing the acceleration for each frame is to multiply the vector by 0 at the end of update().

update() {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
  // Clearing acceleration after it's been applied
  this.acceleration.mult(0);
}

This is also a good spot for me to note that in addition to assuming that all objects have a mass equal to 1, I am choosing to ignore yet another detail which is typically part of a physics engine. I am referring to the time step which can affect the accuracy and behavior of a simulation. Instead of incorporating the time step as a variable (often denoted as dt), I am choosing to to assume that every cycle through draw()represents a time step of one. While this assumption may not be the most accurate, it allows me to focus on the key principles of the simulation, and I will examine the impact of different time steps in greater detail in Chapter 6 when I cover other physics libraries.

Exercise 2.1

Using forces, simulate a helium-filled balloon floating upward and bouncing off the top of a window. Can you add a wind force that changes over time, perhaps according to Perlin noise?

2.4 Factoring in Mass

While smaller time steps is not something I plan to address here, I can and should expand on the concept of variable mass before integrating forces into the Mover class. After all, Newton’s second law is really \vec{F} = M \times \vec{A}, not \vec{F} = \vec{A}. Incorporating mass to start is as easy as adding a this.mass instance variable to the class, but I need to spend a little more time here because of another impending complication.

First I’ll add mass.

class Mover {
  constructor(){
    this.position = createVector();
    this.velocity = createVector();
    this.acceleration = createVector();
    //{!1} Adding mass as a number
    this.mass = ????;
  }
}

Units of Measurement

Now that I am introducing mass, it’s important to make a quick note about units of measurement. In the real world, things are measured in specific units. Two objects are 3 meters apart, the baseball is moving at a rate of 90 miles per hour, or this bowling ball has a mass of 6 kilograms. Sometimes you do want to take real-world units into consideration. However, in this chapter, I’m going to stick with units of measurement in pixels (“These two circles are 100 pixels apart”) and frames of animation (“This circle is moving at a rate of 2 pixels per frame”, the aforementioned time step).

In the case of mass, there isn’t any unit of measurement to use. How much mass is in any given pixel? You might enjoy inventing your own p5.js unit of mass to associate with those values, like “10 moogs” or “10 yurkles.”

For demonstration purposes, I’ll tie mass to pixels (the larger a circle’s diameter, the larger the mass). This will allow me to visualize the mass of an object, albeit inaccurately. In the real world, size does not indicate mass. A small metal ball could have a much higher mass than a large balloon due to its higher density. And for two circular objects with equal density, I’ll also note that mass should be tied to the formula for area of a circle: \pi r^2 (more about \pi and circles in Chapter 3!)

Mass is a scalar, not a vector, as it’s just one number describing the amount of matter in an object. I could get fancy and compute the area of a shape as its mass, but it’s simpler to begin by saying, “Hey, the mass of this object is…um, I dunno…how about 10?”

constructor() {
  this.position = createVector(random(width), random(height));
  this.velocity = createVector(0, 0);
  this.acceleration = createVector(0, 0);
  this.mass = 10;
}

This isn’t so great since things only become interesting once I have objects with varying mass, but it’s enough to get us started. Where does mass come in? I need to divide force by mass to apply Newton’s second law to the object.

applyForce(force) {
  //{!2} Newton's second law (with force accumulation and mass)
  force.div(mass);
  this.acceleration.add(force);
}

Yet again, even though the code looks quite reasonable, there is a major problem here. Consider the following scenario with two Mover objects, both being blown away by a wind force.

let m1 = new Mover();
let m2 = new Mover();

let wind = createVector(1, 0);

m1.applyForce(wind);
m2.applyForce(wind);

Again, imagine you’re the computer. Object m1 receives the wind force—(1,0)—divides it by mass (10), and adds it to acceleration.

m1 equals wind force (1,0)
Divided by mass of 10 (0.1,0)

Now you move on to object m2. It also receives the wind force—(1,0). Wait. Hold on a second. What is the value of the wind force? Taking a closer look, the wind force is actually now (0.1,0)! Remember that when you pass an object (in this case a p5.Vector) into a function, you are passing a reference to that object. It’s not a copy! So if a function makes a change to that object (which, in this case, it does by dividing the mass) then that object is permanently changed! But I don’t want m2 to receive a force divided by the mass of object m1. I want it to receive the force in its original state—(1,0). And so I must protect the original vector and make a copy of it before dividing by mass. Fortunately, the p5.Vector class has a convenient method for making a copy. copy() returns a new p5.Vector object with the same data. And so I can revise applyForce() as follows:

applyForce(force) {
  //{!1} Making a copy of the vector before using it!
  let f = force.copy();
  //{!1} Divide the copy by mass!
  f.div(this.mass);
  this.acceleration.add(f);
}

Let’s take a moment to recap what I‘ve covered so far. I've defined what a force is (a vector), and how to apply a force to an object (divide it by mass and add it to the object’s acceleration vector). What is missing? Well, I have yet to figure out how to calculate a force in the first place. Where do forces come from?

Exercise 2.2

There’s another way you could write the applyForce() function, using the static method div() instead of copy(). Rewrite applyForce() using the static method. For help with this exercise, review static methods in Chapter 1.

applyForce(force) {
  const f = _______._______(_______,_______);
  this.acceleration.add(f);
}

2.5 Creating Forces

In this chapter, I’ll look at two methods for creating forces in a p5.js world.

  1. Make up a force! After all, you are the programmer, the creator of your world. There’s no reason why you can’t just make up a force and apply it.
  2. Model a force! Yes, forces exist in the physical world. And physics textbooks often contain formulas for these forces. You can take these formulas, translate them into source code, and model real-world forces in JavaScript.

The easiest way to make up a force is to just pick a number (or two numbers really). Let’s start with the idea of simulating wind. How about a wind force that points to the right and is fairly weak? Assuming an object mover, the code would read:

let wind = createVector(0.01, 0);
mover.applyForce(wind);

The result isn’t terribly interesting, but it is a good place to start. I create a p5.Vector object, initialize it, and pass it into a Mover object (which in turn will apply it to its own acceleration). To finish off this example, I‘ll add one more force, gravity (pointing down), and only engage the wind force when the mouse is pressed.

Example 2.1: Forces

Click mouse to apply wind force.
let gravity = createVector(0, 0.1);
mover.applyForce(gravity);

if (mouseIsPressed) {
  let wind = createVector(0.1, 0);
  mover.applyForce(wind);
}

Now I have two forces, pointing in different directions with different magnitudes, both applied to object mover. I’m beginning to get somewhere. I’ve built a world, an environment with forces that act on objects!

Let’s look at what happens now when I add a second object with a variable mass. To do this, you’ll probably want to do a quick review of object-oriented programming. Again, I’m not covering all the basics of programming here (for that you can check out any of the intro p5.js books or video tutorials listed in the introduction). However, since the idea of creating a world filled with objects is fundamental to all the examples in this book, it’s worth taking a moment to walk through the steps of going from one object to many.

This is where I left the Mover class. Notice how it is identical to the Mover class created in Chapter 1, with two additions—mass and a new applyForce() function.

class Mover {

  constructor() {
    //{!1} And for now, set the mass equal to 1 for simplicity.
    this.mass = 1;
    this.position = createVector(width / 2, 30);
    this.velocity = createVector(0, 0);
    this.acceleration = createVector(0, 0);
  }

  // Newton's second law.
  applyForce(force) {
    //{!2} Receive a force, divide by mass, and add to acceleration.
    let f = p5.Vector.div(force, this.mass);
    this.acceleration.add(f);
  }

  update() {
    //{!2} Motion 101 from Chapter 1
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);

    // Now add clearing the acceleration each time!
    this.acceleration.mult(0);
  }

  show() {
    stroke(0);
    fill(175);
    //{!1} Scaling the size according to mass.
    circle(this.position.x, this.position.y, this.mass * 16);
  }

  // Somewhat arbitrarily, I have decided that an object bounces when it hits the edges ofthe canvas.
  checkEdges() {
    if (this.position.x > width) {
      this.position.x = width;
      this.velocity.x *= -1;
    } else if (this.position.x < 0) {
      this.velocity.x *= -1;
      this.position.x = 0;
    }

    if (this.position.y > height) {
      //{!2} Even though I said not to touch position and velocity directly, there are some exceptions. Here I am doing so as a quic way to reverse the direction of the object when it reaches the edge.
      this.velocity.y *= -1;
      this.position.y = height;
    }
  }
}

Now that the class is written, I can create more than one Mover object.

let moverA = new Mover();
let moverB = new Mover();

But there is an issue. Referring back to the Mover object’s constructor…

constructor() {
  //{!2} Every object has a mass of 1 and a position of (width / 2, 30).
  this.mass = 1;
  this.position = createVector(width / 2, 30);
  this.velocity = createVector(0, 0);
  this.acceleration = createVector(0, 0);
}

…you will notice that every Mover object is made exactly the same way! What I want are Mover objects of variable mass that start at variable positions. A nice way to accomplish this is with constructor arguments.

Mover(x, y, mass) {
  //{!2} Now setting these variables with arguments
  this.mass = mass;
  this.position = createVector(x, y);
  this.velocity = createVector(0, 0);
  this.acceleration = createVector(0, 0);
}

Notice how the mass and position are no longer set to hardcoded numbers, but rather initialized via the x, y, and mass arguments passed through the constructor. This means I can create a variety of Mover objects: big ones, small ones, ones that start on the left side of the canvas, ones that start on the right, and everywhere in between.

// A large Mover on the left side of the canvas
let moverA = new Mover(100, 30, 10);
// A smaller Mover on the right side of the canvas
let moverB = new Mover(400, 30, 2);

There are all sorts of ways I could choose to initialize the values (random, perlin noise, in a grid, and so on) and an array most likely makes more sense to manage large numbers of Mover objects. This is just a demonstration of the basics to get started. I‘ll introduce other techniques throughout this chapter and arrays will be covered in greater detail in Chapter 4.

Once the objects are declared and initialized, the rest of the code follows as before. For each object, pass the forces in the environment to applyForce, and enjoy the show.

Example 2.2: Forces acting on two objects

Click mouse to apply wind force.
function draw() {
  background(51);

  //{!3} Make up a gravity force and apply it.
  let gravity = createVector(0, 0.1);
  moverA.applyForce(gravity);
  moverB.applyForce(gravity);

  //{!3} Make up a wind force and apply when mouse is pressed.
  if (mouseIsPressed) {
    let wind = createVector(0.1, 0);
    moverA.applyForce(wind);
    moverB.applyForce(wind);
  }

  moverA.update();
  moverA.show();
  moverA.checkEdges();

  moverB.update();
  moverB.show();
  moverB.checkEdges();
}

When you run this code, notice how the small circle reaches the bottom of the window faster than the larger one. This is because of the formula: acceleration = force divided by mass. The larger the mass, the smaller the acceleration. While such a difference makes sense for the wind force, this is not physically accurate for a simulation of Earth‘s gravitational pull! I‘ll get into why and how to correct this in the next section.

Exercise 2.3

Instead of objects bouncing off the edge of the wall, create an example in which an invisible force pushes back on the objects to keep them in the window. Can you weight the force according to how far the object is from an edge, so that the closer it is, the stronger the force?

Exercise 2.4

Fix the bouncing off the sides of the canvas so that the circle changes direction when it‘s edge hits the side, rather than its center.

Exercise 2.4

Create a wind force that is variable. Can you make it interactive? For example, think of a fan located where the mouse is and pointed towards the circles?

Making up forces will actually get you quite far. The world of p5.js is an orchestra of pixels and you are its conductor. So whatever you deem appropriate to be a force, well by golly, that’s the force it should be. Nevertheless, there may come a time where you find yourself wondering: “But how does it really all work?” That’s when modeling forces, instead of just making them up, enters the picture.

Consider again example 2.3. If you were to climb to the top of the Leaning Tower of Pisa and drop two balls of different masses, which one will hit the ground first? According to legend, Galileo performed this exact test in 1589, discovering that they fell with the same acceleration, hitting the ground at the same time. Why is this? I‘ll dive deeper into this shortly, but the quick answer is that the force of gravity is calculated relative to an object’s mass. The bigger the object, the stronger the force. So if the force is scaled according to mass, it is canceled out when acceleration is divided by mass. A quick fix is to implement this in the sketch by multiplying the gravity force by mass.

Example 2.3: Gravity scaled by mass

Click mouse to apply wind force.
// Made-up gravity force
let gravity = createVector(0, 0.1);

// Scaled by mover A's mass
let gravityA = p5.Vector.mult(gravity, moverA.mass);
moverA.applyForce(gravityA);

// Scaled by mover B's mass
let gravityB = p5.Vector.mult(gravity, moverB.mass);
moverB.applyForce(gravityB);

While the objects now fall at the same rate, because the strength of the wind force is independent of mass, when the mouse is pressed the smaller circle still accelerates to the right more quickly. (I‘ve also included a solution to exercise 2.x in this example with the addition of a radius variable in the Mover class.)

2.6 Modeling a Force

Open up any high school physics textbook and you will find some diagrams and formulas describing different forces—gravity, electromagnetism, friction, tension, elasticity, and more. In this chapter I’m going to look at two forces—friction and gravitational attraction—and consider how to model them with p5.js. The point I’d like to make here is not that friction and gravity are fundamental forces that you always need in your simulations. Rather, I want to demonstrate these two forces as case studies for the following process:

  1. Understanding the concept behind a force
  2. Deconstructing the force’s formula into two parts:
    1. How do you compute the force’s direction?
    2. How do you compute the force’s magnitude?
  3. Translating that formula into p5.js code that calculates a vector to be passed through the Mover's applyForce() function.

If you can follow these steps with the two example forces I‘ll provide here, then hopefully when you find yourself Googling “atomic nuclei weak nuclear force” at 3 a.m., you will have the skills to take what you find and adapt it for p5.js.

Parsing Formulas

In a moment, I’m going to write out the formula for friction. This won’t be the first time you’ve seen a formula in this book; I just finished up the discussion of Newton’s second law, \vec{F} = M \times \vec{A} (or force = mass * acceleration). You hopefully didn’t spend a lot of time worrying about that formula because it’s just a few characters and symbols. Nevertheless, it’s a scary world out there. Just take a look at the equation for a “normal” distribution, which I covered (without looking at the formula) in the Introduction.

\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}

Formulas are regularly written with many symbols (often with letters from the Greek alphabet). Here‘s the formula for friction.

\vec{friction} = -\mu N \hat{\nu}

If it’s been a while since you’ve looked at a formula from a math or physics textbook, there are three key points that are important to cover before I move on.

Friction

Let’s begin with friction and follow the above steps.

Friction is a dissipative force. A dissipative force is one in which the total energy of a system decreases when an object is in motion. Let’s say you are driving a car. When you press your foot down on the brake pedal, the car’s brakes use friction to slow down the motion of the tires. Kinetic energy (motion) is converted into thermal energy (heat). Whenever two surfaces come into contact, they experience friction. A complete model of friction would include separate cases for static friction (a body at rest against a surface) and kinetic friction (a body in motion against a surface), but for simplification here, I am only going to look at the kinetic case.

Figure 2.3 shows the formula for friction.

Figure 2.3 Friction is a force that points in the opposite direction of the sled’s velocity when sliding in contact with the hill.
Figure 2.3 Friction is a force that points in the opposite direction of the sled’s velocity when sliding in contact with the hill.

It’s now time to separate this formula into two components that determine the direction of friction as well as the magnitude. Figure 2.3 indicates that friction points in the opposite direction of velocity. In fact, that’s the part of the formula that says -1 * \hat{v}, or -1 times the velocity unit vector. In p5.js, this would mean taking the velocity vector and multiplying by -1.

let friction = this.velocity.copy();
friction.normalize();
// Let’s figure out the direction of the friction force
// (a unit vector in the opposite direction of velocity).
friction.mult(-1);

Notice two additional steps here. First, it’s important to make a copy of the velocity vector, as I don’t want to reverse the object’s direction by accident. Second, the vector is normalized. This is because the magnitude of friction is not associated with the speed of the object, and I want to start with a vector of length 1 so that it can easily be scaled.

According to the formula, the magnitude is \mu * N. The Greek letter mu (\mu, pronounced “mew”), is used here to describe the coefficient of friction. The coefficient of friction establishes the strength of a friction force for a particular surface. The higher it is, the stronger the friction; the lower, the weaker. A block of ice, for example, will have a much lower coefficient of friction than, say, sandpaper. Since this is a pretend p5.js world, I can arbitrarily set the coefficient to scale the strength of the friction.

let c = 0.01;

Now for the second part. N refers to the normal force, the force perpendicular to the object’s motion along a surface. Think of a vehicle driving along a road. The vehicle pushes down against the road with gravity, and Newton’s third law tells us that the road in turn pushes back against the vehicle. That’s the normal force. The greater the gravitational force, the greater the normal force. As you’ll see in the next section, gravitational attraction is associated with mass, and so a lightweight sports car would experience less friction than a massive tractor trailer truck. In Figure 2.3, however, where the object is moving along a surface at an angle, computing the magnitude and direction of the normal force is a bit more complex because it doesn’t point in the opposite direction of gravity. You’d need to know something about angles and trigonometry.

All of these specifics are important; however, a “good enough” simulation can be achieved without them. I can, for example, make friction work with the assumption that the normal force will always have a magnitude of 1. When I get into trigonometry in the next chapter, you could return to this question and make the friction example more sophisticated. And so:

let normal = 1;

Now that I have the magnitude and direction for friction, I can put it all together in code.

let c = 0.1;
let normal = 1;
//{!1} Calculate the magnitude of friction (really just an arbitrary constant).
let frictionMag = c * normal;

let friction = mover.velocity.copy();
friction.mult(-1);
friction.normalize();

// Take the unit vector and multiply it by magnitude and this is the force vector!
friction.mult(frictionMag);

This code calculates a friction force, but doesn‘t answer the question of when to apply it? There is no answer to this question, of course, given this is all a made-up world visualized in a two dimensional p5.js canvas! I‘ll make the arbitrary, but logical, decision to apply friction when the circle comes into contact with the bottom of the canvas, which I can detect by adding a function to the Mover class called contactEdge()

contactEdge() {
  // The mover is touching the edge when it's within one pixel
  return (this.position.y > height - this.radius - 1);
}

This is a good time for me to also mention that the actual bouncing off the edge here simulates an “idealized elastic collision,” meaning no kinetic energy is lost when the circle and the edge collide. This is rarely true in the real world; pick up a tennis ball and drop it against any surface and the height at which it bounces will slowly lower until it rests against the ground. There are many factors at play here (including air resistance, which I will cover in the next section), but a quick way to simulate an inelastic collision is to reduce the magnitude of velocity by a percentage with each bounce.

bounceEdges() {
  // A new variable to simulate an inelastic collision
  // 10% of the velocity's x or y component is lost
  let bounce = -0.9;

  if (this.position.y > height - this.radius) {
    this.position.y = height - this.radius;
    this.velocity.y *= bounce;
  }
}

Finally, I can add all these pieces to the “forces” example, and simulate the object experiencing three forces: wind (when mouse is clicked), gravity (always), and now friction (when in contact with the bottom of the canvas):

Example 2.4: Including friction

Click mouse to apply wind force.
function draw() {
  background(0);

  let gravity = createVector(0, 1);
  //{!1} I should scale by mass to be more accurate, but this example only has one circle
  mover.applyForce(gravity);

  if (mouseIsPressed) {
    let wind = createVector(0.5, 0);
    mover.applyForce(wind);
  }

  if (mover.contactEdge()) {
		//{!5 .bold}
    let c = 0.1;
    let friction = mover.velocity.copy();
    friction.mult(-1);
    friction.setMag(c);

    //{!1 .bold} Apply the friction force vector to the object.
    mover.applyForce(friction);
  }

  //{!1} Calling the new bounceEdges() function
  mover.bounceEdges();
  mover.update();
  mover.display();

}

Running this example, you’ll notice that the circle eventually comes to rest. You can make this happen more or less quickly by varying the coefficient of friction as well as the percentage speed loss in the bounceEdges() function.

Exercise 2.x

Add a second object to example 2.x. How do you handle having two objects of different masses? What if each object has its own coefficient of friction relative to the bottom surface? Does it make sense to encapsulate the friction force calculation into a Mover method?

Exercise 2.x

Instead of wind, can you add functionality to this example that allows you to toss the circle with mouse interaction?

Air and Fluid Resistance

Figure 2.4
Figure 2.4

Friction also occurs when a body passes through a liquid or gas. This force has many different names, all really meaning the same thing: viscous force, drag force, fluid resistance. While the result is ultimately the same as our previous friction examples (the object slows down), the calculation of a drag force and how it behaves is different. Let’s look at the formula:

F_d = - \frac{1}{2}\rho\nu^2 A C_d\hat{\nu}

Now let’s break this down and see what we really need for an effective simulation in p5, making a simpler formula in the process.

Now that I’ve analyzed each of these components and determined what is needed for my simulation, I can reduce the formula to:

Figure 2.5: My simplified formula for a drag force
Figure 2.5: My simplified formula for a drag force

While I’ve written the formula above with C_d as the lone constant representing the “coefficient of drag”, I can also think of it as all of the constants combined ( -1/2, \rho, A). A more sophisticated simulation might consider them separately and could be considered an exercise for the reader.

The code follows:

let c = 0.1;
let speed = this.velocity.mag();
//{!1} Part 1 of the formula (magnitude): Cd * v2
let dragMagnitude = c * speed * speed;
let drag = this.velocity.copy();
//{!1} Part 2 of the formula (direction): -1 * velocity
drag.mult(-1);
drag.normalize();
// Magnitude and direction together!
drag.mult(dragMagnitude);

Let’s implement this force in the Mover example. For the friction example, I enabled the force whenever the object was in contact with the bottom edge of the canvas. Whenever the object was in contact, friction would slow it down. Here, I will introduce an element to the environment—a “liquid” that the mover passes through. The Liquid object will be drawn as a rectangle with position, width, height, and “coefficient of drag”—i.e., is it easy for objects to move through it (like air) or difficult (like molasses)? In addition, it will include show() method and more.

class Liquid {

  constructor(x, y, w, h, c) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    //{!1} The liquid object includes a variable defining its coefficient of drag.
    this.c = c;
  }

  show() {
    noStroke();
    fill(175);
    rect(this.x, this.y, this.w, this.h);
  }

}

The sketch also includes a variable liquidvariable initialized in setup().

let liquid;

function setup() {
  //{!1} Initialize a Liquid object.  Note I'm choosing a coefficient is low (0.1), for a weaker effect. Try a stronger one!
  liquid = new Liquid(0, height/2, width, height/2, 0.1);
}

Now comes an interesting question: how does the Mover object talk to the Liquid object? In other words, I want to implement the following:

When a mover passes through a liquid it experiences a drag force.

…or in object-oriented speak:

// If the Liquid contains the Mover, apply the drag force.
if (liquid.contains(mover) {
  liquid.drag(mover);
} 

The above code serves as instructions for what I need to add to the Liquid class: (1) a function that determines if a Mover object is inside the Liquid object‘s area, and (2) a function that calculates and applies the appropriate drag force on that mover.

The first is easy; I can use a Boolean expression to determine if the position vector rests inside the rectangle defined by the liquid.

contains(mover) {
  // Store position in a separate variable to make the code more readable
  let pos = mover.position;
  //{.offset-top} This Boolean expression determines if the position vector is contained within the rectangle defined by the Liquid class.
  return (pos.x > this.x && pos.x < this.x + this.w && pos.y > this.y && pos.y < this.y + this.h);
}

The drag() function requires more complexity; however, I’ve written the code for it already. This is an implementation of the drag formula! The drag force is equal to the coefficient of drag multiplied by the speed of the mover squared in the opposite direction of velocity.

 drag(mover) {
  let speed = mover.velocity.mag();
  // The force's magnitude: Cd * v~2~
  let dragMagnitude = this.c * speed * speed;
  // The force's direction: -1 * velocity
  let drag = mover.velocity.copy();
  // Finalize the force: magnitude and direction together.
  drag.setMag(dragMagnitude);
  //{!1} Apply the force.
  mover.applyForce(drag);
}

And with these two functions added to the Liquid class, I’m ready to put it all together! In the following example, I‘ll expand the code to use an array (with mover objects spaced out evenly) to demonstrate how the drag force behaves with objects of variable mass.

Example 2.5: Fluid Resistance

Click mouse to reset.
let movers = [];

let liquid;

function setup() {
  createCanvas(640, 360);
  // Initializee an array of Mover objects
  for (let i = 0; i < 10; i++) {
    // Random mass
    let mass = random(0.1, 5);
    // x value is spaced out evenly according to i
    movers[i] = new Mover(i * 20, 0, mass);
  }
  liquid = new Liquid(0, height/2, width, height/2, 0.1);
}

function draw() {
  background(255);

  liquid.show();

  for (let i = 0; i < movers.length; i++) {
    if (liquid.contains(movers[i])) {
      liquid.drag(movers[i]);
    }

		//{!2} Note that gravity is scaled according to mass.
    let m = 0.1 * movers[i].mass;
    let gravity = createVector(0, m);
    movers[i].applyForce(gravity);

    movers[i].update();
    movers[i].show();
    movers[i].checkEdges();
  }
}

Running the example, you may notice that it appears to simulate objects falling into water. The objects only slow down when crossing through the gray area at the bottom of the window (representing the liquid). You’ll also notice that the smaller objects slow down a great deal more than the larger objects. Remember Newton’s second law? A = F / M. Acceleration equals force divided by mass. A massive object will accelerate less. A smaller object will accelerate more. In this case, the acceleration is the “slowing down” due to drag. The smaller objects slow down at a greater rate than the larger ones.

Exercise 2.5

Take a look at the formula for drag again: drag force = coefficient * speed * speed. The faster an object moves, the greater the drag force against it. In fact, an object not moving (velocity of zero) experiences no drag at all. Expand the example to drop the objects from variable height. How does this affect the drag as they hit the liquid?

Exercise 2.6

The formula for drag also included surface area. Can you create a simulation of boxes falling into water with a drag force dependent on the length of the side hitting the water?

Exercise 2.7

In addition to drag being a force in opposition to the velocity vector, a drag force can be also be perpendicular. This is known as “lift-induced drag” and will cause an airplane with an angled wing to rise in altitude. Try creating a simulation of lift.

2.9 Gravitational Attraction

 Figure 2.6
Figure 2.6

Probably the most famous force of all is gravitational attraction. We humans on earth think of gravity as an apple hitting Isaac Newton on the head. Gravity means that stuff falls down. But this is only our experience of gravity. In truth, just as the earth pulls the apple towards it due to a gravitational force, the apple pulls the earth as well. The thing is, the earth is just so freaking big that it overwhelms all the other gravity interactions. In fact, every object with mass exerts a gravitational force on every other object (this is Newton‘s third law). And there is a formula for calculating the strengths of these forces, as depicted in Figure 2.6.

Let’s examine this formula a bit more closely.

Hopefully by now the formula makes some sense. I’ve shown you a diagram and dissected the individual components of the formula. Now it’s time to figure out how to translate the math into p5.js code. Let’s make the following assumptions.

We have two objects, and:

  1. Each object has a position: position1 and position2.
  2. Each object has a mass: mass1 and mass2.
  3. There is a variable G for the universal gravitational constant.

Given these assumptions, I want to compute a vector, the force of gravity. I’ll do it in two parts. First, I’ll compute the direction of the force \hat{r} in the formula above. Second, I’ll calculate the strength of the force according to the masses and distance.

Figure 2.7
Figure 2.7

Remember in Chapter 1, when I created an example of an object accelerating towards the mouse? (See Figure 2.7.)

A vector is the difference between two points. To calculate a vector that points from the circle to the mouse, I subtracted one point from another:

let dir = p5.Vector.sub(mouse, position);

Here, the direction of the attraction force that object 1 exerts on object 2 then is equal to:

let dir = p5.Vector.sub(position1, position2);
dir.normalize();

Don’t forget that since you want a unit vector, a vector that indicates direction only, it’s important to normalize the vector after subtracting the positions. (Later, I might skip this step and use setMag() instead.)

OK, I’ve got the direction of the force. Now I need to compute the magnitude and scale the vector accordingly.

let magnitude = (G * mass1 * mass2) / (distance * distance);
dir.mult(magnitude);
Figure 2.8
Figure 2.8

The only problem is that I don’t know the distance. G, mass1, and mass2 are all givens, but I need to calculate distance before the above code will work. Didn’t I just make a vector that points all the way from one position to another? Wouldn’t the length of that vector be the distance between the two objects?

Indeed, if I add one more line of code and grab the magnitude of that vector before normalizing it, then I’ll have the distance. And this time, I‘ll skip the normalize() step and use setMag().

//{!1} The vector that points from one object to another
let force = p5.Vector.sub(position1, position2);

//{!1} The length (magnitude) of that vector is the distance between the two objects.
const distance = force.mag();

//{!1} Use the formula for gravity to compute the strength of the force.
let magnitude = (G * mass1 * mass2) / (distance * distance);

//{!1} Normalize and scale the force vector to the appropriate magnitude.
force.setMag(magnitude);

Note that I also renamed the vector dir to force. After all, when the calculations are finished, the vector I started with ends up being the actual force vector I wanted all along.

Now that I’ve worked out the math and code for calculating an attractive force (emulating gravitational attraction), let‘s turn our attention to applying this technique in the context of an actual p5.js sketch. In Example 2.1, I set up the foundation for all of these examples, a Mover class—a template for making objects with p5.Vector objects for position, velocity, and acceleration as well as an applyForce() method. Let’s take this exact class and put it in a sketch with:

Figure 2.9: One mover and one attractor. The mover experiences a gravitational force towards the attractor.
Figure 2.9: One mover and one attractor. The mover experiences a gravitational force towards the attractor.

The Mover object will experience a gravitational pull towards the Attractor object, as illustrated in Figure 2.9.

I‘ll start with a basic Attractor class—giving it a position and a mass, along with a function to draw itself (tying mass to size).

class Attractor {
  //{!2} The Attractor is an object that doesn’t move. I just need a mass and a position.
  constructor() {
    this.position = createVector(width / 2, height / 2);
    this.mass = 20;
  }

  show() {
    stroke(0);
    fill(175, 200);
    circle(this.position.x, this.position.y, this.mass * 2);
  }
}

And in the sketch, I can add a variable to hold an object instance of the Attractor.

let mover;
let attractor;

function setup() {
  createCanvas(640, 360);
  mover = new Mover(300, 100, 5);
  //{!1} Initialize Attractor object.
  attractor = new Attractor();
}

function draw() {
  background(255);

  //{!1} Draw Attractor object.
  attractor.show();

  mover.update();
  mover.show();
}

This is a good start: a sketch with a Mover object and an Attractor object, made from classes that handle the variables and behaviors of movers and attractors. The last piece of the puzzle is how to get one object to attract the other. How do these two objects communicate with each other?

There are a number of ways this could be done. Here are just a few possibilities.

Task Function
1. A function that receives both an Attractor and a Mover: attraction(attractor, mover);
2. A function in the Attractor class that receives a Mover: attractor.attract(mover);
3. A function in the Mover class that receives an Attractor: mover.attractedTo(attractor);
4. A function in the Attractor class that receives a Mover and returns a p5.Vector, which is the attraction force. That attraction force is then passed into the Mover's applyForce() function: let force = attractor.attract(mover); mover.applyForce(force);

and so on. . .

It’s good to look at a range of options, and you could probably make arguments for each of the above possibilities. I’d like to at least discard the first one, since I lean towards an object-oriented approach which I prefer over an arbitrary function not tied to either the Mover or Attractor class. Whether you pick option 2 or option 3 is the difference between saying “The attractor attracts the mover” or “The mover is attracted to the attractor.” Number 4 is really my favorite, at least in terms of the examples here. After all, I spent a lot of time working out the applyForce() function, and I think the examples are clearer continuing with the same methodology.

In other words, where I once wrote:

// Made-up force
let force = createVector(0, 0.1);
mover.applyForce(force);

I now have:

//{!1 .bold} Attraction force between two objects
let force = attractor.attract(mover);
mover.applyForce(force);

And so the draw() function can be written as:

function draw() {
  background(255);

  //{!2 .bold} Calculate attraction force and apply it.
  let force = attractor.attract(mover);
  mover.applyForce(force);

  mover.update();

  attractor.display();
  mover.display();
}

I’m almost there. Since I decided to put the attract() function inside of the Attractor class, I still need to actually write that function. The function should receive a Mover object and return a p5.Vector, i.e.:

attract(m) {
  // all the math
  return ______________;
}

And what goes inside that function? All of that nice math for gravitational attraction!

attract(mover) {
  //{!1} What's the force's direction?
  let force = p5.Vector.sub(this.position, mover.position);
  let distance = force.mag();
  //{!2} What's the force's magnitude?
  float strength = (this.mass * m.mass) / (distance * distance);
  force.setMag(strength);

  //{!1} Return the force so that it can be applied!
  return force;
}

And I’m done. Sort of. Almost. There’s one small kink I need to work out. Let’s look at the above code again. See that symbol for divide, the slash? Whenever you have one of these, you should ask yourself the question: What would happen if the distance happened to be a really, really small number or (even worse!) zero??! Well, you can’t divide a number by 0, and if you were to divide a number by something like 0.0001, that is the equivalent of multiplying that number by 10,000! Yes, this is the real-world formula for the strength of gravity, but p5.js is not the real world. And in the p5.js world, the mover could end up being very, very close to the attractor and the force could become so strong the mover would fly way off the canvas. And so with this formula, it is practical to constrain the range of what distance can actually be. Maybe, no matter where the Mover actually is, you should never consider it less than 5 pixels or more than 25 pixels away from the attractor.

  distance = constrain(distance, 5, 25);

For the same reason that you need to constrain the minimum distance, it’s useful to do the same with the maximum. After all, if the mover were to be, say, 500 pixels from the attractor (not unreasonable), that is the equivalent of dividing the force by 250,000. That force might end up being so weak that it’s almost as if it’s not applied at all.

Now, it’s really up to you to decide what behaviors you want. But in the case of, “I want reasonable-looking attraction that is never absurdly weak or strong,” then constraining the distance is a good technique.

The Mover class hasn’t changed at all, so let’s just look at the main sketch and the Attractor class as a whole, adding a variable G for the universal gravitational constant. (On the website, you’ll find that this example also has code that allows you to move the Attractor object with the mouse.)

Example 2.6: Attraction

//{!2} A Mover and an Attractor
let mover;
let attractor;

// Gravitational constant (for global scaling)
let G = 1.0;

function setup() {
  size(640, 360);
  mover = new Mover(300, 100, 5);
  attractor = new Attractor();
}

function draw() {
  background(255);

  //{!2} Apply the attraction force from the Attractor on the Mover.
  const force = a.attract(m);
  mover.applyForce(force);
  mover.update();

  attractor.display();
  mover.display();
}

class Attractor {

  constructor() {
    this.position = createVector(width/2, height/2);
    this.mass = 20;
  }

  attract(mover) {
    let force = p5.Vector.sub(position, mover.position);
    let distance = force.mag();
    //{!1} Remember, we need to constrain the distance so that our circle doesn't spin out of control.
    distance = constrain(distance, 5, 25);
    let strength = (this.G * this.mass * m.mass) / (distance * distance);
    force.setMag(strength);
    return force;
  }

  show() {
    stroke(0);
    fill(175, 200);
    circle(this.position.x, this.position.y, this.mass * 2);
  }
}

Exercise 2.x Adapt the Attractor example to use a radius property for the Attractor and Mover with the mass scaled by the area of the circle (hint: since the area of a circle is \pi r^2 try squaring the radius!)

And you could, of course, expand this example to use an array for many Mover objects, just as I did with drag:

Example 2.7: Attraction with many Movers

//{!1} Now we have 10 Movers!
let movers = [];

let attractor;

function setup() {
  size(640, 360);
  for (let i = 0; i < 10; i++) {
    //{!1 .offset-top} Each Mover is initialized randomly.
    movers[i] = new Mover(random(width), random(height), random(0.1, 2));
  }
  attractor = new Attractor();
}

function draw() {
  background(255);

  aattractor.show();

  for (let i = 0; i < movers.length; i++) {
    //{!1} Calculate an attraction force for each Mover object.
    let force = a.attract(movers[i]);
    movers[i].applyForce(force);

    movers[i].update();
    movers[i].show();
  }

}

Exercise 2.8

In the example above, there is a system (i.e. array) of Mover objects and one Attractor object. Build an example that has systems of both movers and attractors. What if you make the attractors invisible? Can you create a pattern/design from the trails of objects moving around attractors? See the Metropop Denim project by Clayton Cubitt and Tom Carden for an example.

Exercise 2.9

It’s worth noting that gravitational attraction is a model you can follow to invent your own forces. This chapter isn’t suggesting that you should exclusively create sketches that use gravitational attraction. Rather, you should be thinking creatively about how to design your own rules to drive the behavior of objects. For example, what happens if you design a force that is weaker the closer it gets and stronger the farther it gets? Or what if you design your attractor to attract faraway objects, but repel close ones?

2.10 Everything Attracts (or Repels) Everything

Hopefully, you found it helpful that I started with a simple scenario—one object attracts another objectand moved on to one object attracts many objects. However, it’s likely that you are going to find yourself in a slightly more complex situation: many objects attract each other. In other words, every object in a given system attracts every other object in that system (except for itself).

You’ve really done almost all of the work for this already. Let’s consider a p5.js sketch with an array of Mover objects:

let movers = [];

function setup() {
  createCanvas(640, 360);
  for (let i = 0; i < 10; i++) {
    movers[i] = new Mover(random(width), random(height), random(0.1, 2));
  }
}

function draw() {
  background(255);
  for (let i = 0; i < movers.length; i++) {
    movers[i].update();
    movers[i].show();
  }
}

The draw() function is where I need to work some magic. Currently, I’m saying: “for every mover i, update and draw.” Now what I need to say is: “for every mover i, be attracted to every other mover j, and update and draw.”

To do this, I need to nest a second loop.

 for (let i = 0; i < movers.length; i++) {
    //{!1} For every Mover, check every Mover!
    for (let j = 0; j < movers.length; j++) {
      let force = movers[j].attract(movers[i]);
      movers[i].applyForce(force);
    }
    movers[i].update();
    movers[i].show();
  }

In the previous example, I had an Attractor object with a function named attract(). Now, since there are movers attracting movers, all I need to do is copy the attract() function into the Mover class itself.

class Mover {

  //[inline] All the other stuff from before plus. . .

  //{!1} The Mover now knows how to attract another Mover.
  attract(other) {

    const force = p5.Vector.sub(this.position, other.position);
    let distance = force.mag();
    distance = constrain(distance, 5, 25);
    force.normalize();

    // Note to add a value for gravity in the Mover class: this.G
    let strength = (this.G * this.mass * m.mass) / (distance * distance);
    force.setMag(strength);
    return force;
  }
}

Of course, there’s one small problem. When every mover i attracts every mover j, what about when i equals j? Should mover #3 attract mover #3? The answer, of course, is no. If there are five objects, you only want mover #3 to attract 0, 1, 2, and 4, skipping itself. And so, I finish this example by adding a conditional statement to skip applying the force when i equals j.

Example 2.8: Mutual attraction

let movers = [];

function setup() {
  createCanvas(640, 360);
  for (let i = 0; i < 20; i++) {
    movers[i] = new Mover(random(width), random(height), random(0.1, 2));
  }
}

function draw() {
  background(255);

  for (let i = 0; i < movers.length; i++) {
    for (let j = 0; j < movers.length; j++) {
      //{!1} Don't attract yourself!
      if (i !== j) {
        let force = movers[j].attract(movers[i]);
        movers[i].applyForce(force);
      }
    }
    movers[i].update();
    movers[i].display();
  }
}

Exercise 2.10

Change the attraction force in Example 2.8 to a repulsion force. Can you create an example in which all of the Mover objects are attracted to the mouse, but repel each other? Think about how you need to balance the relative strength of the forces and how to most effectively use distance in your force calculations.

The Ecosystem Project

Step 2 Exercise:

Incorporate the concept of forces into your ecosystem. Try introducing other elements into the environment (food, a predator) for the creature to interact with. Does the creature experience attraction or repulsion to things in its world? Can you think more abstractly and design forces based on the creature’s desires or goals?