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.

Main content

Air and fluid resistance

Diagram of fluid resistance around a plane
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 way in which we calculate a drag force will be slightly different. Let’s look at the formula:
Fd=12ρv2ACdv^
Now let’s break this down and see what we really need for an effective simulation in ProcessingJS, making ourselves a much simpler formula in the process.
  • Fd refers to drag force, the vector we ultimately want to compute and pass into our applyForce() function.
  • -1/2 is a constant: -0.5. This is fairly irrelevant in terms of our ProcessingJS world, as we will be making up values for other constants anyway. However, the fact that it is negative is important, as it tells us that the force is in the opposite direction of velocity (just as with friction).
  • ρ is the Greek letter rho, and refers to the density of the liquid, something we don’t need to worry about. We can simplify the problem and consider this to have a constant value of 1.
  • v refers to the speed of the object moving. OK, we’ve got this one! The object’s speed is the magnitude of the velocity vector: velocity.mag(). And v2 just means v squared or vv.
  • A refers to the frontal area of the object that is pushing through the liquid (or gas). An aerodynamic Lamborghini, for example, will experience less air resistance than a boxy Volvo. Nevertheless, for a basic simulation, we can consider our object to be spherical and ignore this element.
  • Cd is the coefficient of drag, exactly the same as the coefficient of friction (μ). This is a constant we’ll determine based on whether we want the drag force to be strong or weak.
  • v^ Look familiar? It should. This refers to the velocity unit vector, i.e. velocity.normalize(). Just like with friction, drag is a force that points in the opposite direction of velocity.
Now that we’ve analyzed each of these components and determined what we need for a simple simulation, we can reduce our formula to:
Simplified formula: F_drag = ||v^2|| * c_d * v - 1
or:
// Part 1 of our formula (magnitude): v^2 * Cd
var c = 0.1;
var speed = velocity.mag();
var dragMagnitude = c * speed * speed;

// Part 2 of our formula (direction): v unit vector * -1 
var drag = velocity.get();
drag.normalize();
drag.mult(-1);

// Magnitude and direction together!
drag.mult(dragMagnitude);
Let’s implement this force in our Mover object type with one addition. When we wrote our friction example, the force of friction was always present. Whenever an object was moving, friction would slow it down. Here, let’s introduce an element to the environment—a “liquid” that the Mover objects pass through. The Liquid object will be a rectangle and will know about its location, 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 should include a function to draw itself on the screen (and two more functions, which we’ll see in a moment).
var Liquid = function(x, y, w, h, c) {
  this.x = x;
  this.y = y;
  this.w = w;
  this.h = h;
  this.c = c;
};

Liquid.prototype.display = function() {
  noStroke();
  fill(50);
  rect(this.x, this.y, this.w, this.h);
};
The main program will now declare and initialize a new Liquid object instance. Note the coefficient is low (0.1), otherwise the object would come to a halt fairly quickly (which may someday be the effect you want).
var liquid = new Liquid(0, height/2, width, height/2, 0.1);
Now comes an interesting question: how do we get the Mover object to talk to the Liquid object? In other words, we want to execute the following:
When a mover passes through a liquid it experiences a drag force.
…or in object-oriented speak (assuming we are looping through an array of Mover objects with index i):
// Is the Mover in the liquid?
if (liquid.contains(movers[i])) {
    // Calculate drag force
    var dragForce = liquid.calculateDrag(movers[i]);
    // Apply drag force to Mover
    movers[i].applyForce(dragForce);
}
The above code tells us that we need to add two functions to the Liquid object type: (1) a function that determines if a Mover object is inside the Liquid object, and (2) a function that computes the drag force exerted on the Mover object.
The first is easy; we can simply use a conditional statement to determine if the location vector rests inside the rectangle defined by the liquid.
Liquid.prototype.contains = function(m) {
    var p = m.position;
    return p.x > this.x && p.x < this.x + this.w &&
         p.y > this.y && p.y < this.y + this.h;
};
The drag() function is a bit more complicated; however, we’ve written the code for it already. This is simply an implementation of our 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!
Liquid.prototype.calculateDrag = function(m) {
  // Magnitude is coefficient * speed squared
  var speed = m.velocity.mag();
  var dragMagnitude = this.c * speed * speed;

  // Direction is inverse of velocity
  var dragForce = m.velocity.get();
  dragForce.mult(-1);

  // Scale according to magnitude
  dragForce.normalize();
  dragForce.mult(dragMagnitude);
  return dragForce;
};
And with these two functions added to the Liquid object type, we’re ready to put it all together into one program:
Running the program, you should notice that we are simulating balls falling into water. The objects only slow down when crossing through the blue 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 we’re talking about is the “slowing down” due to drag. The smaller objects will slow down at a greater rate than the larger ones.

This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.

Want to join the conversation?

  • blobby green style avatar for user Jason Holtrey
    In the Sinking logs challenge how are you supposed to calculate the drag coefficient based on the log width if we haven't defined how long each log is?
    (6 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user seannam1218
    I noticed that objects bounce off the surface of the water when I play around with the coefficient of drag. Why is this happening and how can I fix it?
    (6 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      Drag pushes back on forward movement. If you make the coefficient big enough (or the speed fast enough), the push-back overwhelms the forward movement. So how about
      dragMagnitude = min(speed, dragMagnitude);
      But, be aware in the real world something crashing into a viscous liquid at high speed may bounce off of it.
      (9 votes)
  • ohnoes default style avatar for user Steven  Parenti
    I'm stuck on step 1. It keeps saying "You should always apply forces before the update method is called, so that the force has its effect first." Where did I go wrong? Here is my code.
    var Liquid = function(x, y, w, h, c) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.c = c;
    };

    Liquid.prototype.contains = function(m) {
    var l = m.position;
    return l.x > this.x && l.x < this.x + this.w &&
    l.y > this.y && l.y < this.y + this.h;
    };

    Liquid.prototype.calculateDrag = function(m) {
    var speed = m.velocity.mag();
    var dragMagnitude = this.c * speed * speed;

    var dragForce = m.velocity.get();
    dragForce.mult(-1);

    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
    };

    Liquid.prototype.display = function() {
    noStroke();
    fill(126, 138, 242);
    rect(this.x, this.y, this.w, this.h);
    };

    var Log = function(w, x, y) {
    this.mass = 1;
    this.position = new PVector(x, y);
    this.velocity = new PVector(0, 0);
    this.acceleration = new PVector(0, 0);
    };

    Log.prototype.applyForce = function(force) {
    var f = PVector.div(force, this.mass);
    this.acceleration.add(f);
    };

    Log.prototype.update = function(m) {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    };

    Log.prototype.display = function() {
    stroke(0, 0, 0);
    strokeWeight(2);
    fill(184, 150, 55);
    rect(this.position.x, this.position.y, this.width, this.mass*16);
    };

    Log.prototype.checkEdges = function() {
    if (this.position.y > height) {
    this.velocity.y *= -1;
    this.position.y = height;
    }
    };

    var logs = [];
    var liquid = new Liquid(0, height/2, width, height/2, 0.5);

    for (var i = 0; i < 5; i++) {
    logs[i] = new Log(20, 20+i*width/5, 0);
    }

    draw = function() {
    background(235, 254, 255);
    liquid.display();

    for (var i = 0; i < logs.length; i++) {
    var gravity = new PVector(0, 0.1*logs[i].mass);
    logs[i].applyForce(gravity);
    if (liquid.contains(logs[i])) {
    var Arrastar = liquid.calcuateDrag(logs[i]);
    logs[i].applyForce(Arrastar);
    }
    logs[i].update();
    logs[i].display();
    logs[i].checkEdges();
    }
    };
    (5 votes)
    Default Khan Academy avatar avatar for user
  • leaf orange style avatar for user dawsonsthomas
    Can someone please explain the code of the return statement of this function. it is about halfway through the page, and I have never seen one like this. Thank you!

    Liquid.prototype.contains = function(m) {
    var p = m.position;
    return p.x > this.x && p.x < this.x + this.w &&
    p.y > this.y && p.y < this.y + this.h;
    };
    (2 votes)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user LightningBoltBoy
    I am stuck on step one of the next challenge. I believe I have the right code, but there is still an orange prompt at the top that tells me to " apply forces before the update method is called, so that the force has its effect first." I tried moving the draw function before the update method, but that gave me an Oh Noes message. Any help would be greatly appreciated.
    My code:
    var Liquid = function(x, y, w, h, c) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.c = c;
    };

    Liquid.prototype.contains = function(m) {
    var l = m.position;
    return l.x > this.x && l.x < this.x + this.w &&
    l.y > this.y && l.y < this.y + this.h;
    };

    Liquid.prototype.calculateDrag = function(m) {
    var speed = m.velocity.mag();
    var dragMagnitude = this.c * speed * speed;

    var dragForce = m.velocity.get();
    dragForce.mult(-1);

    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
    };

    Liquid.prototype.display = function() {
    noStroke();
    fill(126, 138, 242);
    rect(this.x, this.y, this.w, this.h);
    };

    var Log = function(w, x, y) {
    this.mass = 1;
    this.width = w;
    this.position = new PVector(x, y);
    this.velocity = new PVector(0, 0);
    this.acceleration = new PVector(0, 0);
    };

    Log.prototype.applyForce = function(force) {
    var f = PVector.div(force, this.mass);
    this.acceleration.add(f);
    };

    Log.prototype.update = function() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    };

    Log.prototype.display = function() {
    stroke(0, 0, 0);
    strokeWeight(2);
    fill(184, 150, 55);
    rect(this.position.x, this.position.y, this.width, this.mass*16);
    };

    Log.prototype.checkEdges = function() {
    if (this.position.y > height) {
    this.velocity.y *= -1;
    this.position.y = height;
    }
    };

    var logs = [];
    var liquid = new Liquid(0, height/2, width, height/2, 0.1);

    for (var i = 0; i < 5; i++) {
    logs[i] = new Log(20, 20+i*width/5, 0);


    }
    draw = function() {
    background(235, 254, 255);
    liquid.display();

    for (var i = 0; i < logs.length; i++) {
    var gravity = new PVector(0, 0.1*logs[i].mass);
    logs[i].applyForce(gravity);
    if (liquid.contains(logs[i])) {
    var dragForce = logs.calculateForce(logs[i]);
    logs[i].applyForce(dragForce);}
    logs[i].update();
    logs[i].display();
    logs[i].checkEdges();
    }
    };
    (2 votes)
    Default Khan Academy avatar avatar for user
  • male robot donald style avatar for user SIsilicon
    Will there ever be a course for object collisions and bouyancy?
    (3 votes)
    Default Khan Academy avatar avatar for user
    • leafers seedling style avatar for user PIANO!
      Oh! Hi didn't expect to see you here! :D

      I know this comment is old. But this is what I used.
      this.calcBuoyancy=function(m){
      var weightOfDisplacedFluid = (this.y-m.position.y)*this.weight;//how deep * how heavy the liquid is
      var apparentImmersedWeight = this.weight - weightOfDisplacedFluid;
      var buoyancy = new PVector(0,-apparentImmersedWeight);
      return buoyancy;
      };


      It looks about right when use it in my simulator. You also have to set the Liquids weight. I keep it between
      this.weight = 0.01; and this.weight = 0.05
      It all depends on the properties of the liquid.
      (4 votes)
  • aqualine tree style avatar for user KidaTerraBoundUltra
    Can anyone help me with step two of sinking logs?
    Here's my code so far:

    for (var i = 0; i < 5; i++) {
    logs[i] = new Log(logs[i].width, 20+i*width/5, 0);
    }

    I'm sure that this is right, but the grader won't accept it.
    (3 votes)
    Default Khan Academy avatar avatar for user
  • hopper happy style avatar for user lilyrosethomas
    This is my code:

    if(liquid.contains(logs[i])){
    var dragForce = liquid.calculateDrag(logs[i]);
    logs[i].applyForce(dragForce);
    }

    and Oh Noes keeps saying: "Cannot read property 'position' of undefined" I don't even have that property in there and I haven't touched any other code, all the other code is fine if I take out that part, but when I put it in the error shows up. Please tell me what I'm doing wrong.
    (4 votes)
    Default Khan Academy avatar avatar for user
  • piceratops ultimate style avatar for user Owen S
    What if the object (in this case, the ball), had a decided density of less than one, meaning the object would float in the water (assuming the liquid was water)? Is there a way to simulate this?
    (3 votes)
    Default Khan Academy avatar avatar for user
  • leaf orange style avatar for user apex [still active]
    Pamela says:
    // Is the Mover in the liquid?
    if (liquid.contains(movers[i])) {
    // Calculate drag force
    var dragForce = liquid.calculateDrag(movers[i]);
    // Apply drag force to Mover
    movers[i].applyForce(dragForce);
    }


    cant you say

    // Is the Mover in the liquid?
    if (liquid.contains(movers[i])) {
    // Calculate drag force
    //var dragForce = ; <--instead of making a variable, can't we put it straight in? Is it a must-do, or is it just neat?
    // Apply drag force to Mover
    movers[i].applyForce(liquid.calculateDrag(movers[i]));
    }
    (2 votes)
    Default Khan Academy avatar avatar for user
    • spunky sam blue style avatar for user Dalendrion
      I prefer pulling statements apart and putting intermediate values into variables.

      I prefer that for two reasons:
      1. It gives the intermediate value a name, so I can understand the code more easily.
      2. If I want to debug, I can quite easily see the value of the variable, without changing any code.

      I would prefer inlining expressions (that is, not use an intermediate variable) for one reason:
      1. It makes the code shorter.

      Whether that reason weighs up against the other two reasons is up to you as the author.
      (3 votes)