Animate your web apps using physically based bouncing.
Bounce effects, where an object falls under the influence of gravity, hitting the ground, losing energy, and rebounding, are ubiquitous in games, and are used somewhat on the web. However, most bounce effects on the web are repetitions of the same easing curves, which makes them feel forced. If you set out to create your own bounce, it’s a bit trickier than you might expect as not only does the height of each oscillation change, but also the frequency of the bounces as the object loses energy. [1]
Here, I describe the mathematics and techniques in javascript for creating a bounce effect a designer can control parametrically using a set duration and number of bounces.
In The Spring Factory, we needed to work with the solution to a differential equation. Here we only need some freshman physics. Other tutorials that show how to create bounce effects in other media often focus on two or dimensional motion (e.g. how to make projectile, such as a ball or bird, bounce). Here, I’ll just discuss the vertical dimension of the motion in detail as the typical implementation of the horizontal component is often very simple. [2]
A basket ball is held above the ground (held to be 0) at some height. It is released and falls to Earth. Upon striking the ground [3], contact friction, flying dirt, and internal deformation steal energy from the ball, and as it springs upward, it reaches a fractionally reduced height after each strike.
To describe the motion of the ball, we’ll need a way to describe the ball’s position, its kinetic and potential energy, and the way the ground steals energy from it.
Up front, it can be seen that this will be a bit trickier than simply solving a single equation because each time the ball hits the ground it creates a discontinuity. One way to tackle this is to run a real time simulation of the trajectory. However, this can be limiting as it makes doing things like reversing the animation or jumping in midway more difficult.[4] Instead, we’ll focus on precomputing the critical points, the points in the animation where the ball strikes the ground, and use a separately parameterized equation for each segment of the animation.
First things first. Let’s show how a falling object behaves. Equation 1 describes the motion of an object under constant acceleration. Our bouncing ball will consist of several of these parabolic arcs. Figure 2 shows how releasing a ball from rest one meter above the surface of Earth will behave. Figure 3 shows what a ball bouncing in place might look like as it loses energy after each bounce.
Equation 1: Position of a falling object accelerated only by gravity. The first term is initial height, the second describes the motion provided by the initial velocity, and the third describes how the position is influenced by gravity. y is height above ground, v is initial velocity, g is gravitational acceleration, and t is time. In the real world, gravitational acceleration is known to be appx. -9.81 meters per second per second near Earth’s surface.
Figure 1: A ball is released at rest one meter above the ground and reaches the ground in about 0.445 seconds. Note that the arc is parabolic. Despite the labeling of y and x, the y axis is height above ground, and the x axis is time.
Figure 2: Here’s our bouncing ball. Several arcs put together, each reduced in height by, in this case, 15%. In our final version, the axes will both be scaled to be between 0 and 1. Note the discontinuities at each contact point. y = height, x = time
A bouncing ball gradually comes to a rest. It does so by releasing its energy into the environment after each bounce. There are two obvious ways to model this, and I’m not sure which is “correct” as physical processes are often complex, but the one that makes sense no matter how big a bounce you make is to fractionally reduce the energy of the ball after each bounce. For example, the ground steals 15% of the ball’s energy each time it lands. This method will look good for any height, but the disadvantage is in the fact that the energy will never reach 0, so we’ll need to set a threshold height below which the ball comes to a rest or a time limit on the animation. The other method is to subtract a constant amount of energy after each bounce, which will naturally resolve itself to 0, though you may still need to set a time limit.
That is to say that after each bounce n, the current energy is some fraction α (alpha) (between 0 and 1) of the initial energy. If the ground steals 15% of your energy, α would be (1–0.15) or 0.85. Since this quantity represents how much energy remains rather than how much is lost, let’s call alpha the elasticity.
Equation 2: The current energy E_n is the repeated application of a fraction to the initial energy. n is the number of bounces 0, 1, 2, … etc. Alpha is the amount of energy the ground steals,
That was pretty abstract, but now it’s time to get more concrete. I’ll introduce the dual equations for the kinetic and potential energy of the ball. Remember from physics that energy is a special quantity that is neither created or destroyed. Whenever kinetic energy changes, it draws from or gives its power to its potential reservoir. That is, KE + PE = const. When the ball strikes the ground, some energy is absorbed by the ground. That might be confusing, since I just said that energy is not created or destroyed but the ball lost energy. However, the ball-ground system maintains its energy. Technically we could track the energy the ground accumulates, but it’s not really necessary for our purposes.
Without further ado, here are our equations:
Equation 3: Kinetic energy of an object with mass m moving at velocity v.
Equation 4: Potential energy of an object with mass m held a height h above the ground.
Conveniently, if we know where the top of the arc is located, we can compute the total energy using the potential energy equation. If we want to know how much velocity the ball has right upon or right after striking the ground, we can solve for that using the kinetic energy equation.
In the “The Strategy” above, I discussed computing the discontinuous points and projecting from the nearest point to create the animation. For example, if you consult Figure 3, at t = 0.25, we would use the first parabola to perform our projection, but at t = 0.75 we would use the second. In order to do this, you need to know where the discontinuities are and what energy remains after the bounce. Each discontinuity is not only a transition to a new parabola, but a shorter parabola in both height and time.
So how do we compute where the discontinuities are? Luckily, we can compute the termination point of the parabolas using a combination of the equations above. Notably, the first bounce is different to compute than the later ones as the ball starts half way through its arc, at the peak.
If you take Equation 1, set y = 0, and solve for t, you’ll get the half bounce time. Since the ball starts at rest at a height h above the ground, the initial position is h and the velocity term is 0.
Equation 5: T/2 is the half bounce time. h is the original height above the ground, g is the acceleration due to gravity.
We can now iteratively project forward by finding computing the height of each arc and then using that to find the next discontinuity. There is a small trick to be had with the first bounce though, because it doesn’t begin on the ground. Our first discontinuity is actually located at -T/2, which is before the starting point of the animations, but we can still use it to project the position at 0.
So what is the height of the next bounce? Using Eqn. 2 and Eqn. 4, you can see that E1 = α E0, and E0 = mgh. Therefore, E1 = α_mgh_. This looks like it has a lot of variables, but we have a few tricks to simplify this. Firstly, α is the elasticity chosen by your designer, so it is known. Since mass isn’t affected by gravitational acceleration, and there are no collisions with other objects that are affected by mass, we can choose m = 1. Since we would like our easing curve to apply to any situation, we can choose h = 1. The output of our easing curve will represent the percentage of the ball’s displacement, rather than its actual position, making this generalizable to many animations. Lastly, since we plan on letting your designer choose the duration of the animation, gravity is meaningless. Gravity’s strength would ordinarily determine how quickly the object falls, but since we will be directly controlling that using duration, we can set g = 1 as well. Therefore, we have a new way of describing arc height:
Equation 6: Derived relationship of arc height to elasticity.
Which means that we can further derive a bounce time equation in terms of α from Eqn. 5 and Eqn. 6, letting g = 1:
Equation 7: Bounce time of the nth arc in terms of elasticity.
It is now easy to compute the locations of the critical points. In order to project an arc from a critical point, we’ll use Eqn. 1, but counting t = 0 at the location of the point. At the point, we know that the current position is 0, and we have held g = 1. We’ll need to know what v is. Thankfully, we’ve had the foresight to track the ball’s energy across the arcs. At the critical point, all of the potential energy has been converted into kinetic energy as the ball accelerated through the air. To get v, we just need to rearrange Eqn. 3, the kinetic energy equation, using m = 1, g = 1, h = 1 as discussed before, and combine it with Eqn. 2:
Equation 8: Initial velocity of ball just after bouncing
There is now one last equation that we’ll need. We need to compute the number of points we’ll need to calculate since we’re going to need to end our for loop at some point. Given our elasticity, and a threshold, ε (epsilon), beyond which it is acceptable to stop bouncing, we can solve for the number of bounces based on Eqn. 2 and recalling E0 = 1:
Equation 9: Eqn. 2 set equal to the cutoff energy threshold ε. Recall E0 = 1, which is why it is not shown.
Equation 10: Eqn. 9 rearranged using logarithms. The elasticity in terms of the number of bounces, n, and the cutoff threshold (epsilon).
We are now ready to proceed to code. We have three knobs with which to tune our animation:
Once you understand the math above the code below is relatively straightforward. However, there are a few tricks that are going on that warrant some explanation.
Firstly, the function is called the bounceFactory. It isn’t a 90’s music group, rather it produces a bounce function that takes a variable t whose domain is 0…1 based on your specifications. 0 represents the beginning of the animation, and 1 the end. This allows you to feed your animation’s percentage completion into the resultant function and get the proportion of the total displacement of your object for that part of the animation.
The bounceFactory is controlled by two arguments. One required, one optional. The required argument, the number of bounces, corresponding to n in the equations above is self explanatory. The threshold, corresponding to epsilon above, should generally be workable at its default value of 0.1%, but you might decide you want to cut off the bouncing earlier or later, e.g. if your animation is much longer or shorter than about 2 seconds. Reasonable values can go as high as around 10%.
For example, say you wanted to generate an easing function for 5 bounces and stops bouncing once the last bounce reaches a maximum height of 1%:
var easingfn = bounceFactory(5, 0.01);
The second thing to note about this function is that the generated function is a closure on the precomputed bounce points. This saves a lot of work up front, as we only need to find the appropriate point in a sorted array each time it is called rather than compute based on energy and number of bounces. While I haven’t profiled two versions, it seems like a good idea and it fit with my preferred coding style. On a 60Hz animation, recall you only have 16.7 msec to do everything you want to before rendering a given frame.
Third, you’ll notice that the very first point is split into two points. Since the animation starts from the drop point with the object already in the air, we can set the start of the parabolic arc half way back and the termination point half way forward, and compute the rest of the arcs normally.
To see an example of this in action, and paired with a working animation framework, take a look at the JSFiddle. You can manipulate the animation by editing the parameters at the bottom of the JS section.
If you enjoyed this article, please follow me on Medium. You might also like The Sigmoid Factory and The Spring Factory.
[1] http://www.motionscript.com/articles/bounce-and-overshoot.html — This article by Dan Ebberts discusses bouncing animations as a two dimensional simulation for Adobe After Effects. It’s really good, but I prefer to create analytic curves so that the animation is precisely controllable by a single variable.
[2] Often, the horizontal velocity is simply a constant. If you account for air resistance, it will be decreasing in some fashion. You can also try subtracting some energy upon striking the ground.
[3] Air resistance could be accounted for in more sophisticated implementations.
[4] Performing a simulation is advantageous if you’d like multiple objects to interact somewhat chaotically. You can use tricks like saving the state of everything in the frame multiple times a second and then consulting your array of frames when going backwards and forwards, but I think for the common case, animating an element in isolation, an analytic solution is cleaner and is as precise as floating point allows. You’ll see later that by creating an easing curve, we’ll abstract away gravity and mass which will further simplify things. Your designer will have precise control of timing and elasticity without having to fiddle with mass and gravity parameters.