path-rite

springStep

Advance a damped harmonic oscillator by one time step.

springStep(current: number, velocity: number, target: number, stiffness: number, damping: number, mass: number, dt: number): { value: number, velocity: number }

current — Current position of the spring.

velocity — Current velocity.

target — Equilibrium position the spring is pulling toward.

stiffness — Spring constant k. Higher values make the spring snappier (try 170).

damping — Friction coefficient. Higher values reduce bounce (try 26).

mass — Inertia of the object. Higher values make movement slower (try 1).

dt — Time step in seconds. Typically 1/60 for 60 fps.

Returns — An object { value, velocity } with the new position and velocity after the step.

The physics model:

F_spring  = -k * (x - target)
F_damping = -c * v
a = (F_spring + F_damping) / m
v' = v + a * dt
x' = x + v' * dt

To interrupt an animation mid-flight, change target -- the physics stays continuous with no discontinuity.

// Spring animation loop
const { springStep, springSettled } = pathRite;

let current = 0;
let velocity = 0;
const target = 100;
const stiffness = 170;
const damping = 26;
const mass = 1;
const precision = 0.01;

function animate() {
  const dt = 1 / 60;
  const state = springStep(current, velocity, target, stiffness, damping, mass, dt);
  current = state.value;
  velocity = state.velocity;

  // Apply the value (e.g. move an element)
  element.style.transform = `translateX(${current}px)`;

  if (!springSettled(current, velocity, target, precision)) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

Presets

Common (stiffness, damping, mass) tuples:

Preset Stiffness Damping Mass Character
Default 170 26 1 Responsive, tiny bounce
Gentle 120 14 1 Slow and smooth, no bounce
Wobbly 180 12 1 Low damping, lots of bounce
Stiff 210 20 1 Fast snap, minimal overshoot
Slow 280 60 3 Heavy mass, gentle movement