vaweGitHub

SpringsJetpack Compose

Compose spring() default

Stiffness 1500 with critical damping gives the shortest settle in this set, 0.24 s. Stiffness 1500, damping 77.46 and mass 1 give a damping ratio of 1, so the spring is about critically damped. It settles to within 0.1% in 0.24 s. It never passes the end value.

Demo

Progress against time. The time axis runs 0 to 0.24 s, the settle time.0100.51progress50%90%
Dashed: a straight line. The dot and the line move with the box on the right.
Compose spring() default
linear

Both boxes take 0.24 s. Playing at 0.25× speed.

Your system asks for reduced motion, so nothing plays. The chart shows the whole move.

Code

A spring has no fixed duration, so the CSS takes the settle time (240 ms) and a linear() string that follows the spring.

css linear()
transition: translate 240ms linear(0, 0.0062, 0.0232, 0.0483, 0.0797, 0.1157, 0.1548, 0.196, 0.2383, 0.281, 0.3236, 0.3654, 0.4062, 0.4457, 0.4836, 0.5199, 0.5545, 0.5872, 0.6182, 0.6473, 0.6746, 0.7002, 0.724, 0.7463, 0.767, 0.7862, 0.804, 0.8204, 0.8356, 0.8497, 0.8626, 0.8745, 0.8854, 0.8955, 0.9047, 0.9132, 0.9209, 0.928, 0.9345, 0.9404, 0.9459, 0.9508, 0.9553, 0.9594, 0.9632, 0.9666, 0.9697, 0.9725, 0.9751, 0.9775, 0.9796, 0.9815, 0.9833, 0.9849, 0.9863, 0.9876, 0.9888, 0.9899, 0.9908, 0.9917, 0.9925, 0.9932, 0.9939, 0.9945, 0.995, 0.9955, 0.9959, 0.9963, 0.9967, 0.997, 0.9973, 0.9976, 0.9978, 0.998, 0.9982, 0.9984, 0.9986, 0.9987, 0.9988, 0.9989, 1);
web animations api
el.animate({ translate: ['0 0', '240px 0'] }, { duration: 240, easing: 'linear(0, 0.0062, 0.0232, 0.0483, 0.0797, 0.1157, 0.1548, 0.196, 0.2383, 0.281, 0.3236, 0.3654, 0.4062, 0.4457, 0.4836, 0.5199, 0.5545, 0.5872, 0.6182, 0.6473, 0.6746, 0.7002, 0.724, 0.7463, 0.767, 0.7862, 0.804, 0.8204, 0.8356, 0.8497, 0.8626, 0.8745, 0.8854, 0.8955, 0.9047, 0.9132, 0.9209, 0.928, 0.9345, 0.9404, 0.9459, 0.9508, 0.9553, 0.9594, 0.9632, 0.9666, 0.9697, 0.9725, 0.9751, 0.9775, 0.9796, 0.9815, 0.9833, 0.9849, 0.9863, 0.9876, 0.9888, 0.9899, 0.9908, 0.9917, 0.9925, 0.9932, 0.9939, 0.9945, 0.995, 0.9955, 0.9959, 0.9963, 0.9967, 0.997, 0.9973, 0.9976, 0.9978, 0.998, 0.9982, 0.9984, 0.9986, 0.9987, 0.9988, 0.9989, 1)', fill: 'both' });
vawe
import { spring, springDuration, springLinear } from '../../core/motion/springs.js';

const k = 1500, d = 77.46;

// per-frame code (window.seek, vawe.onFrame): t is seconds since the move starts
const x = 240 * spring(t, k, d);

// CSS and Web Animations: 240 ms, the linear() string above
el.animate(
  { translate: ['0 0', '240px 0'] },
  { duration: springDuration(k, d) * 1000, easing: springLinear(k, d), fill: 'both' },
);

Numbers

Stiffness
1500k, with mass 1
Damping
77.46d, with mass 1
Damping ratio
1about critically damped
Settle time
0.24 swithin 0.1% of the end
Overshoot
0%stays inside the end
Crosses the end
0times
50% of travel
0.044 s18% of the settle time
90% of travel
0.101 s42% of the settle time
Natural frequency
38.73 rad/ssqrt(k)
Ring period
noneit does not oscillate
Peak speed
3.42xtimes the average speed
linear() points
81within 0.13% of travel

Computed with spring() and springDuration() from core/motion/springs.js. The settle time is the last moment the spring is more than 0.1% away from the end value; a library stops its own spring by its own rest thresholds, so its clock can run shorter.

Source of the numbers

Jetpack Composeandroidx main at 9adbcd8 (compose animation-core 1.14.0-alpha01)
spring(): dampingRatio 1f, stiffness 1500fspring() in AnimationSpec.kt: dampingRatio = DampingRatioNoBouncy (1), stiffness = StiffnessMedium (1500)

Conversion: Stiffness is used as it is with mass 1 (SpringSimulation takes the natural frequency as sqrt(stiffness)). damping = 2 x dampingRatio x sqrt(stiffness), the same c = 2 x naturalFreq x dampingRatio that SpringSimulation.getAcceleration uses.

The four closest curves in this set, by the average gap between the two springs over their first seconds. A gap of 0.01 is invisible; 0.1 is a different feel.

More springs