ressort
v1.0.1
Published
Damped springs solved exactly: frame rate independent steppers for numbers, vectors and quaternions, and springs as easing functions.
Maintainers
Readme
ressort
Damped springs solved exactly: frame rate independent steppers for numbers, vectors and quaternions, and springs as easing functions.
Ressort is French for spring and resilience.
Installation
npm install ressortFeatures
- Exact steppers:
advance,advanceVec2,advanceVec3andadvanceQuatgive the same motion at any frame rate, for any damping, towards still or moving goals. - Springs as easings:
easeturns a spring into an ordinary easing function and its settling duration, with an optional initial velocity. - Familiar parameters: physical mass, stiffness and damping (anime.js, Motion), perceptual duration and bounce (SwiftUI), or smooth time (Unity).
- Smooth damp: Unity's
SmoothDampsignature for numbers, vectors and quaternions, withmaxSpeedand no overshoot. - Exponential damping:
dampwith a half-life, for values without momentum. - Shared steps: one
transitionper spring anddt, reused by every value it steps. Vectors and quaternions update in place.
Demos: overview, frame rate, easing editor with CSS linear() export, steppers.
Usage
Camera following a moving subject:
import {
advanceVec3,
createTransition,
fromSmoothTime,
transition,
} from "ressort";
const config = fromSmoothTime(0.25);
const step = createTransition();
const velocity = [0, 0, 0];
// Every frame
transition(step, config, dt);
advanceVec3(position, subject, velocity, step, subjectVelocity);Bouncy panel released by a fling:
import { advance, createTransition, fromPerceptual, transition } from "ressort";
const config = fromPerceptual({ duration: 0.5, bounce: 0.2 });
const step = createTransition();
const state = { velocity: releaseVelocity };
// Every frame
transition(step, config, dt);
x = advance(x, open ? 1 : 0, state, step);Spring in a timeline:
import { ease, fromPerceptual } from "ressort";
const spring = ease(fromPerceptual({ duration: 0.4, bounce: 0.3 }));
value = from + (to - from) * spring.ease(elapsed / spring.duration);API
Functions
Typedefs
advance(current, goal, state, step, [goalVelocity]) ⇒ number
Steps a value towards goal with a spring.
Kind: global function Returns: number - The new value. See: https://theorangeduck.com/page/spring-roll-call#springdamper
| Param | Type | Default | Description |
| -------------- | -------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------- |
| current | number | | |
| goal | number | | Goal at the start of the step. The caller advances a moving goal by goalVelocity * dt. |
| state | SpringState | | Updated in place. |
| step | SpringTransition | | |
| [goalVelocity] | number | 0 | Goal velocity over the step. |
advanceVec2(current, goal, velocity, step, [goalVelocity]) ⇒ module:pex-math~Vec2
2D vector advance.
Kind: global function
Returns: module:pex-math~Vec2 - current.
| Param | Type | Default | Description | | -------------- | -------------------------------------------------- | ------------------- | ------------------------------ | | current | module:pex-math~Vec2 | | Updated in place. | | goal | module:pex-math~Vec2 | | Goal at the start of the step. | | velocity | module:pex-math~Vec2 | | Updated in place. | | step | SpringTransition | | | | [goalVelocity] | module:pex-math~Vec2 | [0, 0] | |
advanceVec3(current, goal, velocity, step, [goalVelocity]) ⇒ module:pex-math~Vec3
Vector advance.
Kind: global function
Returns: module:pex-math~Vec3 - current.
| Param | Type | Default | Description | | -------------- | -------------------------------------------------- | ---------------------- | ------------------------------ | | current | module:pex-math~Vec3 | | Updated in place. | | goal | module:pex-math~Vec3 | | Goal at the start of the step. | | velocity | module:pex-math~Vec3 | | Updated in place. | | step | SpringTransition | | | | [goalVelocity] | module:pex-math~Vec3 | [0, 0, 0] | |
advanceQuat(current, goal, angularVelocity, step) ⇒ number
Quaternion advance: springs the scaled angle-axis rotation from goal to
current.
Kind: global function Returns: number - The remaining angle in radians. See: https://theorangeduck.com/page/spring-roll-call#quaternionspring
| Param | Type | Description | | --------------- | -------------------------------------------------- | ---------------------------------------------------- | | current | module:pex-math~Quat | Updated in place. | | goal | module:pex-math~Quat | | | angularVelocity | module:pex-math~Vec3 | Scaled axis in radians per second, updated in place. | | step | SpringTransition | |
fromPhysical(options) ⇒ SpringConfig
Spring from mass, stiffness and damping.
Kind: global function See: https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator
| Param | Type | Default | Description | | ----------------- | ------------------- | -------------- | --------------- | | options | object | | | | [options.mass] | number | 1 | | | options.stiffness | number | | Greater than 0. | | options.damping | number | | |
toPhysical(config, [mass]) ⇒ PhysicalSpring
Mass, stiffness and damping of a spring.
Kind: global function
| Param | Type | Default | | ------ | ------------------------------------------ | -------------- | | config | SpringConfig | | | [mass] | number | 1 |
fromPerceptual(options) ⇒ SpringConfig
Spring from Apple's perceptual duration and bounce, as in SwiftUI.
Kind: global function See
- https://developer.apple.com/videos/play/wwdc2023/10158/
- https://www.kvin.me/posts/effortless-ui-spring-animations
| Param | Type | Default | Description | | ---------------- | ------------------- | -------------- | --------------------------------------------------- | | options | object | | | | options.duration | number | | Period of the undamped spring in seconds. | | [options.bounce] | number | 0 | In (−1, 1]: 0 is critical, > 0 bounces, < 0 creeps. |
toPerceptual(config) ⇒ PerceptualSpring
Perceptual duration and bounce of a spring.
Kind: global function
| Param | Type | | ------ | ------------------------------------------ | | config | SpringConfig |
fromSmoothTime(smoothTime) ⇒ SpringConfig
Critically damped spring from Lowe's and Unity's smooth time.
From rest, it covers 59% of the distance in smoothTime and 95% in 2.37 ×
smoothTime.
Kind: global function See: Lowe, T. (2004). Critically damped ease-in/ease-out smoothing. Game Programming Gems 4, §1.10. ISBN 978-1584502951.
| Param | Type | Description | | ---------- | ------------------- | ----------- | | smoothTime | number | In seconds. |
halfLifeDecay(halfLife, dt) ⇒ number
Fraction of the distance left after dt with exponential damping.
Kind: global function See: https://theorangeduck.com/page/spring-roll-call#exactdamper
| Param | Type | Description | | -------- | ------------------- | -------------------------------------------------------------------------------- | | halfLife | number | Time to cover half the remaining distance, in seconds. 0 or less leaves nothing. | | dt | number | In seconds. 0 or less leaves everything, whatever the half-life. |
damp(current, target, halfLife, dt) ⇒ number
Exponential damping towards target: no velocity, no overshoot.
Kind: global function Returns: number - The new value. See
- https://theorangeduck.com/page/spring-roll-call#exactdamper
- https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
| Param | Type | Description | | -------- | ------------------- | ------------------------------------------------------ | | current | number | | | target | number | | | halfLife | number | Time to cover half the remaining distance, in seconds. | | dt | number | In seconds. |
settlingTime(config, [velocity], [restThreshold]) ⇒ number
Time after which the displacement stays under restThreshold, starting from
displacement 1.
Kind: global function Returns: number - In seconds. Infinity when the spring never settles.
| Param | Type | Default | Description | | --------------- | ------------------------------------------ | ----------------- | ----------------------------------------------------------- | | config | SpringConfig | | | | [velocity] | number | 0 | Initial velocity towards the goal, in distances per second. | | [restThreshold] | number | 1e-3 | Relative to the distance. |
ease(config, [options]) ⇒ SpringEase
Spring from 0 to 1 as an easing function over its settling time.
Kind: global function Throws:
- RangeError When the spring never settles: see
settlingTime.
| Param | Type | | --------- | ---------------------------------------------------- | | config | SpringConfig | | [options] | SpringEaseOptions |
smoothDamp(current, target, state, smoothTime, dt, [maxSpeed]) ⇒ number
Critically damped spring towards target that never overshoots it, with
Unity's signature.
Kind: global function Returns: number - The new value. See
- Lowe, T. (2004). Critically damped ease-in/ease-out smoothing. Game Programming Gems 4, §1.10. ISBN 978-1584502951.
- https://github.com/Unity-Technologies/UnityCsReference/blob/225b0fbdb57cc17d094e8056b71f8314aba56f73/Runtime/Export/Math/Mathf.cs#L276-L307
- https://theorangeduck.com/page/spring-roll-call#critical
| Param | Type | Default | Description |
| ---------- | ---------------------------------------- | --------------------- | --------------------------------- |
| current | number | | |
| target | number | | |
| state | SpringState | | Updated in place. |
| smoothTime | number | | In seconds. See fromSmoothTime. |
| dt | number | | In seconds. |
| [maxSpeed] | number | Infinity | In units per second. |
smoothDampVec2(current, target, velocity, smoothTime, dt, [maxSpeed]) ⇒ module:pex-math~Vec2
2D vector smoothDamp: maxSpeed limits the magnitude of the velocity.
Kind: global function
Returns: module:pex-math~Vec2 - current.
See: https://github.com/Unity-Technologies/UnityCsReference/blob/225b0fbdb57cc17d094e8056b71f8314aba56f73/Runtime/Export/Math/Vector2.cs#L447-L502
| Param | Type | Default | Description |
| ---------- | --------------------------------- | --------------------- | --------------------------------- |
| current | module:pex-math~Vec2 | | Updated in place. |
| target | module:pex-math~Vec2 | | |
| velocity | module:pex-math~Vec2 | | Updated in place. |
| smoothTime | number | | In seconds. See fromSmoothTime. |
| dt | number | | In seconds. |
| [maxSpeed] | number | Infinity | In units per second. |
smoothDampVec3(current, target, velocity, smoothTime, dt, [maxSpeed]) ⇒ module:pex-math~Vec3
Vector smoothDamp: maxSpeed limits the magnitude of the velocity.
Kind: global function
Returns: module:pex-math~Vec3 - current.
See: https://github.com/Unity-Technologies/UnityCsReference/blob/225b0fbdb57cc17d094e8056b71f8314aba56f73/Runtime/Export/Math/Vector3.cs#L231-L301
| Param | Type | Default | Description |
| ---------- | --------------------------------- | --------------------- | --------------------------------- |
| current | module:pex-math~Vec3 | | Updated in place. |
| target | module:pex-math~Vec3 | | |
| velocity | module:pex-math~Vec3 | | Updated in place. |
| smoothTime | number | | In seconds. See fromSmoothTime. |
| dt | number | | In seconds. |
| [maxSpeed] | number | Infinity | In units per second. |
smoothDampQuat(current, target, angularVelocity, smoothTime, dt, [maxSpeed]) ⇒ number
Quaternion smoothDamp: damps the scaled angle-axis rotation from target
to current.
Kind: global function Returns: number - The remaining angle in radians. See: https://theorangeduck.com/page/spring-roll-call#quaternionspring
| Param | Type | Default | Description |
| --------------- | --------------------------------- | --------------------- | ---------------------------------------------------- |
| current | module:pex-math~Quat | | Updated in place. |
| target | module:pex-math~Quat | | |
| angularVelocity | module:pex-math~Vec3 | | Scaled axis in radians per second, updated in place. |
| smoothTime | number | | In seconds. See fromSmoothTime. |
| dt | number | | In seconds. |
| [maxSpeed] | number | Infinity | In radians per second. |
createTransition() ⇒ SpringTransition
Identity transition, to be filled by transition.
Kind: global function
transition(out, config, dt) ⇒ SpringTransition
Exact transition of a spring over dt, for any damping ratio.
Kind: global function See: https://www.ryanjuckett.com/damped-springs/
| Param | Type | Description | | ------ | -------------------------------------------------- | ------------------------------------------------------ | | out | SpringTransition | | | config | SpringConfig | | | dt | number | Step in seconds. 0 or less leaves the state unchanged. |
SpringConfig : object
Normalized spring: every parametrisation converts to it.
Kind: global typedef Properties
| Name | Type | Description | | ----- | ------------------- | -------------------------------------------------------- | | omega | number | Natural frequency ω in radians per second. | | zeta | number | Damping ratio ζ: < 1 bounces, 1 is critical, > 1 creeps. |
PhysicalSpring : object
Kind: global typedef Properties
| Name | Type | Description | | --------- | ------------------- | ------------------------------------------------------------------------------------------- | | mass | number | | | stiffness | number | Force per unit of displacement. Greater than 0: without it, nothing pulls towards the goal. | | damping | number | Force per unit of velocity. |
PerceptualSpring : object
Kind: global typedef Properties
| Name | Type | Description | | -------- | ------------------- | --------------------------------------------------- | | duration | number | Period of the undamped spring in seconds. | | bounce | number | In (−1, 1]: 0 is critical, > 0 bounces, < 0 creeps. |
SpringTransition : object
Exact map from displacement and velocity to their values dt later:
e₁ = pp·e₀ + pv·v₀v₁ = vp·e₀ + vv·v₀
Shared by every value stepped with the same config and dt.
Kind: global typedef Properties
| Name | Type | Description | | ---- | ------------------- | ---------------- | | dt | number | Step in seconds. | | pp | number | | | pv | number | | | vp | number | | | vv | number | |
SpringState : object
Velocity carried between scalar steps.
Kind: global typedef Properties
| Name | Type | Description | | -------- | ------------------- | -------------------- | | velocity | number | In units per second. |
SpringEaseOptions : object
Kind: global typedef Properties
| Name | Type | Default | Description | | --------------- | ------------------- | ----------------- | --------------------------------------------------------------------------------- | | [velocity] | number | 0 | Initial velocity towards the goal, in distances per second. | | [restThreshold] | number | 1e-3 | Displacement, relative to the distance, under which the spring counts as settled. |
SpringEase : object
Spring as an easing function over its settling time.
Kind: global typedef Properties
| Name | Type | Description |
| -------- | --------------------- | -------------------------------------------------------------------- |
| ease | function | Progress from 0 to 1 at normalized time t. Exactly 1 from t = 1. |
| duration | number | Settling time in seconds. |
References
- Holden, D. (2021). Spring-It-On: the game developer's spring-roll-call. theorangeduck.com
- Juckett, R. (2012). Damped springs. ryanjuckett.com
- Lowe, T. (2004). Critically damped ease-in/ease-out smoothing. Game Programming Gems 4, §1.10. ISBN 978-1584502951. Verifiable through Unity's Mathf.cs.
- Apple (2023). Animate with springs, WWDC23. developer.apple.com
- Grajeda, K. (2024). Effortless UI spring animations: a two-parameter approach. kvin.me
- Driscoll, R. (2016). Frame rate independent damping using lerp. rorydriscoll.com
- Wikipedia. Harmonic oscillator: damped harmonic oscillator. en.wikipedia.org
License
MIT. See license file.

