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:
| Field | Type | Description |
|---|---|---|
onUpdate | (value: number, velocity: number) => void | Required. Called every frame with the live state. |
onComplete | () => void | Called once when the spring settles. |
now | () => number | Clock source in ms. Defaults to performance.now. |
raf | (cb: (t: number) => void) => number | Frame scheduler. Defaults to requestAnimationFrame. |
caf | (handle: number) => void | Frame 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 placeThe 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 });| Option | Type | Default | Description |
|---|---|---|---|
duration | number | 0.5 | Perceptual duration in seconds — sets the natural frequency. |
bounce | number | 0.2 | Bounciness in [-1, 1]: >0 bouncy, 0 critical, <0 sluggish. |
mass | number | 1 | Mass. |
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:
| Preset | stiffness | damping | mass |
|---|---|---|---|
gentle | 120 | 14 | 1 |
bouncy | 320 | 14 | 1 |
stiff | 420 | 40 | 1 |
lazy | 80 | 26 | 1.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
targeton mount (no entrance animation). - Retargets velocity-continuously when
targetchanges — no jump mid-flight. - SSR-safe: returns
targeton the server and touches no DOM at import. - Honors
prefers-reduced-motionby snapping instead of animating. The preference is evaluated at each retarget, not tracked live. - StrictMode / concurrent-safe.
configis read once, when the controller is created — changing it across renders is a no-op. (configis aSpringConfigwithoutto.)
Types
SpringConfig
All fields optional. Defaults model a moderately bouncy spring.
| Field | Type | Default | Description |
|---|---|---|---|
stiffness | number | 180 | Spring constant k. Higher = snappier. |
damping | number | 12 | Damping coefficient c. Higher = less oscillation. |
mass | number | 1 | Mass m. Higher = more sluggish. |
from | number | 0 | Starting value. |
to | number | 0 | Target value. |
velocity | number | 0 | Initial velocity (units per second). |
restDistance | number | 0.05 | Settle threshold for ` |
restVelocity | number | 0.05 | Settle 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 ω₀
}