A tiny rAF spring helper that stays dependency-free

Hey folks, I’m cleaning up a UI micro-interaction at work and I’m trying to keep the bundle lean, so I replaced a small animation lib with a tiny spring. The failure mode I hit before was accidental double-imports from different packages, and suddenly the “small” easing code costs more than the feature.

// dependency-free rAF spring with optional stagger
export function springTo({
  from = 0,
  to = 1,
  stiffness = 240,
  damping = 26,
  mass = 1,
  dtMax = 1 / 30,
  staggerMs = 0,
  onUpdate,
  onDone,
}) {
  let x = from;
  let v = 0;
  let raf = 0;
  let last = 0;

  const startAt = performance.now() + staggerMs;

  function step(t) {
    raf = requestAnimationFrame(step);
    if (t < startAt) return;

    if (!last) last = t;
    let dt = (t - last) / 1000;
    last = t;
    if (dt > dtMax) dt = dtMax;

    const F = -stiffness * (x - to);
    const a = (F - damping * v) / mass;
    v += a * dt;
    x += v * dt;

    onUpdate?.(x);

    if (Math.abs(v) < 0.001 && Math.abs(x - to) < 0.001) {
      cancelAnimationFrame(raf);
      onUpdate?.(to);
      onDone?.();
    }
  }

  raf = requestAnimationFrame(step);
  return () => cancelAnimationFrame(raf);
}

Neat part is it feels like a real spring and I can do a cheap stagger just by passing different staggerMs, without pulling any deps into the build.