Skip to Content
DocsAPI Reference

API Reference

sprungdesign exports four things from its core entry point and one hook from its React adapter:

import { createSpring, spring, fromFeel, presets } from "sprungdesign"; import { useSpring } from "sprungdesign/react";

createSpring(config?) → Spring

The pure solver. No side effects, no requestAnimationFrame — just math. at(t) samples the trajectory at t seconds.

const s = createSpring({ stiffness: 180, damping: 12, mass: 1, from: 0, to: 100, velocity: 0, }); s.at(0.25); // → { value, velocity, done } s.zeta; // damping ratio (<1 underdamped, =1 critical, >1 overdamped) s.w0; // natural angular frequency (rad/s)

done becomes true once |value − to| < restDistance and |velocity| < restVelocity (both default 0.05). At that point value snaps exactly to to and velocity to 0.

Because at(t) is a pure function of elapsed time, the trajectory is identical no matter how often you sample it — that’s what makes the motion frame-rate independent.


spring(config) → SpringHandle

The live, interruptible controller. Drives requestAnimationFrame; starts at rest at from and animates when you call set().

Its config is SpringConfig without to, plus:

FieldTypeDescription
onUpdate(value: number, velocity: number) => voidRequired. Called every frame with the live state.
onComplete() => voidCalled once when the spring settles.
now() => numberClock source in ms. Defaults to performance.now.
raf(cb: (t: number) => void) => numberFrame scheduler. Defaults to requestAnimationFrame.
caf(handle: number) => voidFrame canceller. Defaults to cancelAnimationFrame.
const handle = spring({ stiffness: 180, onUpdate: (value, velocity) => {}, onComplete: () => {}, }); handle.set(target); // retarget — preserves current velocity (no jump) handle.get(); // → { value, velocity } handle.stop(); // freeze in place

The now / raf / caf injection points let you drive the spring from a fake clock (in tests), a shared rAF loop, or a fixed-timestep environment. Defaults are resolved lazily, so importing and constructing never touches the DOM.


fromFeel(options?) → SpringParams

Maps designer-friendly inputs to physics constants:

import { fromFeel, spring } from "sprungdesign"; const params = fromFeel({ duration: 0.5, bounce: 0.3 }); // → { stiffness, damping, mass } spring({ ...params, onUpdate });
OptionTypeDefaultDescription
durationnumber0.5Perceptual duration in seconds — sets the natural frequency.
bouncenumber0.2Bounciness in [-1, 1]: >0 bouncy, 0 critical, <0 sluggish.
massnumber1Mass.

The bounce extremes are clamped to a settling range (the damping ratio is kept finite and strictly positive), so values at or near ±1 stay usable rather than degenerate.


presets

Ready-made { stiffness, damping, mass } configs, lifted from the reference tuner:

Presetstiffnessdampingmass
gentle120141
bouncy320141
stiff420401
lazy80261.4
import { presets, spring } from "sprungdesign"; spring({ ...presets.bouncy, onUpdate });

useSpring(target, config?) → number

The React adapter. Animates a number toward target with spring physics, re-rendering with the live value each frame.

import { useSpring } from "sprungdesign/react"; const x = useSpring(target, { stiffness: 320, damping: 14 });
  • The value starts at target on mount (no entrance animation).
  • Retargets velocity-continuously when target changes — no jump mid-flight.
  • SSR-safe: returns target on the server and touches no DOM at import.
  • Honors prefers-reduced-motion by snapping instead of animating. The preference is evaluated at each retarget, not tracked live.
  • StrictMode / concurrent-safe.
  • config is read once, when the controller is created — changing it across renders is a no-op. (config is a SpringConfig without to.)

Types

SpringConfig

All fields optional. Defaults model a moderately bouncy spring.

FieldTypeDefaultDescription
stiffnessnumber180Spring constant k. Higher = snappier.
dampingnumber12Damping coefficient c. Higher = less oscillation.
massnumber1Mass m. Higher = more sluggish.
fromnumber0Starting value.
tonumber0Target value.
velocitynumber0Initial velocity (units per second).
restDistancenumber0.05Settle threshold for `
restVelocitynumber0.05Settle threshold for `

SpringState

interface SpringState { value: number; velocity: number; done: boolean; }

SpringHandle

interface SpringHandle { set(target: number): void; // retarget, preserving velocity stop(): void; // freeze at the current value get(): { value: number; velocity: number }; }

Spring

interface Spring { at(tSeconds: number): SpringState; readonly zeta: number; // damping ratio ζ readonly w0: number; // natural angular frequency ω₀ }
Last updated on