Impulse-Based Collision Response

Your collision detector has just told you two things touched: a ball and a floor, at a contact point, along a contact normal \mathbf{n}, overlapping by a small penetration depth. Now what? The naive instinct is to push them apart with a force. But a real contact happens in essentially zero time — a finite force acting over zero time changes nothing (\Delta \mathbf{p} = \mathbf{F}\,\Delta t \to 0). To flip a velocity instantly we need a force that is infinite for an instant, and the only sane way to talk about that is its integral: an impulse.

An impulse is a sudden, finite change of momentum, \mathbf{j} = \Delta \mathbf{p}, delivered along the normal. This page is the engine of every game physics response step: how big an impulse to apply, how bouncy to make the result, how spin and friction enter, and how to keep a stack of boxes from sinking through the floor. It builds on momentum and collisions and the wider computer-animation tree.

The one number that sets bounciness: restitution

Before any formula, meet the star of the show. The coefficient of restitution e is a single dimensionless number, e \in [0, 1], that answers "how much of the closing speed survives as separating speed?" It relates the relative velocity after the collision to the relative velocity before, measured along the normal:

v_{\text{rel}}^{\,+} = -\,e\,v_{\text{rel}}^{\,-},

where v_{\text{rel}} = \mathbf{n}\cdot(\mathbf{v}_A - \mathbf{v}_B) is the normal component of relative velocity (a superscript - is pre-collision, + is post-collision). The minus sign flips approach into separation.

The normal-impulse formula

We want a scalar impulse magnitude j such that applying \mathbf{j} = j\,\mathbf{n} to body A (and -j\,\mathbf{n} to body B, by Newton's third law) produces exactly the desired post-collision normal velocity -e\,v_{\text{rel}}^{-}. Each body's velocity changes by \Delta \mathbf{v} = \mathbf{j}/m. Working the algebra through for two bodies with masses m_A, m_B gives the classic result:

j = \frac{-(1 + e)\,v_{\text{rel}}^{-}}{\dfrac{1}{m_A} + \dfrac{1}{m_B}}.

The denominator is the combined inverse mass: a heavier pair resists the impulse. Fixing an object in place — an immovable floor or wall — is just the limit m_B \to \infty, i.e. 1/m_B = 0, so the whole impulse acts on the moving body. (Storing 1/m as "inverse mass" makes static objects a clean 0 rather than a division by infinity — that's why engines store inverse mass, not mass.)

Off-centre hits impart spin: the angular terms

Real bodies rotate. If the contact point is not on the line through the centre of mass, the same impulse also produces a torque, and the object gains angular velocity — an off-centre whack spins the object. Let \mathbf{r}_A be the vector from body A's centre of mass to the contact point. The impulse changes the angular velocity through the inertia tensor I:

\Delta \boldsymbol{\omega}_A = I_A^{-1}\,(\mathbf{r}_A \times \mathbf{j}).

Because the point of contact is now also moving from the body's spin, the "effective mass" seen along the normal picks up rotational terms. The full two-body normal impulse becomes:

j = \frac{-(1+e)\,v_{\text{rel}}^{-}}{\dfrac{1}{m_A} + \dfrac{1}{m_B} + \mathbf{n}\cdot\big(I_A^{-1}(\mathbf{r}_A\times\mathbf{n})\times\mathbf{r}_A\big) + \mathbf{n}\cdot\big(I_B^{-1}(\mathbf{r}_B\times\mathbf{n})\times\mathbf{r}_B\big)}.

Set the rotational terms to zero (point-masses, or a central hit) and this collapses back to the tidy linear formula above. The relative velocity v_{\text{rel}} must now be measured at the contact point, including each body's \boldsymbol{\omega}\times\mathbf{r} surface velocity.

Worked example: a ball hits the ground

Drop a ball of mass m = 0.5\ \text{kg} onto a rigid floor. Just before contact it is moving down at 6\ \text{m/s}, so with an upward normal \mathbf{n} the pre-collision normal velocity is v^{-} = -6\ \text{m/s}. The floor is immovable (1/m_B = 0) and central, so no spin. Take restitution e = 0.8.

Post-collision velocity comes straight from the restitution law: v^{+} = -e\,v^{-} = -0.8 \times (-6) = +4.8\ \text{m/s} — the ball rebounds upward at 4.8\ \text{m/s}, having lost 1.2\ \text{m/s} of speed to the bounce. In one clean stroke, v' = -e\,v.

The impulse that achieved it: j = \frac{-(1+e)\,v^{-}}{1/m} = -(1+0.8)(-6)(0.5) = 5.4\ \text{kg·m/s}. Check: \Delta v = j/m = 5.4/0.5 = 10.8\ \text{m/s}, and indeed -6 + 10.8 = +4.8\ \text{m/s}. The impulse turned a 6\,\text{m/s} descent into a 4.8\,\text{m/s} rebound in a single instant — exactly what a force never could over zero time.

Sideways too: the friction impulse

The normal impulse handles bouncing. But contacts also grip sideways. We apply a second, tangential impulse j_t along the surface (perpendicular to \mathbf{n}) that tries to kill the relative sliding velocity — this is what makes a ball roll instead of skid, and lets a box come to rest on a slope. But friction is not unlimited. The Coulomb model caps it in proportion to how hard the surfaces press together:

|j_t| \le \mu\,j_n,

where \mu is the friction coefficient and j_n the normal impulse we just computed. If the tangential impulse needed to fully stop the sliding is within the cone (\le \mu j_n), the contact sticks (static friction, no slide). If it would exceed the cap, we clamp it to \mu j_n and the surfaces slide (kinetic friction). Harder normal press → more grip: the same reason a heavy crate is harder to shove than a light one.

Many contacts at once: sequential impulses

A single bounce is easy. A stack of boxes, or a pile of debris, is not — every box touches its neighbours, and each contact's correct impulse depends on all the others (push the top box down and the bottom contact must push back harder). Solving that coupled system exactly is expensive. The trick that powers modern engines (Box2D, Bullet, PhysX) is sequential impulses: don't solve it all at once — iterate.

The beauty is that it's cheap, incremental, and degrades gracefully: run more iterations for a tall, wobbly stack; fewer for a couple of loose objects. Accumulate each contact's total impulse across iterations and clamp the accumulation (impulses can only push, never pull) for stability.

Fixing the overlap: positional correction

Impulses fix velocities, but by the time we detect a collision the objects usually already overlap a little (they moved into each other during the timestep). Left alone, that penetration lingers and objects visibly sink. The fix must nudge them apart without secretly injecting energy (or the sim gains speed from nowhere). Two standard cures:

Both correct only a fraction of the penetration per frame (a "slop" tolerance leaves a tiny overlap unresolved) so resting contacts don't buzz. The velocity solver makes things bounce right; the positional solver makes them sit right.

Interactive: how restitution eats bounce height

Each bounce keeps a fraction e of the vertical speed. Since the height a ball reaches goes as the square of its launch speed (h \propto v^2), each bounce keeps e^2 of the height. After n bounces:

h_n = e^{\,2n}\,h_0 \quad\Rightarrow\quad \text{a geometric decay with ratio } e^2.

Drag the e slider and watch the middle curve. Near e = 1 it barely droops (a superball bounces for ages); near e = 0.5 it collapses almost at once (each bounce keeps only a quarter of the height). The faint reference curves are e = 0.9 and e = 0.5.

The starting height is h_0 = 1. Notice the decay is geometric, not linear: the drop from bounce 0 to 1 dwarfs the drop from bounce 5 to 6 — which is exactly why a real ball's bounces get rapidly closer together in time and it seems to "buzz" to a stop.

Because they answer two different physical questions. The impulse answers "what should the velocities become so the objects separate at the right speed?" — a genuine dynamics question with real momentum exchange. The penetration, by contrast, is a numerical artefact: it only exists because we advanced time in a finite step and let the objects overlap. If you tried to fix the overlap by adding velocity, you'd be injecting momentum the physics never called for — the objects would ping apart faster each frame. Splitting the two (a "split impulse") lets you shove bodies out of overlap geometrically while leaving their real velocities untouched. It's the difference between correcting a bookkeeping error and changing the actual bank balance.

Two classic ways to blow up your simulation:

Restitution above 1. The formula happily accepts e > 1, and it will faithfully make each bounce come back faster than it arrived — the ball climbs higher every time, kinetic energy grows without bound, and within a few seconds the object rockets off the screen. An e > 1 is a perpetual-motion machine. Always clamp e \in [0, 1]; energy is conserved only at e = 1 and lost for anything less.

One pass over a stack. Resolving contacts one at a time with a single sweep leaves a tall stack unsolved: fixing the top contact un-fixes the bottom one, so boxes sink into each other, jitter, and slowly settle. The cure is not a bigger impulse — it's iteration. Run the sequential-impulse loop several times per frame so the coupled contacts converge together. A stack that sinks or vibrates is almost always starved of solver iterations.