You are previewing HTML5 Canvas.

# Simple Gravity, Elasticity, and Friction

Adding simulated gravity, elasticity, and friction to your objects adds a sense of realism that otherwise would not exist in 2D. These properties are major forces in nature that people feel and understand at nearly every moment of their lives. This means that people who play games expect objects to act in a particular way when these properties are applied. Our job is to simulate those effects as closely as possible, while minimizing the processing power necessary to create them. While there are some very complicated physics equations we could use to create these effects, we will use simplified versions that work well with the limited resources available to HTML5 Canvas in a web browser.

## Simple Gravity

A very simple, yet seemingly realistic gravitational effect can be achieved by applying a constant gravity value to the `y` velocity of an object moving on a vector. To do this, select a value for gravity, such as `.1`, and then add that value to the `y` velocity of your object on every call to `drawScreen()`.

For this example, let’s simulate a ball with a `radius` of `15` pixels being shot from a cannon that rests near the bottom of the canvas. The ball will move at a `speed` of `4` pixels per frame, with an `angle` of `305` degrees. This means it will move up and to the right on the canvas. If we did not apply any gravity, the ball would simply keep moving on that vector until it left the canvas (actually, it would keep moving, we just would not see it any longer).

You have seen the code to create an effect like this already. In the `canvasApp()` function, we would create the starting variables like this:

```var speed = 4;
var angle = 305;
var radians = angle * Math.PI/ 180;
var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;```

Next, we create the starting point for the ball as `p1`, and then create a dynamic object that holds all the values we created for the `ball` object:

```var p1 = {x:20,y:theCanvas.width-radius};

If we want to add gravity to the application, we would first create a new variable named `gravity` and set it to a constant value of `.1`:

`var gravity = .1;`

Next, in the `drawScreen()` function, we apply this gravity value to the `ball` object when it is drawn to the canvas (`ball.velocityy += gravity`). We want the ball to stop moving when it hits the “ground” (the bottom of the canvas), so we test to see whether the `y` position of the `ball` plus the `radius` of the ball (the outer edge) has passed the bottom of the canvas (```ball.y + ball.radius <= theCanvas.height```). If so, we stop the ball’s movement:

```if (ball.y + ball.radius <= theCanvas.height) {
ball.velocityy += gravity;
} else {
ball.velocityx = 0;
ball.velocityy = 0;

}```

Next, we apply the constant `x` velocity and the new `y` velocity to `ball`, and draw it to the canvas:

```ball.y += ball.velocityy;
ball.x += ball.velocityx;

context.fillStyle = "#000000";
context.beginPath();
context.closePath();
context.fill();```

Figure 5-17 shows what the path looks like when simple gravity is applied to a ball moving on a vector. We have added the points to illustrate the path.

You can test out Example 5-14 with the file CH5EX14.html in the code distribution, or type in the full code listing below.

Example 5-14. Simple gravity

```a<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX14: Simple Gravity</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
canvasApp();
}

function canvasSupport () {
return Modernizr.canvas;
}

function canvasApp() {

if (!canvasSupport()) {
return;
}

function  drawScreen () {

context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#000000';
context.strokeRect(1,  1, theCanvas.width-2, theCanvas.height-2);

if (ball.y + ball.radius <= theCanvas.height) {
ball.velocityy += gravity;
} else {
ball.velocityx = 0;
ball.velocityy = 0;

}

ball.y += ball.velocityy;
ball.x += ball.velocityx;

context.fillStyle = "#000000";
context.beginPath();
context.closePath();
context.fill();

}
var speed = 4;

var gravity = .1;
var angle = 305;
var radians = angle * Math.PI/ 180;
var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;

theCanvas = document.getElementById("canvasOne");
context = theCanvas.getContext("2d");

setInterval(drawScreen, 33);

}

</script>

<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="500">
Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>```

## Simple Gravity with a Bounce

The last example showed what a cannonball might look like if it was shot out, landed on a surface, and stuck there with no reaction. However, even a heavy cannonball will bounce when it hits the ground.

To create a bouncing effect we do not have to change the code very much at all. In `drawScreen()`, we first apply `gravity` on every frame; then, instead of stopping the ball if it hits the bottom of the canvas, we simply need to reverse the `y` velocity of `ball` when it hits the ground.

In CH5EX14.html you would replace this code…

```if (ball.y + ball.radius <= theCanvas.height) {
ball.velocityy += gravity;
} else {
ball.velocityx = 0;
ball.velocityy = 0;
}```

…with this:

```ball.velocityy += gravity;
if ((ball.y + ball.radius) > theCanvas.height) {
ball.velocityy = -(ball.velocityy)
}```

This code will send the ball bouncing back “up” the canvas. Since it is still traveling on the vector, and gravity is applied every time `drawScreen()` is called, the ball will eventually come down again as the applied `gravity` overtakes the reversed `y` velocity.

Figure 5-18 shows what the cannonball looks like when the bounce is applied.

### Note

To achieve a nice-looking bounce for this example, we also changed the `angle` of the vector in `canvasApp()` to `295`:

`var angle = 295;`

Example 5-15 offers the full code.

Example 5-15. Simple gravity with a bounce

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX15: Gravity With A Bounce</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
canvasApp();

}

function canvasSupport () {
return Modernizr.canvas;
}

function canvasApp() {

if (!canvasSupport()) {
return;
}

function  drawScreen () {

context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#000000';
context.strokeRect(1,  1, theCanvas.width-2, theCanvas.height-2);

ball.velocityy  += gravity;

if ((ball.y + ball.radius) > theCanvas.height) {
ball.velocityy = -(ball.velocityy)
}
ball.y += ball.velocityy;
ball.x += ball.velocityx;

context.fillStyle = "#000000";
context.beginPath();
context.closePath();
context.fill();

}
var speed = 5;

var gravity = .1;
var angle = 295;
var radians = angle * Math.PI/ 180;

var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;

theCanvas = document.getElementById("canvasOne");
context = theCanvas.getContext("2d");

setInterval(drawScreen, 33);

}

</script>

<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="500">
Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>```

## Gravity with Bounce and Applied Simple Elasticity

In physics, the elasticity of a bouncing ball refers to how much energy is conserved when a ball bounces off a surface. We already covered a bit about conservation of energy when we discussed balls colliding, but when we are simulating objects falling, we need to take a slightly different path with our code. In Example 5-15, we applied 100% elasticity and the ball bounced forever (actually, this was only implied since we did not consider elasticity at all). However, in real life, balls usually lose some of their energy every time they bounce off a surface. The amount of energy conserved depends on the material the ball is made from, as well as the surface it is bouncing on. For example, a rubber Super Ball is much more elastic than a cannonball and will bounce higher on the first bounce off a surface. Both will bounce higher off a concrete surface than a surface made of thick mud. Eventually, both will come to rest on the surface as all the energy is transferred away from the ball.

We can simulate simple elasticity by applying a constant value to the ball when it bounces off the ground. For this example, we will set the `speed` of the ball to `6` pixels per frame, and the `angle` to `285`. We will keep our `gravity` at `.1`, but set a new variable named `elasticity` to `.5`. To make this more straightforward, we will also assume that the surface the ball is bouncing on does not add or subtract from the elasticity of the ball.

In `canvasApp()` we would set the new properties like this:

```var speed = 6;
var gravity = .1;
var elasticity = .5;
var angle = 285;```

We then add the new `elasticity` property to the `ball` object because, unlike `gravity`, elasticity describes a property of an object, not the entire world it resides within. This means that having multiple balls with different values for elasticity would be very easy to implement:

```var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius,
elasticity: elasticity};```

In the `drawScreen()` function, we still add the `gravity` value to the `y` velocity (`velocityy`). However, instead of simply reversing the `y` velocity when the `ball` hits the bottom of the canvas, we also multiply the `y` velocity by the `elasticity` value stored in the `ball.elasticity` property. This applies the elasticity to the bounce, preserving the `y` velocity by the percentage value of `elasticity` for the object:

```ball.velocityy += gravity;
if ((ball.y + ball.radius) > theCanvas.height) {
ball.velocityy = -(ball.velocityy)*ball.elasticity;
}
ball.y += ball.velocityy;
ball.x += ball.velocityx;```

In Figure 5-19 you can see what this application looks like when executed in a web browser.

### Note

With `gravity` applied, the bounce is not exactly as you might expect. Gravity is always pulling down on our object, so the effect of a loss of `y` velocity due to an elastic bounce is pronounced.

The full code is shown in Example 5-16.

Example 5-16. Simple gravity with bounce and elasticity

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX16: Gravity With A Vector With Bounce And Elasticity</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
canvasApp();

}

function canvasSupport () {
return Modernizr.canvas;
}

function canvasApp() {

if (!canvasSupport()) {
return;
}

function  drawScreen () {

context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#000000';
context.strokeRect(1,  1, theCanvas.width-2, theCanvas.height-2);

ball.velocityy += gravity;

if ((ball.y + ball.radius) > theCanvas.height) {
ball.velocityy = -(ball.velocityy)*ball.elasticity;
}
ball.y += ball.velocityy;
ball.x += ball.velocityx;

context.fillStyle = "#000000";
context.beginPath();
context.closePath();
context.fill();

}
var speed = 6;
var gravity = .1;
var elasticity = .5;
var angle = 285;
var radians = angle * Math.PI/ 180;

var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;

theCanvas = document.getElementById("canvasOne");
context = theCanvas.getContext("2d");

elasticity: elasticity};

setInterval(drawScreen, 33);

}

</script>

<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="500">
Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>```

## Simple Gravity, Simple Elasticity, and Simple Friction

Now that we have a ball traveling on a vector that is affected by both gravity and elasticity, we have one more element to add to make the animation more realistic. In the previous example, the `y` velocity was affected by gravity and elasticity, but the ball still traveled on the x-axis without any degradation in velocity. We will fix this issue now by adding friction into the equation.

In physics, friction is a force that resists the motion of an object. We have already discussed friction as it applies to colliding balls, and this implementation is similar except that it affects only the `x` velocity. For our purposes, we will achieve simple friction by degrading the `x` velocity as gravity degrades the `y` velocity.

Taking the code from Example 5-16, in `canvasApp()` we create a new variable named `friction`. This is the amount of pixels to degrade the `x` velocity on every frame:

`var friction = .008;`

Notice that the amount is quite small. Friction does not have to be a large value to look realistic—it just needs to be applied uniformly each time `drawScreen()` is called. In `drawScreen()`, we apply `friction` to the `x` velocity like this:

`ball.velocityx = ball.velocityx - ( ball.velocityx*friction);`

This is the same type of proportional application of friction we used with the colliding balls, but again, this time we applied it only to the `x` velocity.

Figure 5-20 shows what this final version of our application looks like when executed in a web browser.

Example 5-17 gives the full code for CH5EX17.html, the final code of our simple gravity, simple elasticity, and simple friction example.

Example 5-17. Gravity with a vector with bounce friction

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX17: Gravity With A Vector With Bounce Friction</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
canvasApp();
}

function canvasSupport () {
return Modernizr.canvas;
}

function canvasApp() {

if (!canvasSupport()) {
return;
}

function  drawScreen () {

context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//Box
context.strokeStyle = '#000000';
context.strokeRect(1,  1, theCanvas.width-2, theCanvas.height-2);

ball.velocityx = ball.velocityx - ( ball.velocityx*friction);

ball.velocityy += gravity;

if ((ball.y + ball.radius) > theCanvas.height) {
ball.velocityy = -(ball.velocityy)*ball.elasticity;
}
ball.y += ball.velocityy;
ball.x += ball.velocityx;

context.fillStyle = "#000000";
context.beginPath();
context.closePath();
context.fill();

}
var speed = 6;
var gravity = .1;
var friction = .008;
var elasticity = .5;
var angle = 285;
var radians = angle * Math.PI/ 180;

var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;

theCanvas = document.getElementById("canvasOne");
context = theCanvas.getContext("2d");

elasticity: elasticity};

setInterval(drawScreen, 33);

}

</script>