HTML5 Canvas and Friction

So you started moving an object on your canvas by manipulating vx and vy, i.e., velocity on the x and y axis. But then, you just realized that in real world scenarios the speed of an object reduces with time, if there’s no continuous acceleration. Even with an acceleration of X, there will be some friction faced counter-affecting the acceleration.

Basic Idea

Friction should be subtracted from the speed. If you choose a random value for friction, subtract that from the speed of velocity but not the x and y axis themselves, which are vx and vy in our case. Why ? Let’s understand.

Let’s say we got a vx of 100 and vy of 50. Your friction is 10 and you start subtracting from them. At some point you will reach a vx of 50 while a vy of 0. Form there onwards your object is going to keep on moving horizontally until vx is 0. This will definitely look awkward.

This is why, you either subtract the friction from the magnitude of velocity or decrease the vx and vy by a constant percentage so that they scale down by the same ratio and the final effect achieved seems proper.

Easy Approach

Implementing friction is really easy. You just need to multiply your velocities with a fraction of 1. So just take a value between 0 and 1 and multiply that with your velocities. Something like this:

// Friction Factor
var friction = 0.7;
// .. more code ..

// Inside the drawing method
vx *= friction;
vy *= friction;

// And finally adding your velocities to
// both axis
object.x += vx;
object.y += vy;

So the logic is that, as you multiply by say 0.7, you basically reduce the velocity by 70%. The velocity will keep on cutting down to a lower magnitude but actually not reach 0. Although, at a point it’s going to be so small that any applied motion will be almost imperceptible.

But you can always add in a if...else check for a low value of speed and just set your velocity to 0 there.

The Math-style Approach

There’s one more approach that requires more code and logic through which you can basically brag off that you are smart with Math, Physics and JS. Ok, just joking, but let’s take a look at it.

Consider this little figure:

Fit this drawing in your head. Will help you many times when coding 2D experiments or games.

Now, we have our speed using pythagorean theorem:

// Pythagorean Theorem
var speed = Math.sqrt(vx*vx + vy*vy);

Next, all we need to do is get our friction value deducted from our speed.

// Decrease the speed
if (speed > friction) {
	speed -= friction;
} else {
	speed = 0;
}

If our speed is less than friction, then we just set it to 0. This is important as you can already imagine. Now given that we have our “new” resultant speed, we need to calculate our new vx and vy. How do we do that ? Easy! Using trigonometry.

// First get the angle
var angle = Math.atan2(vy, vx);
vx = Math.cos(angle) * speed; // cos(x) = adjacent/hypotenuse
vy = Math.sin(angle) * speed; // sin(x) = opposite/hypotenuse

That’s it! This method is more accurate and realistic than the “easy way”, but really, atleast I couldn’t figure out difference with an experiment where I used both the implementations. Similarly, most users won’t be able to figure out either. They’ll just be fine the easy way, so use it most of the times.

Update – Yet Another Way

So a reader thinks that the previous approach that involves trigonometry is incorrect, so I thought it would be good to share what he recommends instead. According to him, we do not need to use trig.

var speed = Math.sqrt(vx*vx+vy*vy);
var newSpeed = Math.max(0, speed - delta);
var scaleFactor = newSpeed / speed || 0;
vx *= scaleFactor;
vy *= scaleFactor;

He says it would be incorrect to call the delta value “friction” from a physics point of view. Friction is a force, not a velocity.

Demo

I prepared a quick demo on this concept that has some acceleration too!


Must be giving you some ideas for your next game ? Yeh, I bet! I’ll probably build on this sometime soon and create a neat 2D car game. Stay tuned!

Share:

2 thoughts on “HTML5 Canvas and Friction

  1. Stefan

    Nice explanations + drawings. As a non mathematician I appreciate the handling of these more complex calculations :-)

    The last implementation without the Math-Function calls seems to be the most efficient one (besides the Math.max). It just needs to be checked for the default case when newSpeed & speed are 0. In this case you get 0/0 -> NaN, which doesn’t work for the calculation.

    Just add the fallback as a guard and it works: scaleFactor = newSpeed / speed || 0;

    Reply
    1. Rishabh Post author

      You’re right! I have updated the code.

      But remember you might need a few more checks. For example speed might never reach 0 but a really small number like 0.1… – So you’ll need an if/else check and re-set it to 0 or something.

      Glad you liked it. Good Luck!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>