If you're seeing this message, it means we're having trouble loading external resources for Khan Academy.

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

Now we're going to use more advanced object-oriented programming techniques like inheritance, so you may want to review "Inheritance" in the Intro to JS course and come back. Don't worry, we'll wait!

Feeling good about how inheritance works? Good, because we're going to use inheritance to make different types of Particle sub-objects, which share much of the same functionality but also differ in key ways.

Let's review a simplified Particle implementation:

var Particle = function(position) {
  this.acceleration = new PVector(0, 0.05);
  this.velocity = new PVector(random(-1, 1), random(-1, 0));
  this.position = position.get();
};

Particle.prototype.run = function() {
  this.update();
  this.display();
};

Particle.prototype.update = function(){
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

Particle.prototype.display = function() {
  fill(127, 127, 127);
  ellipse(this.position.x, this.position.y, 12, 12);
};

Next, we create a new object type based on Particle, which we'll call Confetti. We'll start off with a constructor function that accepts the same number as arguments, and simply calls the Particle constructor, passing them along:

var Confetti = function(position) {
  Particle.call(this, position);
};

Now, in order to make sure that our Confetti objects share the same methods as Particle objects, we need to specify that their prototype should be based on the Particle prototype:

Confetti.prototype = Object.create(Particle.prototype);
Confetti.prototype.constructor = Confetti;

At this point, we have Confetti objects that act exactly the same way as Particle objects. The point of inheritance isn't to make duplicates, it's to make new objects that share a lot of functionality but also differ in some way. So, how is a Confetti object different? Well, just based on the name, it seems like it should look different. Our Particle objects are ellipses, but confetti is usually little bits of square paper, so at the very least, we should change the display method to show them as rectangles instead:

Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255, this.timeToLive);
  stroke(0, 0, 0, this.timeToLive);
  strokeWeight(2);
  rect(0, 0, 12, 12);
};

Here's a program with one Particle object instance and one Confetti object instance. Notice they behave similarly but differ in their appearance:

Adding rotation

Let’s make this a bit more sophisticated. Let’s say we want to have the Confetti particle rotate as it flies through the air. We could, of course, model angular velocity and acceleration as we did in the Oscillations section. Instead, we’ll try a quick and dirty solution.

If the particles were in a vacuum, 

We know a particle has an x location somewhere between 0 and the width of the window. What if we said: when the particle’s x location is 0, its rotation should be 0; when its x location is equal to the width, its rotation should be equal to TWO_PI? Sound familiar? Whenever we have a value with one range that we want to map to another range, we can use the ProcessingJS map() function to easily compute the new value.

var theta = map(this.position.x, 0, width, 0, TWO_PI);

And just to give it a bit more spin, we can actually map the angle’s range from 0 to TWO_PI*2. Let’s look at how this code fits into the display() method.

Confetti.prototype.display = function(){
  rectMode(CENTER);
  fill(0, 0, 255);
  stroke(0, 0, 0);
  strokeWeight(2);
  pushMatrix();
  translate(this.position.x, this.position.y);
  var theta = map(this.position.x, 0, width, 0, TWO_PI * 2);
  rotate(theta);
  rect(0, 0, 12, 12);
  popMatrix();
};

Here's how that looks - restart it a few time to see the effect of the rotation:

We could also base the theta on the y position, which has a bit of a different effect. Why is that? Well, the particle has a non-zero constant acceleration in the y direction, which means that the y velocity is a linear function of time, and that the y position is actually a parabolic function of time. You can see what that means in the graphs below (which were generated based on the above program):

That means that if we base the confetti rotation on the y position, the rotation will also be parabolic. This won't be too physically accurate since the actual rotation of confetti falling through the air is pretty complicated, but try it yourself and see how realistic it looks! Can you think of other functions which might look even more realistic?"

A diverse ParticleSystem

Now, what we really want is to be able to create many Particle objects and many Confetti objects. That's what we made the ParticleSystem object for, so perhaps we can just extend it to also keep track of Confetti objects? Here's one way we could do that, copying what we did for the Particle objects:

var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
  this.confettis = []; // SAD FACE!
};

ParticleSystem.prototype.addParticle = function() {
    this.particles.push(new Particle(this.origin));
    this.particles.push(new Confetti(this.origin));
};

ParticleSystem.prototype.run = function(){
  for (var i = this.particles.length-1; i >= 0; i--) {
    var p = this.particles[i];
    p.run();
  }
for (var i = this.confettis.length-1; i >= 0; i--) {
    var p = this.confettis[i]; p.run();
  }
};

Notice that we have two separate arrays, one for particles and one for confetti. Every time we do something to the particles array, we have to do it to the confetti array! That's annoying, because it means we have to write twice as much code, and if we change something, we have to change it in two places. We could actually avoid this duplication, because we're allowed to store objects of different types in arrays in JavaScript, and because our objects have the same interface - we're calling the run() method, and both types of objects define that interface. So, we'll go back to just storing a single array, we'll randomly decide what type of particle object to add, and we'll go back to iterating through the single array. This is a much simpler change - all we end up modifying is the addParticle method:

var ParticleSystem = function(position) {
  this.origin = position;
  this.particles = [];
};

ParticleSystem.prototype.addParticle = function() {
  var r = random(1);
  if (r < 0.5) {
    this.particles.push(new Particle(this.origin));
  } else {
    this.particles.push(new Confetti(this.origin));
  }
};

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);
    }
  }
};

All together now!