Code Steps

p5.js Particle Workshop 2/3

November 23, 2019

Add a second particle.

This post continues Part 1.

}
let particleX = 150
let particleY = 50
let dy = 1
export function setup() {
createCanvas(windowWidth, windowHeight)
}
export function draw() {
fill("darkblue")
rect(0, 0, width, height)
particleY += dy
dy += 0.01
fill("white")
rect(particleX, particleY, 10, 10)
}
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)
}
createCanvas(windowWidth, windowHeight)

Here’s where we left off at the end of Part 1. The sketch so far draws a single “particle” (white rectangle), that is accelerating downwards.

In this section, we’ll add a second particle.

We’ll separate this into two steps:

  1. Identify the part of the code has responsbility for the single particle.

  2. Copy the identified code, to draw a second particle.

Identifying the code responsible for the particle

The particle is spread across two parts:

  • The data (represented as variables)

  • The behavior is represented as code that (1) changes the values of these variables (by assigning to them), and (2) changes the state of the program or of the world (in this case, by calling p5.js functions such as rect, that change what’s on the screen).

When we copy the code that involves variables, we’re going to need to create new variables, with new names. In order to set up for this, rename the variables that have to do with the first (and only) particle, so that it’s clear that this is what they do.

This doesn’t crash, and there’s still a moving particle. But it doesn’t completely work — we only see one particle, when the code has two.

The problem is that the two particles are exactly the same. They start out in the same position, and they move the same way. One is drawn on top of the other.

We need to make one of the particles a little different from the other. We’ll do this by giving the second particle a different initial position.

Getting past two

This works for two particles. What would it take to add a third particle? A hundred particles? What makes this difficult?

The problem is that we each particle needs its own lines of code. This makes the program easier to understand, but difficult to extend.

We’ll fix this problem in a series of steps:

  • Package the data into an object. It’s easier to make copies of an object, than to make lots of copies of the code that defines the data.

  • Package the behavior into a function. It’s easier to run a function on each object, than to make lots of copies of the code that implements the behavior.

let defines a variable, with a name and value. A JavaScript object acts like a set of variables (called “properties”), each with its own name and value. An object is created by using the { } syntax, and its property values are accessed by using the dot notation: the value of anObject.prop is the value of the property named prop, in the object that is the value of anObject.

This is easier to understand from an example. Study the difference between the data and behavior of particle 1, and the data and behavior of particle 2. These two particles behave in exactly the same way (except that particle 2 appears at a greater x position, since we set its x value differently).

Now update particle 2 to store its data in an object too.

Create a function, that encapsulates the behavior of the first particle.1

This definition of processParticle only works on particle1. (That’s the variable that it mentions in the function body — the lines of code between { and } in the function definition.)

We can parameterize processParticle, by giving it a parameter (here, particle). This is the name that gets used inside the function, to refer to different values. Each time the function is run, this name has whatever value was used in the function call (inside of draw). Here, draw calls processParticle(particle1), so processParticle is run with particle1 as the value of particle.

This is like the use of the word “it” in English.

Since particle 1 and particle 2 have the same behavior (just different initial data), and processParticle implements that behavior, we can now use it for particle2 as well.

The actual code and variable names now describe which values belong to which particle, and which part of the code implements the behavior for each particle. The comments that we created in order to organize the code, are no longer necessary.

Part 3 continues these notes.


  1. Since p5.js isn’t looking for a function with this name, we don’t need to export it like we did setup and draw. We just need to be consistent between where we define the function (in function particle()), and where we use it (in processParticle() inside of the draw function).