Suppose you wish to make a particle effect where the particles are constrained to stay in a large circle, and when they hit the boundary of the circle they should bounce off. Bouncing off a circular boundary is rather straightforward if you understand some basic vector algebra. Click here or on the screenshot below to see a simple particle example, then read below to learn more about the mathematics behind it.

Circular Boundary screencap

Download

the complete code can be downloaded here: CircleBoundary.zip

About the code

The code is well-commented, and you can download it and check it out for yourself. But the calculations for the boundary bouncing may be a little mysterious, so I explain it below along with some of the mathematics behind it.

The basic particle code in the example above is similar to my earlier experiments: particles are defined by abstract JavaScript objects, retaining parameters for their position and velocity. On every refresh of the screen, the particle velocities are updated; here I am adding a small randomized acceleration amounts to change the particle trajectories as they fly around. After updating positions, testing for collisions with the boundary and recalculating as necessary, we draw the particles to the canvas using a sprite sheet. This sprite sheet, which is created at runtime, is similar to those used in my earlier experiments (for example, 2D here and 3D here) and allows for faster drawing of the particles compared to repeated graphics commands. See my post HTML5 Canvas – A Simple Sprite Sheet Example for more details about creating and copying from such a sprite sheet.

Bouncing off the (circular) walls

A classic particle effect constrains particles to stay inside a rectangle. When a particle collides with the border of a rectangle (that is, its updated position is out of the rectangle bounds), creating the bouncing effect is simple: you simply need to place the particle back inside the rectangle and negate either the x or y component of the velocity. Bouncing off a circle is similar, except finding the part of the velocity that needs to be negated is a little more involved.

A 2D vector such as velocity can always be thought of as formed out of two separate components. A typical decomposition is into x and y components, but that is not the only way to break down vectors. When bouncing off a wall we need to consider what part of the velocity is perpendicular (normal) to the wall, and what part is parallel (tangent) to the wall. The normal part of the velocity should be negated, but the tangential part should be left alone.

Finding these normal and tangential componenents requires a little bit of vector algebra. What we are interested in finding is called the projection of one vector onto another. In the diagram below, we show two vectors \(\mathbf{a}\) and \(\mathbf{b}\), and the projection of \(\mathbf{a}\) onto \(\mathbf{b}\):

projection vector

This projection vector can be computed in terms of something called a dot product: the dot product of two 2D vectors \(\mathbf{a} = \langle x_1,y_1 \rangle \) and \(\mathbf{b} = \langle x_2,y_2 \rangle\) is the quantity
\[
\mathbf{a} \cdot \mathbf{b} = x_1x_2 + y_1y_2.
\]
Because of a nice geometric property of the dot product, it can be used to find projection vectors:
\[
\mathrm{\textbf{proj}}_\mathbf{b}(\mathbf{a}) = \left(\frac{\mathbf{a}\cdot \mathbf{b}}{\mathbf{b}\cdot \mathbf{b}}\right)\mathbf{b}
\]
In the case of our particle colliding with a circular boundary, we are interested in finding the normal component of the velocity. As shown below, suppose the particle is at position \(P\), right on the circle, and it has velocity \(\mathbf{v}\).

circular bounce vectors

The normal component of the velocity is \(\mathrm{\textbf{proj}}_\mathbf{N}(\mathbf{v})\), and the tangential part is
\(
\mathbf{v} – \mathrm{\textbf{proj}}_\mathbf{N}(\mathbf{v}).
\) We negate the normal part but leave the tangential component unchanged, obtaining a new velocity vector
\[
\mathbf{v}^{\prime} = \mathbf{v} – 2\;\mathrm{\textbf{proj}}_\mathbf{N}(\mathbf{v}).
\]
Finding the normal vector on a circle is easy. If the circle is centered at the origin, then a normal vector at any point \((x,y)\) is given by the “position vector” \(\langle x, y \rangle\).

One more important bit of math we will use below is that any vector dotted with itself produces the square of the length of the vector:
\[
\mathbf{a} \cdot \mathbf{a} = ||\mathbf{a}||^2,
\]
where \(||\mathbf{a}||\) represents the length (magnitude) of a vector \(\mathbf{a}\). Note that the dot product of a vector with itself appears in the projection formula above.

Writing the code

When we update the x and y coordinates of a particle p, we must first determine if a collision with the walls has occurred. In the case of a boundary circle centered at the origin, this is easy to test: we simply see if the distance from the particle to the center is more than the radius of the circle (actually, to avoid a square root in the distance formula, we compare squares). If the particle is out of bounds, we must place it back inside the circle and adjust its velocity. A simple way to place the particle back inside is to find the midpoint between the last (interior) position and the new (out of bounds) position, and place the particle there. But to ensure that the particle is placed inside the circle, we scale this position to have the correct length. This positioning is only approximate, but it is fine for our purposes. So our boundary collision code begins as follows:

radSquare= p.x*p.x + p.y*p.y;
if (radSquare > boundaryRadSquare) {				
    //find intersection point with circle. simple method: midpoint
    exitX = (lastX + p.x)/2;
    exitY = (lastY + p.y)/2;

    //scale to proper radius
    exitRad = Math.sqrt(exitX*exitX + exitY*exitY);
    exitX *= boundaryRad/exitRad;
    exitY *= boundaryRad/exitRad;

    //place particle there
    p.x = exitX;
    p.y = exitY;

Now we must set the velocity using the mathematics we outlined above. First note that for our normal vector we will simply be using our position vector, which is now \(\langle \mathrm{exitX}, \mathrm{exitY} \rangle\). Also, because this point is on the circle, when we dot it with itself we obtain the square of the radius of the circle (see the note above concerning the dot product of a vector with itself).

We now complete the boundary collision code by computing the new velocity \(\mathbf{v} – 2\mathrm{\textbf{proj}}_\mathbf{N}(\mathbf{v})\):

    twiceProjFactor = 2*(exitX*p.velX
                       + exitY*p.velY)/boundaryRadSquare;
    vx = p.velX - twiceProjFactor*exitX;
    vy = p.velY - twiceProjFactor*exitY;
    p.velX = vx;
    p.velY = vy;
}

That’s all there is to it. The actual calculations are short, but the ideas behind them take a little while to explain!

After the particle positions are updated, they are drawn to the screen using an offscreen sprite sheet which contains an image of one typical particle. To add to the aesthetics of the effect, I have set the globalCompositeOperation to “lighter,” so the particles will additively blend where they overlap.

Acknowledgment

The mathematics was typset in this blog post using the open source JavaScript display engine MathJax.