If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

### Course: Computer programming - JavaScript and the web>Unit 5

Lesson 8: Particle Systems

# Particle systems with forces

So far in this section, we’ve been focusing on structuring our code in an object-oriented way to manage a collection of particles. Maybe you noticed, or maybe you didn’t, but during this process we unwittingly took a couple steps backward from where we were in previous sections. Let’s examine the constructor of our simple Particle object:
var Particle = function(position) {
this.acceleration = new PVector(0, 0.05);
this.velocity = new PVector(random(-1, 1), random(-1, 0));
this.position = new PVector(position.x, position.y);
this.timeToLive = 255.0;
};
And now let’s look at the update() method:
Particle.prototype.update = function(){
this.timeToLive -= 2;
};
Notice that our acceleration is constant, it's never set beyond the constructor. A much better framework would be to follow Newton’s second law ($\stackrel{\to }{F}=M\stackrel{\to }{A}$) and incorporate the force accumulation algorithm we worked so hard on in the Forces section.
The first step is to add in an applyForce() method. (Remember, we need to make a copy of the PVector before we divide it by mass.)
Particle.prototype.applyForce = function(force) {
var f = force.get();
f.div(this.mass);
};
Once we have this, we can add in one more line of code to clear the acceleration at the end of update().
Particle.prototype.update = function() {
this.acceleration.mult(0);
this.timeToLive -= 2.0;
};
And thus, we have a Particle object that can have force applied to it. Now, where should we call the applyForce() function? Where in the code is it appropriate to apply a force to a particle? The truth of the matter is that there’s no right or wrong answer; it really depends on the exact functionality and goals of a particular program. Still, we can create a generic situation that would likely apply to most cases and craft a model for applying forces to individual particles in a system.

Let’s consider the following goal: Apply a force globally every time through draw() to all particles. We’ll start with applying a simple wind-like force that pushes the particles to the right:
var wind = new PVector(0.4, 0);
We said it should always be applied, i.e. in draw(), so let’s take a look at our draw() function as it stands.
draw = function() {
background(168, 255, 156);
particleSystem.run();
};
Well, it seems that we have a small problem. applyForce() is a method written inside the Particle object, but we don’t have any reference to the individual particles themselves, only the ParticleSystem object: the variable particleSystem.
Since we want all particles to receive the force, however, we can decide to apply the force to the particle system and let it manage applying the force to all the individual particles.
draw = function() {
background(168, 255, 156);
particleSystem.applyForce(wind);
particleSystem.run();
};
Of course, if we call a new function on the ParticleSystem object in draw(), well, we have to write that function in the ParticleSystem object. Let’s describe the job that function needs to perform: receive a force as a PVector and apply that force to all the particles.
Now in code:
ParticleSystem.prototype.applyForce = function(f){
for(var i = 0; i < this.particles.length; i++){
this.particles[i].applyForce(f);
}
};
It almost seems silly to write this function. What we’re saying 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 really quite reasonable. After all, the ParticleSystem object is in charge of managing the particles, so if we want to talk to the particles, we’ve got to talk to them through their manager.
Here it is, all together. Play around with the wind force and see how it affects the particle movement, and notice how particles of different mass respond differently. Think about why that is.

Now let's apply a more complex force, gravity, which is different from wind because it varies based on the mass of the objects its applied to.
Let's recall the equation for calculating the force of gravity between two masses:  $\stackrel{\to }{{F}_{g}}=\frac{G{m}_{1}{m}_{2}}{||r|{|}^{2}}\stackrel{^}{r}$
Remember that when we're modeling the force of gravity on earth, the force exerted by the earth overwhelms all other gravitational forces, so the only equation we're dealing with is to compute the force of gravity between the earth and the object.$G$ and ${m}_{1}$ are the same for every particle, and $r$(the radius from the earth) is basically the same (since earth's radius is so large compared to how little the particles move away from it), so we typically simplify those as simply g, the constant for gravity on earth:
$g=\frac{G{m}_{1}}{||r|{|}^{2}}$
Now, the force of gravity is just some constant g, times the mass of the particles, multiplied by a unit vector in the direction of the force (which will always be down):
$\stackrel{\to }{{F}_{g}}=g{m}_{2}\stackrel{^}{r}$
In code, that means that we will need to apply a different gravity force to each particle based on its mass. How can we do that? We can't re-use the existing applyForce function, because it expects the same force for each particle. We might consider passing a parameter to it that instructs applyForce to multiply by the mass, but let's leave that function alone and create a new function, applyGravity, which calculates force based on a global constant vector:
// A constant down vector, declared at the top
var gravity = new PVector(0, 0.2);
ParticleSystem.prototype.applyGravity = function() {
for(var i = 0; i < this.particles.length; i++) {
var particleG = gravity.get();
particleG.mult(this.particles[i].mass);
this.particles[i].applyForce(particleG);
}
};
Now, if we've done this correctly, all of our particles should fall at the same rate in the simulation below. That's because the force of gravity is based on multiplying the mass, but the acceleration is based on dividing by the mass, so in the end, the mass doesn't have an effect. It might seem silly to go through that much effort to not have an effect, but its important once we start combining multiple different forces.

What if we wanted to take this example one step farther and add a repeller object that pushes particles away as they get close? It would be similar to the attractor object we created earlier, only pushing in the opposite direction. Once again, like gravity, we must calculate a different force for each particle, but in the case of the repeller, the difference is that the calculation isn't based on mass, it's based on distance. For gravity, all of our force vectors had the same direction, but for the repeller, all the force vectors will have different directions:
Because the calculation of a repeller force is a little more complex than the calculation of gravity (and we might ultimately want multiple repellers!), we will solve this problem by incorporating a new Repeller object into our simple particle system plus gravity example. We’re going to need two major additions to our code:
1. A Repeller object (declared, initialized, and displayed).
2. A function that passes the Repeller object into the ParticleSystem so that it can apply a force to each particle object.
var particleSystem = new ParticleSystem(new PVector(width/2, 50));
var repeller = new Repeller(width/2-20, height/2);
var gravity = new PVector(0, 0.1);

draw = function() {
background(214, 255, 171);

// Apply gravity force to all Particles
particleSystem.applyForce(gravity);
particleSystem.applyRepeller(repeller);
repeller.display();
particleSystem.run();
};
Making a displayable Repeller object is easy; it’s a duplicate of the Attractor object that we created earlier:
var Repeller = function(x, y) {
this.position = new PVector(x, y);
};

Repeller.prototype.display = function() {
stroke(255);
strokeWeight(2);
fill(127);
ellipse(this.position.x, this.position.y, 32, 32);
};
The more difficult question is, how do we write the applyRepeller() method? Instead of passing a PVector into a function like we do with applyForce(), we’re going 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:
ParticleSystem.prototype.applyRepeller = function(r) {
for(var i = 0; i < this.particles.length; i++){
var p = this.particles[i];
var force = r.calculateRepelForce(p);
p.applyForce(force);
}
};
The big difference here is that a new force is calculated for each particle, because, as we saw before, the force is different depending on properties of each particle in relation to the repeller. We calculate that force using the calculateRepelForce function, which is the inverse of the calculateAttractionForce function from our Attractors.
Repeller.prototype.calculateRepelForce = function(p) {
// Calculate vector for force between objects
var dir = PVector.sub(this.position, p.position);
// Calculate distance between objects
var dist = dir.mag();
// Keep distance within a reasonable range
dist = constrain(dist, 1, 100);
// Calculate repelling force,
// inversely proportional to distance squared
var force = -1 * this.power/ (dist * dist);
// Normalize direction vector
dir.normalize();
// Calculate force vector: direction * magnitude
dir.mult(force);
return dir;
};
Notice how throughout this entire process of adding a repeller to the environment, we’ve never once considered editing the Particle object itself. A particle doesn’t actually have to know anything about the details of its environment; it simply needs to manage its location, velocity, and acceleration, as well as have the ability to receive an external force and act on it.
So we can now look at this example in its entirety. Try changing the power of the forces acting on the particles - the gravity and the repeller - and see how that changes them:

## Want to join the conversation?

• One thing I noticed about the repeller is that it only repels once the "liquid" particles are "inside" it. Is there a way to check if they are hitting the edges then repel?
• You can change the constraints on line 27. However, no code captures the notion of "inside" or "on the edge". It simply applies the inverse R² rule to the particle and repeller.

Note that both particles and the repeller are represented by points although they are drawn with circles. That non-realistic representation is the root of the issue. This program takes pain to address that issue: https://www.khanacademy.org/computer-programming/elastic-collisions-w-pvector/6239663899672576
• Oh noes guy says the function eval() can be harmful, can anyone tell me why? Also, is there a better place to put questions like this, that don't relate to any of the lessons?
• For your second question: No, this is the right place to be.

For your first question: JavaScript can be powerful. eval() allows other people to execute their JavaScript code on your website. That means you're giving those people a lot of power...which can be abused in ways you may not have expected. This opens a path for hackers.
• Was this one hard to understand for anybody or is it just me?
• This course as well as Algorithms is where most people struggle to move forward given the information density and evaluator's pickiness. It's been a couple of years, why not give this another try?
(1 vote)
• angleMode = "radians";var Repeller = function(x, y) {    this.power = 400;    this.position = new PVector(x, y);};Repeller.prototype.display = function() {    image(getImage("cute/Rock"), this.position.x, this.position.y-50, 60, 80);};Repeller.prototype.calculateRepelForce = function(p) {    var dir = PVector.sub(this.position, p.position);    var d = dir.mag();    dir.normalize();    d = constrain(d, 1, 100);        var force = -1 * this.power/ (d * d);              dir.mult(force);                                      return dir;};var Particle = function(position) {    this.acceleration = new PVector(0, 0);    this.velocity = new PVector(random(0, 1), random(-1, 0.5));    this.position = position.get();    this.timeToLive = 255.0;    this.mass = 10;};Particle.prototype.run = function() {    this.update();    this.display();};Particle.prototype.applyForce = function(force) {    var f = force.get();    f.div(this.mass);    this.acceleration.add(f);};Particle.prototype.update = function() {    this.velocity.add(this.acceleration);    this.position.add(this.velocity);    this.acceleration.mult(0);    this.timeToLive -= 2.0;};Particle.prototype.display = function() {    noStroke();    fill(204, 241, 255, 100);    ellipse(this.position.x, this.position.y, 12, 12);};Particle.prototype.isDead = function(){    if (this.timeToLive < 0.0 || this.position.x > width || this.position.y < 0 || this.position.y > height) {        return true;    } else {        return false;    }};var ParticleSystem = function(origin, height) {    this.origin = origin.get();    this.height = height;    this.particles = [];};ParticleSystem.prototype.addParticle = function() {    for (var i = 0; i < 10; i++) {      var startPos = new PVector(this.origin.x,            this.origin.y + random(-this.height/2, this.height/2));        this.particles.push(new Particle(startPos));      }};ParticleSystem.prototype.applyForce = function(f){    for(var i = 0; i < this.particles.length; i++){        this.particles[i].applyForce(f);    }};ParticleSystem.prototype.applyRepeller = function(r) {    for(var i = 0; i < this.particles.length; i++){        var p = this.particles[i];        var force = r.calculateRepelForce(p);        p.applyForce(force);    }};ParticleSystem.prototype.run = function(){	for (var i = this.particles.length-1; i >= 0; i--) {        var p = this.particles[i];        p.run();        if (p.isDead()) {            this.particles.splice(i, 1);        }    }};var repellers = [];//for (var i = 0; i<10; i++){mousePressed = function(){repellers.push(new Repeller(mouseX, mouseY));};// Set up water pressure, particle system, and repellervar pressure = new PVector(0.4, 0);var particleSystem = new ParticleSystem(new PVector(0, height/2), 200);//var repeller = new Repeller(width/2, height/2);draw = function() {    // Draw ground    for (var i = 0; i < 10; i++) {    image(getImage("cute/DirtBlock"), i*95, -50);       image(getImage("cute/DirtBlock"), i*95, 268);    }        // Draw river    noStroke();    fill(163, 230, 255);    rect(0, 86, width, 233);          for (var i = 0; i < repellers.length; i++) {      // Update particle system      particleSystem.applyForce(pressure); particleSystem.applyRepeller(repellers[i]);        repellers[i].display();            }      //repeller.display();          particleSystem.addParticle();    particleSystem.run();  };

so i did the next challenge and it works but it doesn't pass, why?
• First of all, thanks for posting your code, because it helped me pass. Secondly, the grader, as usual, is being a nitpicker--instead of using "mousePressed", write "mouseClicked".
• Visually, I can see what this.acceleration.mult(0); does, but what is it actually doing?
• Multiplying by the scaler zero is a simple way of zeroing out all the components of the PVector. It is simpler than this.acceleration.set(0, 0, 0);
• Why, on KA, does some program's text get misaligned? I can see from the tumbnails that they were supposed to be aligned but for me its all cut off?
(1 vote)
• The easiest explanation is "font mismatch". The author's machine uses a font that your machine does not have, so your machine use its default font. Disappointment ensues.
In the example you cite, the author is using his default font. We expect defaults to be (nearly) the same, but there is no guarantee of that!
Scrutinize the word "You" in the thumbnail versus what you see in the running program. The "o" is half of the height of the "Y" in the thumbnail. On my machine, the "o" is 70% or 80% of the height of the "Y". I'm sure this mismatch carries over to the widths too, and thus our disappointment.
• Last example, line 38, I typed this.position = position instead of this.position = position.get(); and got a different result. I think I know why, but I'm not 100% clear. Please explain.
• It's because position.get(); will return the original position variable, but the position variable is being changed in between the point where they first set position and where you changed the code. Just using position will use the current position value, not the original.
• How would I get a particle to react to another particle? I know how to adjust each particle one-by-one using a for loop. But how could I get an individual particle to react to all other particles? Thanks!
for (var i = 0; i < particles.length; i++) { for (var j = 0; j < particles.length; j++) { if (particles[i] !== particles[j]) { // Do whatever } }}