{"course":{"slug":"motion-and-audio","title":"Motion, Sound and Music","url":"https://addaly.com/learn/motion-and-audio","level":"intermediate","track":"create"},"lesson":{"slug":"springs-instead-of-curves","title":"Springs, and what you give up for them","url":"https://addaly.com/learn/motion-and-audio/springs-instead-of-curves","position":6,"of":72,"minutes":10,"answer":"A spring's duration is an output of stiffness, damping and mass rather than something you set, which is why it can be redirected mid-flight without a visible restart.","keyIdea":"A spring's duration is an output of stiffness, damping and mass rather than something you set, which is why it can be redirected mid-flight without a visible restart, and why it is the wrong tool for anything that must land on a beat or must never exceed its target value."},"body":{"format":"markdown","text":"## Duration becomes an output\n\nA bezier tween is defined by where it starts, where it ends, and how long it takes. A spring is defined by physics, and the duration falls out of the physics. You do not set it.\n\nThree numbers control a spring simulation:\n\n- **Stiffness**: how hard it pulls towards the target. Higher is faster and snappier.\n- **Damping**: how much energy is removed each step. Higher settles sooner with less wobble.\n- **Mass**: inertia. Higher is slower to start and slower to stop.\n\nThe relationship that matters is the **damping ratio**: damping divided by twice the square root of stiffness times mass.\n\n- Ratio **below 1**: underdamped. It overshoots and oscillates. This is what most people want and what \"springy\" means.\n- Ratio **exactly 1**: critically damped. It arrives as fast as possible with no overshoot at all.\n- Ratio **above 1**: overdamped. It creeps in and never overshoots. Usually feels sluggish.\n\nA useful starting set for interface work: stiffness 200, damping 20, mass 1. That gives a ratio of about 0.7, a small single overshoot, and a settle in roughly half a second. Drop damping to 12 and it wobbles three times. Raise it to 28 and the overshoot disappears.\n\n## The real reason frameworks use springs\n\nIt is not that springs look nicer. It is that they can be interrupted.\n\nConsider a sheet being dragged up, released halfway, and grabbed again before it finishes. A keyframed tween has a fixed start value, a fixed end value, and a clock. To redirect it you must cancel it and start a new one, and the new one starts from zero velocity, so the sheet momentarily stops dead in the user's hand. Everybody has felt this and blamed the phone.\n\nA spring carries velocity as state. Change the target mid-flight and the simulation continues from wherever it is, at whatever speed it had. The motion stays continuous because it never restarted. This is why SwiftUI, Framer Motion, React Spring and the Android motion libraries all default to springs for gesture-driven work.\n\n## Where a spring is the wrong choice\n\nOvershoot means going past the value. Sometimes the value means something.\n\n- **Progress indicators.** A bar that springs to 100% visibly passes 100%. You have just told the user the download is 107% done.\n- **Anything reading a real quantity**: a gauge, a counter, a temperature. Overshoot is a false reading held for 200ms.\n- **Motion that must land on a beat or a word.** You cannot specify a spring's duration, so you cannot make it finish at frame 74. For sound sync, use keyframes.\n- **Anything rendered to a video file for a fixed-length edit.** Same reason. Bake the spring to keyframes first if you need both.\n\nThere is also a subtler one: springs make everything feel like the same product. When every element in an interface uses the house spring, motion stops carrying information because nothing is distinguishable from anything else.\n\n## Doing it without a framework\n\nCSS gained the `linear()` easing function, which takes a list of sampled progress values. A spring is just a function of time, so you can sample it and paste the result:\n\n```css\n.sheet {\n  transition: transform 0.6s linear(\n    0, 0.14, 0.42, 0.72, 0.94, 1.06, 1.09, 1.06, 1.02, 1, 1\n  );\n}\n```\n\nThose eleven numbers are a spring evaluated at eleven points in time. You can see the overshoot in the data: it passes 1 at the sixth sample, peaks at 1.09, and comes back. Any spring calculator will generate the list, and the result runs on the compositor with no JavaScript.\n\nIn Blender, add a **Damped Track** or bake the physics: animate the target, run the simulation, then convert to keyframes so the curve becomes editable. Glaxnimate has no spring, so you approximate with three keyframes (target, overshoot, settle) which is the bezier method from the previous lesson.\n\n## The limitation\n\nA spring simulation is deterministic but not predictable in the way a designer needs. \"How long does it take?\" has no clean answer, because mathematically it never fully arrives: implementations stop when the displacement and velocity both fall under a threshold. Two libraries with the same stiffness and damping can settle at visibly different times because they chose different thresholds and different integration step sizes.\n\nSo if you specify \"stiffness 200, damping 20\" to a developer using a different library than the one you prototyped in, you have specified less than you think. The thing to hand over is the *feel plus the numbers plus a reference recording*, and a willingness to tune once it is running on the real device."},"check":{"question":"A bottom sheet animated with a keyframed tween feels like it 'sticks' when the user grabs it again mid-animation, while the spring version does not. What is the mechanism behind the difference?","options":[{"text":"Spring animations are handed to the GPU compositor while keyframed tweens are evaluated on the main thread, so the tween is dropping frames at exactly the moment a touch event arrives and needs to be processed.","correct":false,"tempting":"Conflates a rendering-performance issue with a state issue; the stick happens even when both run at a full 60fps."},{"text":"The spring uses a shorter duration, so there is less time for an interruption to land in the middle of it.","correct":false,"tempting":"Treats the problem as probabilistic; the stick occurs on every interruption regardless of how long the animation is."},{"text":"A tween is defined by fixed endpoints and a clock, so redirecting it means cancelling and restarting from zero velocity, while a spring carries velocity as state and continues from it.","correct":true},{"text":"Tweens cannot overshoot, so the sheet has to stop exactly at the boundary before it can begin moving the other way.","correct":false,"tempting":"Overshoot is achievable with keyframes and is not what continuity during interruption depends on."}]},"prev":{"slug":"why-ease-ease-still-looks-wrong","title":"Why pressing Easy Ease still looks wrong","url":"https://addaly.com/learn/motion-and-audio/why-ease-ease-still-looks-wrong"},"next":{"slug":"arcs-and-follow-through","title":"Straight lines and the things that lag behind","url":"https://addaly.com/learn/motion-and-audio/arcs-and-follow-through"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Springs, and what you give up for them', in Motion, Sound and Music. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/motion-and-audio/springs-instead-of-curves (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Springs, and what you give up for them","courseTitle":"Motion, Sound and Music","module":{"index":1,"slug":"how-motion-reads-as-real","title":"How motion reads as real"},"answer":null,"keyIdea":"A spring’s duration is an output of stiffness, damping and mass rather than something you set, which is why it can be redirected mid-flight without a visible restart, and why it is the wrong tool for anything that must land on a beat or must never exceed its target value.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"duration-becomes-an-output","runs":[{"text":"Duration becomes an output"}]},{"t":"p","runs":[{"text":"A bezier tween is defined by where it starts, where it ends, and how long it takes. A spring is defined by physics, and the duration falls out of the physics. You do not set it."}]},{"t":"p","runs":[{"text":"Three numbers control a spring simulation:"}]},{"t":"ul","items":[[{"text":"Stiffness","b":true},{"text":": how hard it pulls towards the target. Higher is faster and snappier."}],[{"text":"Damping","b":true},{"text":": how much energy is removed each step. Higher settles sooner with less wobble."}],[{"text":"Mass","b":true},{"text":": inertia. Higher is slower to start and slower to stop."}]]},{"t":"p","runs":[{"text":"The relationship that matters is the "},{"text":"damping ratio","b":true},{"text":": damping divided by twice the square root of stiffness times mass."}]},{"t":"ul","items":[[{"text":"Ratio "},{"text":"below 1","b":true},{"text":": underdamped. It overshoots and oscillates. This is what most people want and what “springy” means."}],[{"text":"Ratio "},{"text":"exactly 1","b":true},{"text":": critically damped. It arrives as fast as possible with no overshoot at all."}],[{"text":"Ratio "},{"text":"above 1","b":true},{"text":": overdamped. It creeps in and never overshoots. Usually feels sluggish."}]]},{"t":"p","runs":[{"text":"A useful starting set for interface work: stiffness 200, damping 20, mass 1. That gives a ratio of about 0.7, a small single overshoot, and a settle in roughly half a second. Drop damping to 12 and it wobbles three times. Raise it to 28 and the overshoot disappears."}]},{"t":"h2","id":"the-real-reason-frameworks-use-springs","runs":[{"text":"The real reason frameworks use springs"}]},{"t":"p","runs":[{"text":"It is not that springs look nicer. It is that they can be interrupted."}]},{"t":"p","runs":[{"text":"Consider a sheet being dragged up, released halfway, and grabbed again before it finishes. A keyframed tween has a fixed start value, a fixed end value, and a clock. To redirect it you must cancel it and start a new one, and the new one starts from zero velocity, so the sheet momentarily stops dead in the user’s hand. Everybody has felt this and blamed the phone."}]},{"t":"p","runs":[{"text":"A spring carries velocity as state. Change the target mid-flight and the simulation continues from wherever it is, at whatever speed it had. The motion stays continuous because it never restarted. This is why SwiftUI, Framer Motion, React Spring and the Android motion libraries all default to springs for gesture-driven work."}]},{"t":"h2","id":"where-a-spring-is-the-wrong-choice","runs":[{"text":"Where a spring is the wrong choice"}]},{"t":"p","runs":[{"text":"Overshoot means going past the value. Sometimes the value means something."}]},{"t":"ul","items":[[{"text":"Progress indicators.","b":true},{"text":" A bar that springs to 100% visibly passes 100%. You have just told the user the download is 107% done."}],[{"text":"Anything reading a real quantity","b":true},{"text":": a gauge, a counter, a temperature. Overshoot is a false reading held for 200ms."}],[{"text":"Motion that must land on a beat or a word.","b":true},{"text":" You cannot specify a spring’s duration, so you cannot make it finish at frame 74. For sound sync, use keyframes."}],[{"text":"Anything rendered to a video file for a fixed-length edit.","b":true},{"text":" Same reason. Bake the spring to keyframes first if you need both."}]]},{"t":"p","runs":[{"text":"There is also a subtler one: springs make everything feel like the same product. When every element in an interface uses the house spring, motion stops carrying information because nothing is distinguishable from anything else."}]},{"t":"h2","id":"doing-it-without-a-framework","runs":[{"text":"Doing it without a framework"}]},{"t":"p","runs":[{"text":"CSS gained the "},{"text":"linear()","code":true},{"text":" easing function, which takes a list of sampled progress values. A spring is just a function of time, so you can sample it and paste the result:"}]},{"t":"code","lang":"css","label":"CSS","text":".sheet {\n  transition: transform 0.6s linear(\n    0, 0.14, 0.42, 0.72, 0.94, 1.06, 1.09, 1.06, 1.02, 1, 1\n  );\n}"},{"t":"p","runs":[{"text":"Those eleven numbers are a spring evaluated at eleven points in time. You can see the overshoot in the data: it passes 1 at the sixth sample, peaks at 1.09, and comes back. Any spring calculator will generate the list, and the result runs on the compositor with no JavaScript."}]},{"t":"p","runs":[{"text":"In Blender, add a "},{"text":"Damped Track","b":true},{"text":" or bake the physics: animate the target, run the simulation, then convert to keyframes so the curve becomes editable. Glaxnimate has no spring, so you approximate with three keyframes (target, overshoot, settle) which is the bezier method from the previous lesson."}]},{"t":"h2","id":"the-limitation","runs":[{"text":"The limitation"}],"role":"limit"},{"t":"p","runs":[{"text":"A spring simulation is deterministic but not predictable in the way a designer needs. “How long does it take?” has no clean answer, because mathematically it never fully arrives: implementations stop when the displacement and velocity both fall under a threshold. Two libraries with the same stiffness and damping can settle at visibly different times because they chose different thresholds and different integration step sizes."}]},{"t":"p","runs":[{"text":"So if you specify “stiffness 200, damping 20” to a developer using a different library than the one you prototyped in, you have specified less than you think. The thing to hand over is the "},{"text":"feel plus the numbers plus a reference recording","i":true},{"text":", and a willingness to tune once it is running on the real device."}]}],"check":{"question":[{"text":"A bottom sheet animated with a keyframed tween feels like it ‘sticks’ when the user grabs it again mid-animation, while the spring version does not. What is the mechanism behind the difference?"}],"options":[{"text":[{"text":"Spring animations are handed to the GPU compositor while keyframed tweens are evaluated on the main thread, so the tween is dropping frames at exactly the moment a touch event arrives and needs to be processed."}],"correct":false,"tempting":[{"text":"Conflates a rendering-performance issue with a state issue; the stick happens even when both run at a full 60fps."}]},{"text":[{"text":"The spring uses a shorter duration, so there is less time for an interruption to land in the middle of it."}],"correct":false,"tempting":[{"text":"Treats the problem as probabilistic; the stick occurs on every interruption regardless of how long the animation is."}]},{"text":[{"text":"A tween is defined by fixed endpoints and a clock, so redirecting it means cancelling and restarting from zero velocity, while a spring carries velocity as state and continues from it."}],"correct":true,"tempting":null},{"text":[{"text":"Tweens cannot overshoot, so the sheet has to stop exactly at the boundary before it can begin moving the other way."}],"correct":false,"tempting":[{"text":"Overshoot is achievable with keyframes and is not what continuity during interruption depends on."}]}],"order":[3,1,0,2],"why":null},"prev":{"slug":"why-ease-ease-still-looks-wrong","title":"Why pressing Easy Ease still looks wrong","minutes":10},"next":{"slug":"arcs-and-follow-through","title":"Straight lines and the things that lag behind","minutes":9}}}