Physics was always my first love in school - there was something magical about how it could explain everything around us – from why leaves fall to why the sky is blue.
As a developer who’s been extensively using Framer Motion for the past few months, I’ve fallen in love with its spring animations. It’s a beautiful marriage of my two passions: physics and programming.
While implementing these animations across various UI components in my projects, I noticed that many developers, including myself initially, weren’t fully aware of what parameters like mass
, stiffness
, and damping
actually meant.
Thanks to my background in physics, I can help demystify these concepts and show how the principles I learned back in school create these smooth, natural-looking animations.
Hooke’s Law: The Science of Springs
At its core, a spring animation follows the physics of what scientists call a Harmonic Oscillator. Don’t let this term intimidate you – it’s simpler than it sounds!
Back in school, we defined a Harmonic Oscillator like this:
A Harmonic Oscillator is a system that oscillates with a sinusoidal motion. In the context of a spring, this means that the spring will oscillate back and forth around its equilibrium position.
The motion of a spring can be described by the following equation:
F = -k * x
Where:
- ( F ) is the force applied to the spring
- ( k ) is stiffness (how strong the spring is)
- ( x ) is displacement (how far the spring is stretched or compressed) from its equilibrium position
In simpler terms:
- If you pull the spring (x > 0), it moves.
- If you don’t pull it (x = 0), it stays put.
- If you push it (x < 0), it moves in the opposite direction.
This is the basic principle of a spring animation.
But wait, there’s more. Remember that other classic physics formula?
F = m * a
Where:
- ( F ) is the force applied to the spring
- ( m ) is mass (how heavy the object is)
- ( a ) is acceleration (how fast the object is moving)
By combining these two formulas, we get:
m * a = -k * x
Rearranging the equation, we get the acceleration:
a = -k*x /m
i.e. acceleration = -(stiffness * displacement) / mass
This means the spring’s acceleration depends on its stiffness, displacement, and mass. From acceleration, we can figure out:
- Velocity (v): How fast the object is moving.
- Position (p): Where the object is at any point in time.
How? Remember, the age old physics formula?
v = u + at
Where:
- ( v ) is velocity
- ( u ) is initial velocity
- ( a ) is acceleration
- ( t ) is time
and similarly,
p = p0 + v * t
Where:
- ( p ) is position
- ( p0 ) is initial position
- ( v ) is velocity
- ( t ) is time
In both of the above equations, ( t ) is the time interval between frames, which in the world of frontend animations is tied to the frame rate. For Framer Motion, this is typically 60 frames per second, so ( t = 1/60 ) or roughly 0.01666 seconds.
Let’s see how these physics equations translate into code. Here’s a function that calculates object positions over time based on spring motion:
1const calculateSpringMotion = (stiffness, mass, duration) => {2 // Constants3 const SPRING_REST_LENGTH = 1;4 const FRAME_RATE = 1 / 60; // 60 FPS5 const SIMULATION_FRAMES = duration * FRAME_RATE;67 let position = 5; // Initial position of the spring8 let velocity = 0; // Initial velocity is 0910 // Spring constant11 let k = -stiffness; // Negative as spring force opposes displacement1213 /* Initiate the array of position*/14 let positions = [];1516 for (let i = 0; i < SIMULATION_FRAMES; i++) {17 const force = -stiffness * (initialPosition - SPRING_REST_LENGTH);18 const acceleration = force / mass;1920 velocity += acceleration * FRAME_RATE;21 position += velocity * FRAME_RATE;2223 positions.push(position);24 }2526 return positions;27};
To make this interactive, I’ve created a playground below where you can experiment with the spring animation parameters. Adjust the mass and stiffness, to observe how the animation changes in real-time 😉
In the above playground, all we’re doing is that we’re passing these two framer-motion props:
1initial={{ x: "-200%" }}2animate={{ x: "0%" }}
Note: One of the things you might’ve noticed is that the animation never stops.
This actually, makes perfect sense from a physics perspective. In real life, springs never stop oscillating. They keep moving until the energy dissipates.
However, in real life, the energy dissipates due to friction. In Framer Motion, we don’t have friction, so the animation never stops.
To simulate friction,
we need to add damping to the spring.
Damping is a force that opposes the motion of an object. In the context of a spring, it means that the spring will lose energy over time and eventually stop oscillating.
The formula for damping is:
F = -b * v
Where:
- ( F ) is the force applied to the spring
- ( b ) is damping (how much the spring is damped)
- ( v ) is velocity (how fast the spring is moving)
Taking this into account, our total force equation becomes:
F = Spring Force + Damping Force
F = -k * x - b * v
This means that the acceleration equation becomes:
a = (b*v -k*x) / m
Let’s make these changes in our code:
1const calculateSpringMotion = (stiffness, mass, damping, duration) => {2 // Constants3 const SPRING_REST_LENGTH = 1;4 const FRAME_RATE = 1 / 60; // 60 FPS5 const SIMULATION_FRAMES = duration * FRAME_RATE;67 let position = 5; // Initial position of the spring8 let velocity = 0; // Initial velocity is 0910 // Spring constant11 let k = -stiffness; // Negative as spring force opposes displacement1213 // Damping constant14 let d = -damping; // Negative as damping force opposes velocity1516 /* Initiate the array of position*/17 let positions = [];1819 for (let i = 0; i < SIMULATION_FRAMES; i++) {20 const springForce = -stiffness * (initialPosition - SPRING_REST_LENGTH);21 const dampingForce = -damping * velocity;22 const acceleration = (springForce + dampingForce) / mass;2324 velocity += acceleration * FRAME_RATE;25 position += velocity * FRAME_RATE;2627 positions.push(position);28 }2930 return positions;31};
Finally, let’s add this to our playground:
As you can see, the spring animation gradually comes to a halt as damping dissipates energy from the system. This is reflected in the chart, where the motion converges toward a final “resting position.”
By increasing the damping slider to a higher value, you’ll notice that the object reaches the “resting position” much faster compared to when the damping value is lower.
In Framer Motion, these physics principles are abstracted away, but understanding them helps in creating better animations. Here’s a simple button animation just to give you a taste of what you can do with these physics principles :)
The default values in Framer Motion (stiffness: 100, damping: 10, mass: 1)
work well for most cases, but now that you understand what each parameter does, you can fine-tune them to achieve exactly the animation feel you’re looking for.
I hope this helps you understand the physics behind spring animations and how you can use it to create better animations.
P.S. Creating these playgrounds took a lot of time, it was fun though! Let me know if you’d like to see more of these in future posts 😉