Moment of Inertia
rotational mass: how far out the weight sits decides how hard it spins
Rotational mass: I = Σmr² grows with distance squared, so spread-out mass spins up slowly. Covers τ = Iα, L = Iω and the inertia tensor.
common4 min read
Both rotors carry the same two spheres (one white, one grey), and the thin grey circle marks their path. The solid red curved arrow is the equal torque on each, and the dashed yellow arc with its arrowhead shows the spin direction, growing longer with ω; the run restarts every 5 s. Move the mass radius to widen the right rotor, or turn on Conserve angular momentum to remove the torque and watch ω rise as the masses come in.
What it is
Moment of inertia I is mass for rotation. Newton's F = ma becomes τ = Iα: the same torque τ produces less angular acceleration α as I grows. Every bit of mass contributes m·r², where r is its distance from the axis, so where the mass sits matters more than how much there is. A torque that spins up two 1 kg masses 0.5 m from the axis (I = 0.5) spins up the same masses at 1.8 m (I = 6.48) about 13 times more slowly.
Angular momentum L = Iω stays constant when no net torque acts. A skater pulling in their arms lowers I, so ω must rise to keep L fixed. Rotational energy L²/2I goes up too, and the extra comes from the work done pulling the arms in.
In 3D, I depends on the axis, so it becomes a 3×3 inertia tensor. In the body's own axes it is constant, and for a box or a sphere it is diagonal. A rigid-body engine therefore stores the inverse tensor in local space and transforms it to world space whenever orientation changes, using the rotation matrix built from the body's quaternion. Storing the inverse saves a matrix inversion and lets a static body use all zeros.
Compound objects add up, but only about a shared axis. The parallel axis theorem moves each part's inertia from its own centre of mass to the axis you need, which is how a long sword gets its swing weight and why a door pivots more sluggishly than the same slab spun about its middle.
- rotation
- torque
- angular-momentum
- inertia-tensor
- rigid-body
Key formulas
Rules of thumb
- Doubling the distance quadruples
I; doubling the mass only doubles it. - For the same
mandr, ordering byI/mr²: solid sphere 0.4, solid cylinder 0.5, hollow sphere 0.67, thin hoop 1. - A 1 kg, 1 m cube has
I = 1/6 ≈ 0.167 kg·m²about any axis through its centre. - Use a box or capsule approximation for irregular meshes. Players notice wrong mass distribution far less than they notice jitter.
- Thin ragdoll limbs have tiny
Iand can jitter. Inflating their inertia about 2–3× is a common way to stabilise them.
Implementation
import * as THREE from "three"
/** Diagonal of the inverse local tensor for full box sizes w, h, d. */export function boxInverseInertia(m: number, w: number, h: number, d: number) { return new THREE.Vector3( 12 / (m * (h * h + d * d)), 12 / (m * (w * w + d * d)), 12 / (m * (w * w + h * h)), )}
const m4 = new THREE.Matrix4()const R = new THREE.Matrix3()const Rt = new THREE.Matrix3()const D = new THREE.Matrix3()
/** out = R · diag(invLocal) · Rᵀ */export function inverseInertiaWorld( q: THREE.Quaternion, invLocal: THREE.Vector3, out: THREE.Matrix3,) { R.setFromMatrix4(m4.makeRotationFromQuaternion(q)) Rt.copy(R).transpose() D.set(invLocal.x, 0, 0, 0, invLocal.y, 0, 0, 0, invLocal.z) return out.multiplyMatrices(R, D).multiply(Rt)}const invWorld = new THREE.Matrix3()const axis = new THREE.Vector3()const dq = new THREE.Quaternion()
export function stepRotation( body: { q: THREE.Quaternion; L: THREE.Vector3; invLocal: THREE.Vector3 }, torque: THREE.Vector3, // world space omega: THREE.Vector3, // written: world-space angular velocity dt: number,) { body.L.addScaledVector(torque, dt) inverseInertiaWorld(body.q, body.invLocal, invWorld) omega.copy(body.L).applyMatrix3(invWorld) // ω = I⁻¹ L const speed = omega.length() if (speed > 1e-9) { axis.copy(omega).divideScalar(speed) body.q.premultiply(dq.setFromAxisAngle(axis, speed * dt)).normalize() }}Common mistakes
- Applying the local inverse tensor to a world-space torque. It looks fine until the body rotates, and then it spins about the wrong axis.
- Writing
Rᵀ I⁻¹ Rfor local to world. That order is the world-to-local transform. - Using half extents in
m(w² + h²)/12. With half extentsaandb, the formula ism(a² + b²)/3. - Adding the inertias of a compound body without the
m·d²term. Each part must be moved to the shared centre of mass first. - Assuming
ωstays constant in free flight. In 3DLis what is conserved, so a tumbling box'sωkeeps changing.
Prompt
# Moment of Inertia
- Write a TypeScript rigid-body rotation module for Three.js.- Inputs: box mass and full sizes (w, h, d), a world-space torque, and a fixed timestep.- Precompute the diagonal inverse local tensor with Ix = m(h²+d²)/12, Iy = m(w²+d²)/12, Iz = m(w²+h²)/12, using zero for a static body so it never divides by zero.- Each step: rebuild I_world⁻¹ = R·I_local⁻¹·Rᵀ from the current quaternion, add torque·dt to the stored angular momentum L, set ω = I_world⁻¹·L, then rotate the quaternion by |ω|·dt about ω and normalise it.- Handle |ω| near 0 without producing NaN.- Unit tests: a cube (w = h = d) has the same response to torque on every axis at any orientation; with no torque, a tumbling non-cubic box keeps L constant while ω changes; a static body never rotates.In learning paths
Related
Further reading
Updated 2026-11-02

