UniversityExtendedPhysicsCollisionProgramming

How do you code a bouncing ball?

Gravity, a floor and a restitution factor make a bouncing ball — but a naive floor check changes the energy. Handle the impact time exactly, in JavaScript.

How do you code a bouncing ball?

Each physics step does three things: apply gravity to the velocity, move the ball, and — if it went through the floor — reverse the velocity and multiply it by the restitution ee. The catch is the last part: where you put the ball when it crosses the floor decides whether energy is conserved. A common shortcut loses energy even with e=1e = 1.

Key fact
Don't just clamp the ball to the floor and flip its velocity. Solve for the moment of impact inside the step, bounce there, and spend the rest of the step moving away.

This is the code companion to How a bouncing ball works, which explains restitution and the hn=e2nh0h_n = e^{2n}h_0 heights we test against below.

The full loop in about 35 lines

javascript
const G = 9.81;            // m/s²
const E = 0.8;             // restitution
const FIXED_DT = 1 / 120;  // physics step, seconds

const ball = { y: 2, vy: 0 }; // metres above the floor, m/s upward

// Advance under constant gravity for dt seconds — exact, not an approximation.
function fall(b, dt) {
  b.y += b.vy * dt - 0.5 * G * dt * dt;
  b.vy -= G * dt;
}

function step(b, dt) {
  let left = dt;
  while (left > 0) {
    const yEnd = b.y + b.vy * left - 0.5 * G * left * left;
    if (yEnd >= 0) { fall(b, left); return; }        // no impact this step

    // Impact inside the step: solve y + v·τ − ½gτ² = 0 for τ
    const tau = (b.vy + Math.sqrt(b.vy * b.vy + 2 * G * b.y)) / G;
    fall(b, tau);                                     // advance exactly to the floor
    b.y = 0;
    b.vy = -E * b.vy;                                 // bounce
    left -= tau;                                      // spend the remaining time
    if (Math.abs(b.vy) < 0.01) { b.vy = 0; return; }  // resting: stop micro-bounces
  }
}

// Fixed-timestep loop: render at any frame rate, step physics at 120 Hz
let acc = 0;
function frame(frameSeconds) {
  acc += Math.min(frameSeconds, 0.25);  // clamp huge frames (tab was hidden)
  while (acc >= FIXED_DT) { step(ball, FIXED_DT); acc -= FIXED_DT; }
}

Does it match the physics?

Dropping the ball from 22 m with e=0.8e = 0.8 and recording each peak, the code reproduces the geometric heights hn=e2nh0h_n = e^{2n}h_0 to within the sampling of the step:

BounceTheory 2e2n2e^{2n} (m)Code (m)
11.2801.280
20.8190.818
30.5240.524
40.3360.335

Why not just clamp the ball to the floor?

The shortcut is if (y < 0) { y = 0; vy = -e * vy; }. When the ball overshoots the floor by a few millimetres, that snap throws away the height it should have had and adds energy at the wrong moment. Even with a perfectly elastic e=1e = 1, a ball dropped from 22 m should return to 22 m every time. Measured at 30 physics steps per second:

Collision handlingFirst peaks for e = 1 (m)
Clamp and flip1.85, 1.86, 1.66, 1.67, 1.47 — decaying
Mirror the penetration1.92, 2.00, 1.92, 2.00 — wobbling
Exact impact time≈ 2.00 every time
Fast objects can skip the floor entirely
If the ball moves more than its own thickness in one step it can jump straight over a thin surface (tunnelling). Use a smaller step, several sub-steps, or a swept test between the old and new position.

What PhysicsHub does

The Bouncing Ball simulation runs on a fixed 1/120 s step with the shared engine. Its Bounds element compensates for the work gravity does while the ball overlaps the floor inside a step, so restitution 1 really is lossless and the heights match the theory rather than drifting down.

Frequently asked questions

Why use a fixed time step for a bouncing ball?

With a variable step the result depends on the frame rate: a slow computer would bounce differently from a fast one. A fixed physics step, with rendering decoupled from it, gives the same result everywhere.

Why does my ball lose energy even with restitution 1?

Almost always the floor handling. Snapping the ball to y = 0 discards the small overshoot inside the step, so each bounce loses a little height. Solve for the impact time and continue from there.

How do I stop the infinite tiny bounces at the end?

Add a rest threshold: when the rebound speed falls below a small value (for example 0.01 m/s), set the velocity to zero. The physical bounces converge in finite time, but floating-point steps do not.

Can I use this for a ball that also moves sideways?

Yes. Gravity and the floor only affect the vertical component, so keep a horizontal velocity that does not change (or that you damp with friction) and handle each axis separately.

Keep exploring