## With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

No credit card required

# Bouncing Off Walls

While it’s neat that we can create a vector with magnitude and direction and then move an object along it infinitely, it’s probably not something you will need to do all that often. Most of the time, you will want to see that object react to the world around it by bouncing off horizontal and vertical walls, for example.

To help you understand how to do this, there is a simple rule in physics. Although this rule is usually applied to rays of light, it can be very useful when animating 2D objects—especially when they are bouncing off horizontal and vertical walls. This rule is known as the angle of reflection:

The angle of incidence is equal to the angle of reflection.

The angle of incidence is the angle an object is traveling when it hits the walls, and the angle of reflection is the angle it travels after it bounces off the wall.

Figure 5-4 illustrates that when an object hits a wall on a line that forms a 45-degree angle with a perpendicular line drawn to the point of impact, it will bounce off (reflect) at a similar 45-degree angle.

In the next section, we will create a series of examples using this rule to animate objects. The first, Example 5-4, will simply allow a single ball to bounce off the edges of the canvas.

## Bouncing a Single Ball

In this first example, we will create a ball traveling on a vector. We will set the `speed` (magnitude) to `5` and the `angle` (direction) to `35` degrees. The rest of the variables are identical to those in Example 5-3. We are still moving on a vector, but now we will test to see whether the ball hits a “wall” (the edges of the canvas), in which case it will bounce off, using the rule of the angle of reflection. One big change from the previous vector example is the location in which we initialize the values for `radians`, `xunits`, and `yunits`. Instead of setting them up when we initialize the application in `canvasApp()`, we save that for a call to a new function named `updateBall()`:

```var speed = 5;
var p1 = {x:20,y:20};
var angle = 35;
var xunits = 0;
var yunits = 0;
var ball = {x:p1.x, y:p1.y};
updateBall();```

The `updateBall()` function is called every time we set a new `angle` for the ball, because we need to recalculate the `radians` and find new values for `xunits` and `yunits`. A new `angle` is generated when the app starts, as well as every time the ball bounces off a wall:

```function updateBall() {
radians = angle * Math.PI/ 180;
}```

In `drawScreen()`, we update the position of the ball, and then draw it on the canvas:

```ball.x += xunits;
ball.y += yunits;
context.fillStyle = "#000000";
context.beginPath();
context.arc(ball.x,ball.y,15,0,Math.PI*2,true);
context.closePath();
context.fill();```

Next, we test to see whether the ball has hit a wall before we draw it to the canvas. If the ball hits the right side (`ball.x > the Canvas.width`) or the left side of the canvas (`ball.x < 0`), we set the angle to 180 degrees minus the angle of the vector on which the ball is traveling. This gives us the angle of reflection. Alternatively, if the ball hits the top (`ball.y < 0`) or bottom (```ball.y > theCanvas.height```) of the canvas, we calculate the angle of reflection with 360 degrees minus the angle of the vector on which the ball is traveling:

```if (ball.x > theCanvas.width || ball.x < 0 ) {
angle = 180 - angle;
updateBall();
} else if (ball.y > theCanvas.height || ball.y < 0) {
angle = 360 - angle;
updateBall();
}```

That’s it. Example 5-4 demonstrates a ball that bounces off walls using the rules of physics. Figure 5-5 illustrates the code.

Example 5-4. Ball bounce

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX4: Ball 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.x += xunits;
ball.y += yunits;
context.fillStyle = "#000000";
context.beginPath();
context.arc(ball.x,ball.y,15,0,Math.PI*2,true);
context.closePath();
context.fill();

if (ball.x > theCanvas.width || ball.x < 0 ) {
angle = 180 - angle;
updateBall();
} else if (ball.y > theCanvas.height || ball.y < 0) {
angle = 360 - angle;
updateBall();
}

}

function updateBall() {
radians = angle * Math.PI/ 180;
}

var speed = 5;
var p1 = {x:20,y:20};
var angle = 35;
var xunits = 0;
var yunits = 0;
var ball = {x:p1.x, y:p1.y};
updateBall();

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>```

### Note

The points on the line are not drawn when executed in the web browser because they slowed down the ball far too much. We left them in Figure 5-5 to illustrate the angles of incidence and reflection.

## Multiple Balls Bouncing Off Walls

One ball is cool, but what about 100? Is the code 100 times more complicated? No, not at all. In fact, the code is only slightly more complicated, but it is also more refined. Most programming tasks that require only a single object of a type tend to allow you to be a bit lazy. However, when you need to build an application that must support `n` number of objects, you need to make sure the code will work in many different cases.

In the case of 100 balls bouncing on the canvas, we will need to create a ball object with a few more properties. Recall that the ball object we created previously had only `x` and `y` properties, and looked like this:

`var ball = {x:p1.x, y:p1.y};`

All the other variables that represented the ball (`speed`, `angle`, `xunits`, `yunits`) were global in scope to the `canvasApp()`. We used global variables because we could get away with it. However, because we need to make sure everything works the same way in this app, we make all those values properties of each ball object.

For the multiple-ball-bounce application, we will create an object that holds all the pertinent information about each bouncing ball: `x`, `y`, `speed`, `angle`, `xunits`, and `yunits`. Because we are going to create 100 balls of various sizes, we also add a property named `radius`, which represents the size of the ball (well, half the size since it is a radius):

```tempBall = {x:tempX,y:tempY,radius:tempRadius, speed:tempSpeed,
angle:tempAngle, xunits:tempXunits, yunits:tempYunits}```

Inside `canvasApp()`, we define some new variables to help manage the multiple balls that will bounce around the canvas:

`numBalls`

The number of balls to randomly create

`maxSize`

The maximum radius length for any given ball

`minSize`

The minimum radius length for any given ball

`maxSpeed`

The maximum speed any ball can travel

`balls`

An array to hold all of the ball objects we will create

The following code shows the newly defined variables:

```var numBalls = 100 ;
var maxSize = 8;
var minSize = 5;
var maxSpeed = maxSize+5;
var balls = new Array();```

We also create a set of temporary variables to hold the values for each ball before we push it into the `balls` array:

```var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempXunits;
var tempYunits;```

Next, in `canvasApp()`, we iterate through a loop to create all the ball objects. Notice how `tempX` and `tempY` are created below. These values represent the ball’s starting location on the canvas. We create a random value for each, but we offset it by the size of the ball (`tempRadius*2`). If we did not do that, some of the balls would get “stuck” in a wall when the app starts because their `x` or `y` location would be “through” the wall, but their `speed` would not be enough so that a “bounce” would get them back on the playfield. They would be stuck in bouncing limbo forever (which is kind of sad when you think about it).

### Note

When you try this app, you will see that occasionally a ball still gets stuck in a wall. There is a further optimization we need to make to prevent this, but it is a bigger subject than this little iteration. We will talk about it in the section Multiple Balls Bouncing and Colliding.

The `tempSpeed` variable is created by subtracting the value of `tempRadius` from the value of `maxSpeed`, which we created earlier. The `speed` is not random, but it is inversely proportional to the size (radius) of the ball. A larger ball has larger radius, so the value you subtract from `tempSpeed` will be larger, thus making the ball move slower:

### Note

When you run CH5EX4.html in your web browser, you will notice that this little trick makes the ball appear more “real” because your brain expects larger objects to move slower.

```for (var i = 0; i < numBalls; i++) {
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

xunits:tempXunits, yunits:tempYunits}
balls.push(tempBall);
}```

Now we need to draw the balls onto the canvas. Inside `drawScreen()`, the code to draw the balls should look very familiar because it is essentially the same code we used for one ball in Example 5-4. We just need to loop through the `balls` array to render each ball object:

```for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x += ball.xunits;
ball.y += ball.yunits;

context.beginPath();
context.closePath();
context.fill();

if (ball.x > theCanvas.width || ball.x < 0 ) {
ball.angle = 180 - ball.angle;
updateBall(ball);
} else if (ball.y > theCanvas.height || ball.y < 0) {
ball.angle = 360 - ball.angle;
updateBall(ball);
}
}```

When you load Example 5-5 in your web browser, you will see a bunch of balls all moving around the screen independently, as shown in Figure 5-6. For the fun of it, why not change the `numBalls` variable to 500 or 1,000? What does the canvas look like then?

Example 5-5. Multiple ball bounce

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX5: Multiple Ball 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);

//Place balls
context.fillStyle = "#000000";
var ball;

for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x += ball.xunits;
ball.y += ball.yunits;

context.beginPath();
context.closePath();
context.fill();

if (ball.x > theCanvas.width || ball.x < 0 ) {
ball.angle = 180 - ball.angle;
updateBall(ball);
} else if (ball.y > theCanvas.height || ball.y < 0) {
ball.angle = 360 - ball.angle;
updateBall(ball);
}
}

}

function updateBall(ball) {

ball.radians = ball.angle * Math.PI/ 180;

}

var numBalls = 100 ;
var maxSize = 8;
var minSize = 5;
var maxSpeed = maxSize+5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempXunits;
var tempYunits;

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

for (var i = 0; i < numBalls; i++) {
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

xunits:tempXunits, yunits:tempYunits}
balls.push(tempBall);
}

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>```

## Multiple Balls Bouncing with a Dynamically Resized Canvas

Before we move on to some more complex interaction among balls, let’s try one more thing. Back in Chapter 3, we resized the canvas with some HTML5 form controls to display text in the center of the canvas. Well, let’s do the same thing now with the ball example. This will give you a better idea of how we can make objects interact with a dynamically resizing canvas.

First, in the HTML, we create two HTML5 `range` controls, one for `width` and one for `height`, and set their maximum values to `1000`. We will use these controls to set the width and height of the canvas at runtime:

```<form>

Canvas Width:  <input type="range" id="canvasWidth"
min="0"
max="1000"
step="1"
value="500"/>
<br>
Canvas Height:  <input type="range" id="canvasHeight"
min="0"
max="1000"
step="1"
value="500"/>
<br>

</form>```

In `canvasApp()`, we create the event listeners for the HTML5 form controls. We listen for the `change` event, which means any time the `range` control is moved, the event handlers will be called:

```formElement = document.getElementById("canvasWidth")

formElement = document.getElementById("canvasHeight")

The event handler functions capture the changes to the range, set `theCanvas.width` or `theCanvas.height`, and then call `drawScreen()` to render the new size. Without a call to `drawScreen()` here, the canvas will blink when the new size is applied in `drawScreen()` on the next interval:

```function canvasWidthChanged(e) {
var target = e.target;
theCanvas.width = target.value;
drawScreen();
}

function canvasHeightChanged(e) {
var target = e.target;
theCanvas.height = target.value;
drawScreen();
}```

### Note

All of this is explained in gory detail in Chapter 3.

One last thing—let’s increase the number of balls set in `canvasApp()` to `500`:

`var numBalls = 500 ;`

Now, check out Example 5-6 (CH5EX6.html from the code distribution). When you run the code in a web browser, you should see 500 balls bounce around the canvas, as shown in Figure 5-7. When you increase the width or height using the `range` controls, they continue moving until they hit the new edge of the canvas. If you make the canvas smaller, the balls will be contained within the smaller space. If you adjust the size too rapidly, some balls will be lost off the canvas, but they will reappear when the canvas is resized. Neat, huh?

Example 5-6. Multiple ball bounce with dynamically resized canvas

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

function canvasSupport () {
return Modernizr.canvas;
}

function canvasApp() {

if (!canvasSupport()) {
return;
}

formElement = document.getElementById("canvasWidth")

formElement = document.getElementById("canvasHeight")

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

//Place balls
context.fillStyle = "#000000";
var ball;

for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x += ball.xunits;
ball.y += ball.yunits;

context.beginPath();
context.closePath();
context.fill();

if (ball.x > theCanvas.width || ball.x < 0 ) {
ball.angle = 180 - ball.angle;
updateBall(ball);
} else if (ball.y > theCanvas.height || ball.y < 0) {
ball.angle = 360 - ball.angle;
updateBall(ball);
}
}

}

function updateBall(ball) {

ball.radians = ball.angle * Math.PI/ 180;

}

var numBalls = 500 ;
var maxSize = 8;
var minSize = 5;
var maxSpeed = maxSize+5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempXunits;
var tempYunits;

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

for (var i = 0; i < numBalls; i++) {
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

xunits:tempXunits, yunits:tempYunits}
balls.push(tempBall);
}

setInterval(drawScreen, 33);

function canvasWidthChanged(e) {
var target = e.target;
theCanvas.width = target.value;
drawScreen();
}

function canvasHeightChanged(e) {
var target = e.target;
theCanvas.height = target.value;
drawScreen();
}

}

</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>
<form>

Canvas Width:  <input type="range" id="canvasWidth"
min="0"
max="1000"
step="1"
value="500"/>
<br>
Canvas Height:  <input type="range" id="canvasHeight"
min="0"
max="1000"
step="1"
value="500"/>
<br>

</form>
</div>
</body>
</html>```

## Multiple Balls Bouncing and Colliding

Now it’s time to step it up again. Testing balls bouncing off walls is one thing, but what about balls bouncing off one another? We will need to add some pretty intricate code to handle this type of interaction.

### Ball interactions in physics

For this example, we are going to create an elastic collision, which means that the total kinetic energy of the objects is the same before and after the collision. This is known as the law of conservation of momentum (Newton’s third law). To do this, we will take the x and y velocities of two colliding balls, and draw a “line of action” between their centers. This is illustrated in Figure 5-8, which has been adapted from Jobe Makar and Ben Winiarczyk’s Macromedia’s Flash MX 2004 Game Design Demystified (Macromedia Press). Then we will create new x and y velocities for each ball based on this angle and the law of conservation of momentum.

To properly calculate conservation of momentum when balls collide on the canvas, we need to add a new property: `mass`. Mass is the measurement of how much a ball (or any object) resists any change in its velocity. Because collisions tend to change the velocity of objects, this is an important addition to the ball objects we will use on the canvas.

### Making sure the balls don’t start on top of each other

We will work from the code we created for Example 5-6 (CH5EX6.html). The first big change to that code is to make sure the balls don’t randomly start on top of one another. If we let them start in the same location, they would be forever intertwined and would spin off into oblivion. To be honest, it looks pretty cool when that happens, but that’s not the result we are looking to achieve.

In `canvasApp()`, we set a variable named `tempRadius` to `5`. We will use this value as the radius for each ball we create. Next, we create another new variable named `placeOK` and set it to `false`. When this is equal to `true`, we know we have found a place to put a ball that is not on top of another ball.

Next, we enter a `while()` loop that will continue to iterate as long as `placeOK` is `false`. Then, we set all the values for our new ball object:

```tempRadius = 5;
var placeOK = false;
while (!placeOK) {
tempSpeed = 4;
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

Now, we need to make a dynamic object out of the values we just created and place that object into the `tempBall` variable. This is where we create a `mass` property for each ball. Again, we do this so that we can calculate the effect when the balls hit one another. For all the balls in this example, the `mass` will be the same—the value of `tempRadius`. We do this because, in our 2D environment, the relative size of each ball is a very simple way to create a value for `mass`. Since the `mass` and `speed` of each ball will be the same, they will affect each other in a similar way. Later, we will show you what happens when we create ball objects with different `mass` values.

Finally, we create `nextX` and `nextY` properties that are equal to `x` and `y`. We will use these values as “look ahead” properties to help alleviate collisions that occur “between” our iterations, which lead to overlapping balls and other oddities:

```tempBall = {x:tempX,y:tempY, nextX: tempX, nextY: tempY, radius:tempRadius,
speed:tempSpeed, angle:tempAngle, velocityx:tempvelocityx,

Now that we have our new dynamic ball object represented by the `tempBall` variable, we will test to see whether it can be placed at the `tempX` and `tempY` we randomly created for it. We will do this with a call to a new function named `canStartHere()`. If `can``StartHere()` returns `true`, we drop out of the `while()` loop; if not, we start all over again:

```         placeOK = canStartHere(tempBall);
}```

The `canStartHere()` function is very simple. It looks through the `ball` array, testing the new `tempBall` against all existing balls to see whether they overlap. If they do, the function returns `false`; if not, it returns `true`. To test the overlap, we have created another new function: `hitTestCircle()`:

```function canStartHere(ball) {
var retval = true;
for (var i = 0; i <balls.length; i++) {
if (hitTestCircle(ball, balls[i])) {
retval = false;
}
}
return retval;
}```

### Circle collision detection

The `hitTestCircle()` function performs a circle/circle collision-detection test to see whether the two circles (each representing a ball) passed as parameters to the function are touching. Because we have been tracking the balls by the center `x` and `y` of their location, this is quite easy to calculate. First, the function finds the distance of the line that connects the center of each circle. We do this using our old friend the Pythagorean theorem (A2+B2 = C2). We use the `nextx` and `nexty` properties of the ball because we want to test the collision before it occurs. (Again, if we test after by using the current `x` and `y` locations, there is a good chance the balls will get stuck together and spin out of control.) We then compare that `distance` value to the sum of the radius of each ball. If the distance is less than or equal to the sum of the radii, we have a collision. This is a very simple and efficient way to test collisions, and it works especially well with collisions among balls in 2D:

```function hitTestCircle(ball1,ball2) {
var retval = false;
var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;
var distance = (dx * dx + dy * dy);
retval = true;
}
return retval;
}```

Figure 5-9 illustrates this code.

### Separating the code in drawScreen()

The next thing we want to do is simplify `drawScreen()` by separating the code into controllable functions. The idea here is that to test collisions correctly, we need to make sure some of our calculations are done in a particular order. We like to call this an update-collide-render cycle.

`update()`

Sets the `nextx` and `nexty` properties of all the balls in the `balls` array.

`testWalls()`

Tests to see whether the balls have hit one of the walls.

`collide()`

Tests collisions among balls. If the balls collide, updates `nextx` and `nexty`.

`render()`

Makes the `x` and `y` properties for each ball equal to `nextx` and `nexty` respectively, and then draws them to the canvas.

And here is the code:

```function  drawScreen () {

update();
testWalls();
collide();
render();

}```

### Updating positions of objects

The `update()` function loops through all the balls in the `balls` array, and updates the `nextx` and `nexty` properties with the `x` and `y` velocity for each ball. We don’t directly update `x` and `y` here, because we want to test collisions against walls and other balls before they occur. We will use the `nextx` and `nexty` properties for this purpose:

```function update() {
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.nextx = (ball.x += ball.velocityx);
ball.nexty = (ball.y += ball.velocityy);
}
}```

### Better interaction with the walls

We discussed the interactions between balls and walls in the last example, but there is still one issue. Since we move the balls by the `x` and `y` location of their center, the balls would move halfway off the canvas before a bounce occurred. To fix this, we add or subtract the `radius` of the `ball` object, depending on which walls we are testing. For the right side and bottom of the canvas, we add the radius of the ball when we test the walls. In this way, the ball will appear to bounce exactly when its edge hits a wall. Similarly, we subtract the radius when we test the left side and the top of the canvas, so that the ball does not move off the side before we make it bounce off a wall:

```function testWalls() {
var ball;
var testBall;

for (var i = 0; i <balls.length; i++) {
ball = balls[i];

ball.velocityx = ball.velocityx*−1;

} else if (ball.nextx-ball.radius < 0 ) {
ball.velocityx = ball.velocityx*−1;

} else if (ball.nexty+ball.radius > theCanvas.height ) {
ball.velocityy = ball.velocityy*−1;

} else if(ball.nexty-ball.radius < 0) {
ball.velocityy = ball.velocityy*−1;
}

}

}```

### Collisions with balls

The `collide()` function tests to see whether any balls have hit one another. This function uses two nested loops, both iterating through the `balls` array to ensure we test each ball against every other ball. We take the ball from the first loop of the `balls` array, and place it into the `ball` variable. Then we loop through `balls` again, placing each ball in the `testBall` variable, one at a time. When we have both `ball` and `testBall`, we make sure they are not equal to one another. We do this because a ball will always have a false positive collision if we test it against itself. When we are sure they are not the same ball, we call `hitTestCircle()` to test for a collision. If we find one, we call `collideBalls()`, and then all hell breaks loose. (OK, not really, but the balls do collide, and some really interesting code gets executed.) See that code here:

```function collide() {
var ball;
var testBall;
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
for (var j = i+1; j < balls.length; j++) {
testBall = balls[j];
if (hitTestCircle(ball,testBall)) {
collideBalls(ball,testBall);
}
}
}
}```

### Ball collisions in depth

So now we get to the most interesting code of this example. We are going to update the properties of each ball so they appear to bounce off one another. Recall that we use the `nextx` and `nexty` properties because we want to make sure to test where the balls will be after they are drawn—not where they are right now. This helps keep the balls from overlapping in a way that will make them stick together.

### Note

Sometimes the balls will still stick together. This is a common problem when creating collisions among balls. This happens when balls overlap one another before the collision test, and the reaction bounce is not enough to split them apart completely. We have made every attempt to optimize this function for the canvas, but we are sure further optimizations are possible.

The `collideBalls()` function takes two parameters: `ball1` and `ball2`. Both parameters are the `ball` objects that we want to make collide:

`function collideBalls(ball1,ball2) {`

First, we need to calculate the difference between the center points of each ball. We store this as `dx` and `dy` (difference x and difference y). This should look familiar because we did something similar when we tested for a collision between the balls. The difference is that now we know they have collided, and we want to know how that collision occurred:

```var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;```

To do this, we need to find the angle of the collision using the `Math.atan2()` function. This function gives us the angle in radians of the collisions between the two balls. This is the line of action or angle of collision. We need this value so that we can determine how the balls will react when they collide:

`var collisionAngle = Math.atan2(dy, dx);`

Next, we calculate the velocity vector for each ball given the `x` and `y` velocities that existed before the collision occurred:

```var speed1 = Math.sqrt(ball1.velocityx * ball1.velocityx +
ball1.velocityy *   ball1.velocityy);
var speed2 = Math.sqrt(ball2.velocityx * ball2.velocityx +
ball2.velocityy * ball2.velocityy);```

Then, we calculate angles (in radians) for each ball given its current velocities:

```var direction1 = Math.atan2(ball1.velocityy, ball1.velocityx);
var direction2 = Math.atan2(ball2.velocityy, ball2.velocityx);```

Next, we need to rotate the vectors counterclockwise so that we can plug those values into the equation for conservation of momentum. Basically, we are taking the angle of collision and making it flat so we can bounce the balls, similar to how we bounced balls off the sides of the canvas:

```var velocityx_1 = speed1 * Math.cos(direction1 - collisionAngle);
var velocityy_1 = speed1 * Math.sin(direction1 - collisionAngle);
var velocityx_2 = speed2 * Math.cos(direction2 - collisionAngle);
var velocityy_2 = speed2 * Math.sin(direction2 - collisionAngle);```

We take the `mass` values of each ball and update their `x` and `y` velocities based on the law of conservation of momentum. To find the final velocity for both balls, we use the following formulas:

```velocity1 = ((mass1 - mass2) * velocity1 + 2*mass2 * velocity2) / mass1 + mass2
velocity2 = ((mass2 - mass1) * velocity2 + 2*mass1 * velocity1)/ mass1+ mass2
```

Actually, only the `x` velocity needs to be updated; the `y` velocity remains constant:

```var final_velocityx_1 = ((ball1.mass - ball2.mass) * velocityx_1 +
(ball2.mass + ball2.mass) * velocityx_2)/(ball1.mass + ball2.mass);
var final_velocityx_2 = ((ball1.mass + ball1.mass) * velocityx_1 +
(ball2.mass - ball1.mass) * velocityx_2)/(ball1.mass + ball2.mass);

var final_velocityy_1 = velocityy_1;
var final_velocityy_2 = velocityy_2```

After we have our final velocities, we rotate our angles back again so that the collision angle is preserved:

```ball1.velocityx = Math.cos(collisionAngle) * final_velocityx_1 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_1;
ball1.velocityy = Math.sin(collisionAngle) * final_velocityx_1 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_1;
ball2.velocityx = Math.cos(collisionAngle) * final_velocityx_2 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_2;
ball2.velocityy = Math.sin(collisionAngle) * final_velocityx_2 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_2;```

Now, we update `nextx` and `nexty` for both balls so can use those values in the `render()` function—or, for another collision:

```   ball1.nextx = (ball1.nextx += ball1.velocityx);
ball1.nexty = (ball1.nexty += ball1.velocityy);
ball2.nextx = (ball2.nextx += ball2.velocityx);
ball2.nexty = (ball2.nexty += ball2.velocityy);
}```

### Note

If this is confusing to you, you are not alone. It took some serious effort for us to translate this code from other sources into a working example on HTML5 Canvas. The code here is based on “Flash Lite Effort - Embedded Systems and Pervasive Computing Lab” by Felipe Sampaio, available here: http://wiki.forum.nokia.com/index.php/Collision_for_Balls. It is also partly based on Jobe Makar and Ben Winiarczyk’s work in Macromedia Flash MX 2004 Game Design Demystified, and Keith Peters’ books on ActionScript animation.

Here is the full code listing for Example 5-7.

Example 5-7. Balls with simple interactions

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX7: Balls With Simple Interactions</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);

update();
testWalls();
collide();
render();

}

function update() {
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.nextx = (ball.x += ball.velocityx);
ball.nexty = (ball.y += ball.velocityy);
}

}

function testWalls() {
var ball;
var testBall;

for (var i = 0; i <balls.length; i++) {
ball = balls[i];

ball.velocityx = ball.velocityx*−1;

} else if (ball.nextx-ball.radius < 0 ) {
ball.velocityx = ball.velocityx*−1;

} else if (ball.nexty+ball.radius > theCanvas.height ) {
ball.velocityy = ball.velocityy*−1;

} else if(ball.nexty-ball.radius < 0) {
ball.velocityy = ball.velocityy*−1;
}

}

}

function render() {
var ball;
context.fillStyle = "#000000";
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x = ball.nextx;
ball.y = ball.nexty;

context.beginPath();
context.closePath();
context.fill();
}

}

function collide() {
var ball;
var testBall;
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
for (var j = i+1; j < balls.length; j++) {
testBall = balls[j];
if (hitTestCircle(ball,testBall)) {
collideBalls(ball,testBall);
}
}
}
}

function hitTestCircle(ball1,ball2) {
var retval = false;
var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;
var distance = (dx * dx + dy * dy);
retval = true;
}
return retval;
}

function collideBalls(ball1,ball2) {

var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;

var collisionAngle = Math.atan2(dy, dx);

var speed1 = Math.sqrt(ball1.velocityx * ball1.velocityx +
ball1.velocityy * ball1.velocityy);
var speed2 = Math.sqrt(ball2.velocityx * ball2.velocityx +
ball2.velocityy * ball2.velocityy);

var direction1 = Math.atan2(ball1.velocityy, ball1.velocityx);
var direction2 = Math.atan2(ball2.velocityy, ball2.velocityx);

var velocityx_1 = speed1 * Math.cos(direction1 - collisionAngle);
var velocityy_1 = speed1 * Math.sin(direction1 - collisionAngle);
var velocityx_2 = speed2 * Math.cos(direction2 - collisionAngle);
var velocityy_2 = speed2 * Math.sin(direction2 - collisionAngle);

var final_velocityx_1 = ((ball1.mass - ball2.mass) * velocityx_1 +
(ball2.mass + ball2.mass) * velocityx_2)/(ball1.mass + ball2.mass);
var final_velocityx_2 = ((ball1.mass + ball1.mass) * velocityx_1 +
(ball2.mass - ball1.mass) * velocityx_2)/(ball1.mass + ball2.mass);

var final_velocityy_1 = velocityy_1;
var final_velocityy_2 = velocityy_2;

ball1.velocityx = Math.cos(collisionAngle) * final_velocityx_1 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_1;
ball1.velocityy = Math.sin(collisionAngle) * final_velocityx_1 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_1;
ball2.velocityx = Math.cos(collisionAngle) * final_velocityx_2 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_2;
ball2.velocityy = Math.sin(collisionAngle) * final_velocityx_2 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_2;

ball1.nextx = (ball1.nextx += ball1.velocityx);
ball1.nexty = (ball1.nexty += ball1.velocityy);
ball2.nextx = (ball2.nextx += ball2.velocityx);
ball2.nexty = (ball2.nexty += ball2.velocityy);
}

var numBalls = 200 ;
var maxSize = 15;
var minSize = 5;
var maxSpeed = maxSize+5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempvelocityx;
var tempvelocityy;

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

for (var i = 0; i < numBalls; i++) {
var placeOK = false;
while (!placeOK) {
tempSpeed = 4;
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

speed:tempSpeed, angle:tempAngle, velocityx:tempvelocityx,
placeOK = canStartHere(tempBall);
}
balls.push(tempBall);
}

function canStartHere(ball) {
var retval = true;
for (var i = 0; i <balls.length; i++) {
if (hitTestCircle(ball, balls[i])) {
retval = false;
}
}
return retval;
}
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>```

Now, when you execute Example 5-7 (CH5EX7.html), you will see a bunch of balls of the same size and mass bumping off of each other and the walls of the canvas, as shown in Figure 5-10. When you look at this demo, imagine all the ways you could modify it to do different things. You could create balls with different masses and different speeds, or even create balls that don’t move but simply alter the direction of other balls that hit them. In Example 5-8, we will take a slightly different look at this same code and add some new properties to make it more interesting.

## Multiple Balls Bouncing with Friction

If we want the balls to slow down and eventually stop, we need to add friction to Example 5-7. For our purposes, simple friction is a value we use to modify the velocity of our objects every time they are drawn to the canvas.

In `canvasApp()`, we now want to create balls of various sizes. In the previous example, the balls were all the same size. It worked, but having balls of different sizes with different masses will create more interesting effects. To do this, we set `minSize` to `3` and `maxSize` to `12`, meaning the radii for our balls will range from `3` to `12` pixels. We also add a new property named `friction`. This is a global property, so it will not be applied to each individual ball. We set it to `.01`, which means our balls will degrade their `x` and `y` velocities by `.01` pixels per frame (every time `drawScreen()` is called):

```var numBalls = 50 ;
var maxSize = 12;
var minSize = 3;
var maxSpeed = maxSize+5;
var friction = .01;```

We will now allow for various ball sizes. The mass of each ball will be different, and balls will have different effects on one another depending on their sizes. Recall that in Example 5-7 we needed a `mass` property so we could calculate conservation of momentum when the balls collided. We are doing the same thing here, but now the masses are different depending on the size:

```for (var i = 0; i < numBalls; i++) {

In `update()`, we apply the `friction` value by calculating the product of the current velocity multiplied by friction, and then subtracting that value from the current velocity. We do this for both the `x` and `y` velocities. Why must we do this instead of simply subtracting the friction value from the `x` and `y` velocities? Because the `x` and `y` velocities are not always proportional to each other. If we simply subtract the friction, we may alter the velocity unintentionally. Instead, we need to subtract a value for the friction that is proportional to the velocity itself, and that value is the product of the velocity multiplied by the `friction` value. This method will give you a smooth degradation of the velocity when the `friction` value is applied:

```function update() {
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
//Friction
ball.velocityx = ball.velocityx - ( ball.velocityx*friction);
ball.velocityy = ball.velocityy - ( ball.velocityy*friction);

ball.nextx = (ball.x += ball.velocityx);
ball.nexty = (ball.y += ball.velocityy);
}

}```

You can see the full version of this code by executing CH5EX8.html from the code distribution, or by typing in Example 5-8. You should notice that the smaller balls have less of an effect on the larger balls when they collide, and vice versa. Also, the balls slow down as they move due to the applied friction.

Example 5-8. Balls with friction

```<!doctype html>
<html lang="en">
<meta charset="UTF-8">
<title>CH5EX8: Balls With 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);

update();
testWalls();
collide();
render();

}

function update() {
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
//Friction
ball.velocityx = ball.velocityx - ( ball.velocityx*friction);
ball.velocityy = ball.velocityy - ( ball.velocityy*friction);

ball.nextx = (ball.x += ball.velocityx);
ball.nexty = (ball.y += ball.velocityy);
}

}

function testWalls() {
var ball;
var testBall;

for (var i = 0; i <balls.length; i++) {
ball = balls[i];

ball.velocityx = ball.velocityx*−1;

} else if (ball.nextx-ball.radius < 0 ) {
ball.velocityx = ball.velocityx*−1;

} else if (ball.nexty+ball.radius > theCanvas.height ) {
ball.velocityy = ball.velocityy*−1;

} else if(ball.nexty-ball.radius < 0) {
ball.velocityy = ball.velocityy*−1;
}

}

}

function render() {
var ball;

context.fillStyle = "#000000";
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
ball.x = ball.nextx;
ball.y = ball.nexty;

context.beginPath();
context.closePath();
context.fill();
}

}

function collide() {
var ball;
var testBall;
for (var i = 0; i <balls.length; i++) {
ball = balls[i];
for (var j = i+1; j < balls.length; j++) {
testBall = balls[j];
if (hitTestCircle(ball,testBall)) {
collideBalls(ball,testBall);
}
}
}
}

function hitTestCircle(ball1,ball2) {
var retval = false;
var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;
var distance = (dx * dx + dy * dy);
retval = true;
}
return retval;
}

function collideBalls(ball1,ball2) {

var dx = ball1.nextx - ball2.nextx;
var dy = ball1.nexty - ball2.nexty;

var collisionAngle = Math.atan2(dy, dx);

var speed1 = Math.sqrt(ball1.velocityx * ball1.velocityx +
ball1.velocityy * ball1.velocityy);
var speed2 = Math.sqrt(ball2.velocityx * ball2.velocityx +
ball2.velocityy * ball2.velocityy);

var direction1 = Math.atan2(ball1.velocityy, ball1.velocityx);
var direction2 = Math.atan2(ball2.velocityy, ball2.velocityx);

var velocityx_1 = speed1 * Math.cos(direction1 - collisionAngle);
var velocityy_1 = speed1 * Math.sin(direction1 - collisionAngle);
var velocityx_2 = speed2 * Math.cos(direction2 - collisionAngle);
var velocityy_2 = speed2 * Math.sin(direction2 - collisionAngle);

var final_velocityx_1 = ((ball1.mass - ball2.mass) * velocityx_1 +
(ball2.mass + ball2.mass) * velocityx_2)/(ball1.mass + ball2.mass);
var final_velocityx_2 = ((ball1.mass + ball1.mass) * velocityx_1 +
(ball2.mass - ball1.mass) * velocityx_2)/(ball1.mass + ball2.mass);

var final_velocityy_1 = velocityy_1;
var final_velocityy_2 = velocityy_2;

ball1.velocityx = Math.cos(collisionAngle) * final_velocityx_1 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_1;
ball1.velocityy = Math.sin(collisionAngle) * final_velocityx_1 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_1;
ball2.velocityx = Math.cos(collisionAngle) * final_velocityx_2 +
Math.cos(collisionAngle + Math.PI/2) * final_velocityy_2;
ball2.velocityy = Math.sin(collisionAngle) * final_velocityx_2 +
Math.sin(collisionAngle + Math.PI/2) * final_velocityy_2;

ball1.nextx = (ball1.nextx += ball1.velocityx);
ball1.nexty = (ball1.nexty += ball1.velocityy);
ball2.nextx = (ball2.nextx += ball2.velocityx);
ball2.nexty = (ball2.nexty += ball2.velocityy);
}
var numBalls = 50 ;
var maxSize = 12;
var minSize = 3;
var maxSpeed = maxSize+5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempvelocityx;
var tempvelocityy;
var friction = .01;

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

for (var i = 0; i < numBalls; i++) {
var placeOK = false;
while (!placeOK) {
tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;

nextx: tempX, nexty:tempY};
placeOK = canStartHere(tempBall);
}
balls.push(tempBall);
}

function canStartHere(ball) {
var retval = true;
for (var i = 0; i <balls.length; i++) {
if (hitTestCircle(ball, balls[i])) {
retval = false;
}
}
return retval;
}
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>```

Figure 5-11 illustrates how this code will look in the browser.

## With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

No credit card required