The Inertia Tensor and Angular Velocity

Toss a book into the air — spine held shut with a rubber band — and give it a spin. Flip it about its long axis or its short axis and it spins clean, like a top. But flip it end-over-end about the middle axis and something eerie happens: after half a turn it suddenly flips its face over, wobbles, flips back, over and over, even though nothing touched it. The same trick works with a phone, a tennis racket, or a spinning wingnut floating in a spacecraft. Cosmonaut Vladimir Dzhanibekov filmed exactly this in orbit in 1985, and it spooked mission control.

Nothing about that motion makes sense if you think of rotation the way you think of straight-line motion — where mass is a single number and pushing an object always makes it move the way you push. Rotation is richer. The "mass" of a spinning body is not a number but a matrix: the inertia tensor. Getting it right is the difference between a rigid-body simulator that tumbles convincingly and one where everything spins like a boring gyroscope. This page builds the inertia tensor from scratch, shows why a spinning body's angular momentum and angular velocity usually point in different directions, and explains the flipping book.

Mass tells force how to move; the inertia tensor tells torque how to spin

For straight-line motion, momentum is mass times velocity, \mathbf{p} = m\mathbf{v}. Mass m is a scalar, so \mathbf{p} and \mathbf{v} always point the same way: push a puck and it slides straight along the push. The rotational analogue relates angular velocity \boldsymbol{\omega} (how fast, and about which axis, the body turns) to angular momentum \mathbf{L}:

\mathbf{L} = I\,\boldsymbol{\omega}.

Written out, with the body's angular velocity \boldsymbol{\omega} = (\omega_x, \omega_y, \omega_z):

\begin{pmatrix} L_x \\ L_y \\ L_z \end{pmatrix} = \begin{pmatrix} I_{xx} & I_{xy} & I_{xz} \\ I_{xy} & I_{yy} & I_{yz} \\ I_{xz} & I_{yz} & I_{zz} \end{pmatrix} \begin{pmatrix} \omega_x \\ \omega_y \\ \omega_z \end{pmatrix}.

The diagonal entries (the moments of inertia) say how hard the body resists spinning about each axis; the off-diagonal entries (the products of inertia) are what tilt \mathbf{L} away from \boldsymbol{\omega}. A long thin rod is easy to spin about its length and hard to spin end-over-end — one object, two very different resistances. That is why a single number cannot do the job.

Principal axes: the directions where spinning is clean

A symmetric matrix can always be diagonalised: there is a special set of three perpendicular directions — the principal axes — in which the inertia tensor becomes diagonal,

I = \begin{pmatrix} I_1 & 0 & 0 \\ 0 & I_2 & 0 \\ 0 & 0 & I_3 \end{pmatrix},

with no products of inertia at all. These axes are the eigenvectors of I and the three moments I_1, I_2, I_3 are its eigenvalues. Spin the body about a principal axis and \mathbf{L} = I_k\boldsymbol{\omega} is a plain scalar multiple of \boldsymbol{\omega} — momentum and spin point the same way, the rotation is "clean", and left alone the body spins steadily forever. Off a principal axis, the two vectors diverge and the body wobbles.

Any object with a bit of symmetry hands you its principal axes for free: for a box they are the three directions perpendicular to its faces. This is why simulators love boxes, spheres and capsules — their inertia tensors are diagonal from the start.

Seeing the two arrows diverge

Take a flat, plate-like body whose principal moments differ: I_1 about the horizontal axis is small, I_2 about the vertical axis is larger. Spin it about a direction that is not a principal axis and watch the two vectors. The blue arrow is the spin axis \boldsymbol{\omega} you chose; the orange arrow is the resulting angular momentum \mathbf{L} = I\boldsymbol{\omega}. Because the vertical axis is "heavier", \mathbf{L} is tugged toward it and lags the spin axis. Only when \boldsymbol{\omega} lines up with a principal axis (slider at the far ends) do the arrows coincide.

Worked example: a box spun off its axes

Take a solid box of mass m = 1 with distinct side lengths a = 2 (x), b = 1 (y), c = 3 (z). Its inertia tensor about the centre, in the body frame lined up with the edges, is diagonal with

I_{xx} = \tfrac{1}{12}m(b^2 + c^2) = \tfrac{1}{12}(1 + 9) = 0.833, I_{yy} = \tfrac{1}{12}m(a^2 + c^2) = \tfrac{1}{12}(4 + 9) = 1.083, I_{zz} = \tfrac{1}{12}m(a^2 + b^2) = \tfrac{1}{12}(4 + 1) = 0.417.

Three different numbers — the box resists spinning about each edge differently. Now spin it about a direction wedged between the x and y edges, \boldsymbol{\omega} = (1, 1, 0). The angular momentum is

\mathbf{L} = I\boldsymbol{\omega} = (I_{xx}\cdot 1,\; I_{yy}\cdot 1,\; I_{zz}\cdot 0) = (0.833,\; 1.083,\; 0).

Compare the directions. \boldsymbol{\omega} sits at exactly 45^\circ from the x-axis. But \mathbf{L} sits at \arctan(1.083 / 0.833) \approx 52.4^\circ — a 7.4° gap. The momentum leans toward the y-edge because the box is harder to spin that way. Feed \boldsymbol{\omega} straight down a single edge instead ((1,0,0)) and the gap collapses to zero: \mathbf{L} = (0.833, 0, 0) is parallel to \boldsymbol{\omega}. That is a principal axis at work.

The tensor lives in the body frame — and must be rotated to the world each step

Here is the subtlety that trips up every first rigid-body engine. The inertia tensor I_{\text{body}} is constant in the body's own frame: the mass never moves relative to the object, so its principal moments never change. But the body tumbles in the world, so the tensor as the world sees it changes every frame. If R is the body's current orientation (a rotation matrix taking body coordinates to world), then

I_{\text{world}} = R\, I_{\text{body}}\, R^{\mathsf{T}}.

Reading it right to left: R^{\mathsf{T}} takes a world-frame \boldsymbol{\omega} back into the body, where the constant I_{\text{body}} acts, and R carries the result back out to the world. Because engines actually need \boldsymbol{\omega} from \mathbf{L} (angular momentum is what is conserved and integrated), they store the constant inverse I_{\text{body}}^{-1} once and rebuild the world inverse each step:

I_{\text{world}}^{-1} = R\, I_{\text{body}}^{-1}\, R^{\mathsf{T}}, \qquad \boldsymbol{\omega} = I_{\text{world}}^{-1}\,\mathbf{L}. interface Mat3 { /* 3x3, row-major */ m: number[] } // Stored ONCE at setup: the body-frame inverse inertia (constant, diagonal for a box). const invIbody: Mat3 = diag(1 / Ixx, 1 / Iyy, 1 / Izz); // Every substep: rebuild the world inverse from the current orientation R. function angularVelocity(R: Mat3, L: Vec3): Vec3 { const invIworld = mul(mul(R, invIbody), transpose(R)); // R · I_body⁻¹ · Rᵀ return apply(invIworld, L); // ω = I_world⁻¹ · L }

Storing the inverse in the body frame means the expensive part (the inversion, and often the diagonalisation) happens once, offline; the per-step cost is just two matrix products.

Euler's equations: why a free body tumbles

Write the rotational dynamics in the body's own principal frame and, with no external torque, you get Euler's equations:

\begin{aligned} I_1\dot{\omega}_1 &= (I_2 - I_3)\,\omega_2\,\omega_3, \\ I_2\dot{\omega}_2 &= (I_3 - I_1)\,\omega_3\,\omega_1, \\ I_3\dot{\omega}_3 &= (I_1 - I_2)\,\omega_1\,\omega_2. \end{aligned}

Look at the right-hand sides. If the body spins purely about one principal axis — say \boldsymbol{\omega} = (\omega_1, 0, 0) — every product \omega_i\omega_j vanishes, so all the \dot{\omega} are zero and the spin holds steady. But once two components are nonzero, they drive each other: the coupling terms pump angular velocity between the axes even though total angular momentum \mathbf{L} is perfectly conserved in the world. That energy-shuffling between axes is the non-trivial tumbling you see when you toss any irregular object — a phone, a remote, a hammer.

(1) The inertia tensor is NOT constant in world space. Only I_{\text{body}} is constant. As the body rotates, the world-space tensor is I_{\text{world}} = R\,I_{\text{body}}\,R^{\mathsf{T}} and changes every frame. Plugging the fixed body-frame tensor straight into world-space equations gives the wrong \boldsymbol{\omega}, wrong torques, and a simulation that leaks or invents energy. Always rotate the tensor (or its inverse) into the current orientation first.

(2) L and ω are not interchangeable. They are only parallel on a principal axis. Code that treats "the spin axis" and "the angular momentum" as the same vector will look fine for a sphere and fall apart for a tumbling box.

(3) Spinning about the intermediate axis is unstable — objects flip. This is the Dzhanibekov effect (the tennis-racket theorem). Order the principal moments I_1 < I_2 < I_3. Euler's equations make spin about the largest (I_3) and the smallest (I_1) axes stable — a tiny wobble stays tiny — but spin about the middle axis I_2 is unstable: the smallest perturbation grows exponentially and the body periodically flips end over end. It is not numerical error; it is real physics, and a correct simulator will reproduce it. If your engine's book/phone/racket refuses to flip, your torque handling is probably wrong.

In 1985 Vladimir Dzhanibekov unscrewed a wingnut aboard the Salyut-7 station and flicked it off its bolt. It sailed across the cabin spinning quietly — then, without anything touching it, snapped around 180°, spun a while, snapped back, and kept doing so at regular intervals. The Soviets classified the footage for a decade, half-wondering if it hinted at something deep and cosmic. It was Euler's equations from 1765, hiding in plain sight: the nut was spinning near its intermediate axis, and the instability flipped it over and over. The maths had been on the books for two centuries; it just took a weightless wingnut to make it visible.