🧮

Remotion Code Analysis — spring() Physics Engine Internals

The math and code of spring() simulating damped spring differential equations in JavaScript

GitHub: remotion-dev/remotion/packages/core/src/spring

The secret behind spring()'s smooth bounce is the damped harmonic oscillator equation: m*x'' + c*x' + k*x = 0.

The discriminant D=c²-4mk determines 3 solution types: overdamped (no bounce), critically damped (fastest convergence), underdamped (bounce). The zeta ratio damping/(2*sqrt(stiffness*mass)) selects which path.

measureSpring() pre-calculates how many frames the spring animation lasts by iterating until the value is within threshold of 1.

How It Works

1

spring({frame, fps}) calculates time (seconds) from frame/fps

2

Calculate damping ratio: zeta = damping / (2 * sqrt(stiffness * mass))

3

If zeta < 1, underdamped: exp(-ζω₀t) * cos(ωDt) solution → bounce animation

4

If zeta ≥ 1, overdamped/critically damped: converges to 1 without bounce

5

measureSpring() finds the frame where |1-value| < 0.001 to determine durationInFrames

Pros

  • Real physics: natural motion based on physics laws, not arbitrary curves like CSS ease-in-out
  • Deterministic: same frame always same value, 100% reproducible as it is a math formula
  • measureSpring() auto-calculates duration → determines appropriate length without hardcoding

Cons

  • Mathematical understanding needed: config tuning is trial-and-error without understanding damping ratio/natural frequency

Use Cases

Spring config tuning: mathematically understanding visual effects of mass/damping/stiffness values Custom easing function implementation: creating own physics simulation referencing spring() pattern