Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (2024)

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

When dealing with complex CSS animations, there is a tendency to create expansive @keyframes with lots of declarations. There are a couple of tricks though that I want to talk about that might help make things easier, while staying in vanilla CSS:

  1. Multiple animations
  2. Timing functions

The first one is more widely used and familiar but the second one is less common. There could be good reasons for that — chaining animations with commas is relatively easier than grokking the various timing functions that are available to us and what they do. There’s one especially neat timing function that gives us total control to create custom timing functions. That would be cubic-bezier() and in this post I will show you the power of it and how it can be used to create fancy animation without too much complexity.

Let’s start with a basic example showing how we can move a ball around in interesting directions, like an infinity (∞) shape:

As you can see, there is no complex code — only two keyframes and a cubic-bezier() function. And yet, a pretty complex-looking final infinity-shape animation is what we get.

Cool, right? Let’s dig into this!

The cubic-bezier() function

Let’s start with the official definition:

A cubic Bézier easing function is a type of easing function defined by four real numbers that specify the two control points, P1 and P2, of a cubic Bézier curve whose end points P0 and P3 are fixed at (0, 0) and (1, 1) respectively. The x coordinates of P1 and P2 are restricted to the range [0, 1].

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (1)

The above curve defines how the output (y-axis) will behave based on the time (x-axis). Each axis has a range of [0, 1] (or [0% 100%] ). If we have an animation that lasts two-second (2s), then:

0 (0%) = 0s 1 (100%) = 2s

If we want to animate left from 5px to 20px, then:

0 (0%) = 5px 1 (100%) = 20px

X, the time, is always restricted to [0 1]; however, Y, the output, can go beyond [0 1].

My goal is to adjust P1 and P2 in order to create the following curves:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (2)
Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (3)

You may think this is impossible to achieve because, as stated in the definition, P0 and P3 are fixed at (0,0) and (1,1) meaning they cannot be on the same axis. That’s true, and we will use some math tricks to “approximate” them.

Parabolic curve

Let’s start with the following definition: cubic-bezier(0,1.5,1,1.5). That gives us the following curve:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (4)

Our goal is to move (1,1) and make it at (0,1) which isn’t technically possible. So we will try to fake it.

We previously said that our range is [0 1] (or [0% 100%]) so let’s imagine the case when 0% is very close to 100%. If, for example, we want to animate top from 20px (0%) to 20.1px (100%) then we can say that both the initial and final states are equal.

Hm, but our element will not move at all, right?

Well, it will move a little because the Y value exceeds 20.1px (100%). But that’s not enough to give us perceptible movement:

Let’s update the curve and use cubic-bezier(0,4,1,4) instead. Notice how our curve is way taller than before:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (5)

But yet, still no movement — even if the top value is crossing 3 (or 300%). Let’s try cubic-bezier(0,20,1,20):

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (6)

Yes! it started to move a little. Did you notice the evolution of the curve each time we increase the value? It’s making our point (1,1) “visually” closer to (0,1) when we zoom out to see the full curve and this is the trick.

By using cubic-bezier(0,V,1,V) where V is some very big value and both the initial and final states are very close together (or almost equal), we can simulate the parabolic curve.

An example is worth a thousand words:

I applied the “magic” cubic-bezier function in there to the top animation, plus a linear one applied to left. This gives us the curve we want.

Digging into the math

For those of you math-minded folks out there, we can break that explanation down further. A cubic bezier can be defined using the following formula:

P = (1−t)³P0 + 3(1−t)²tP1 + 3(1−t)t²P2 + t³P3

Each point is defined as follows: P0 = (0,0), P1 = (0,V), P2 = (1,V), and P3 = (1,1).

This gives us the two functions for x and y coordinates:

  • X(t) = 3(1−t)t² + t³ = 3t² - 2t³
  • Y(t) = 3(1−t)²tV +3(1−t)t²V + t³ = t³ - 3Vt² + 3Vt

V is our big value and t is within the range [0 1]. If we consider our previous example, Y(t) will give us the value of top while X(t) is the time progress. The points (X(t),Y(t)) will then define our curve.

Let’s find the maximum value of Y(t). For this, we need to find the value of t that will give us Y'(t) = 0 (when the derivative is equal to 0):

Y'(t) = 3t² - 6Vt + 3V

Y'(t) = 0 is a quadratic equation. I will skip the boring part and will give you the result, which is t = V - sqrt(V² - V).

When V is a large value, t will be equal to 0.5. So, Y(0.5) = Max and X(0.5) will be equal to 0.5. That means we reach the maximum value at the halfway point in the animation, which conforms to the parabolic curve we want.

Also, Y(0.5) will give us (1 + 6V)/8 and this will allow us to find the max value based on V. And since we will always use a big value for V, we can simplify to 6V/8 = 0.75V.

We used V = 500 in the last example, so the max value there would come out to 375 (or 37500%) and we get the following:

  • Initial state (0): top: 200px
  • Final state (1): top: 199.5px

There’s a difference of -0.5px between 0 and 1. Let’s call it the increment. For 375 (or 37500%) we have an equation of 375*-0.5px = -187.5px. Our animated element is reaching top: 12.5px (200px - 187.5px) and gives us the following animation:

top: 200px (at 0% of the time ) → top: 12.5px (at 50% of the time) → top: 199.5px (at 100% of the time) 

Or, expressed another way:

top: 200px (at 0%) → top: 12.5px (at 50%) → top: 200px (at 100%)

Let’s do the opposite logic. What value of V should we use to make our element reach top: 0px? The animation will be 200px → 0px → 199.5px, so we need -200px to reach 0px. Our increment is always equal to -0.5px. The max value will be equal to 200/0.5 = 400, so 0.75V = 400 which means V = 533.33.

Our element is touching the top!

Here is a figure that sums up that math we just did:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (7)

Sinusoidal curve

We will use almost the exact same trick to create a sinusoidal curve but with a different formula. This time we will use cubic-bezier(0.5,V,0.5,-V)

Like we did before, let’s see how the curve will evolve when we increase the value:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (8)

I think you probably get the idea by now. Using a big value for V gets us close to a sinusoidal curve.

Here’s another one with a continuous animation — a real sinusoidal animation!

The math

Let’s get in the math for this one! Folllowing the same formula as before, we will get the following functions:

  • X(t) = 3/2(1−t)²t + 3/2(1−t)t² + t³ = (3/2)t - (3/2)t² + t³
  • Y(t) = 3(1−t)²tV - 3(1−t)t²V + t³ = (6V + 1)t³ - 9Vt² + 3Vt

This time we need to find the minimum and maximum values for Y(t). Y'(t) = 0 will give us two solutions. After solving for this:

Y'(t) = 3(6V + 1)t² - 18Vt + 3V = 0

…we get:

  • t' = (3V + sqrt(3V² - V))/(6V + 1)
  • t''= (3V - sqrt(3V² - V))/(6V + 1)

For a big value of V, we have t'=0.211 and t"=0.789. That means that Y(0.211) = Max and Y(0.789) = Min. That also means that X(0.211)= 0.26 and X(0.789) = 0.74. In other words, we reach the Max at 26% of the time and Min at 74% of the time.

Y(0.211) is equal to 0.289V and Y(0.789) to -0.289V. We got those values with some rounding considering that V is very big.

Our sinusoidal curve should also cross the x-axis (or Y(t) = 0) at half the time (or X(t) = 0.5). In order to prove this, we use the second derivate of Y(t) — which should be equal to 0 — so Y''(t) = 0.

Y''(t) = 6(6V + 1)t - 18V = 0

The solution is 3V/(6V + 1), and for a big V value, the solution is 0.5. That give us Y(0.5) = 0 and X(0.5) = 0.5 which confirms that our curve crosses the (0.5,0) point.

Now let’s consider the previous example and try to find the value of V that gets us back to top: 0%. We have:

  • Initial state (0): top: 50%
  • Final state (1): top: 49.9%
  • Increment: -0.1%

We need -50% to reach top: 0%, so 0.289V*-0.1% = -50% which gives us V = 1730.10.

As you can see, our element is touching the top and disappearing at the bottom because we have the following animation:

top: 50% → top: 0% → top: 50% → top: 100% → top: 50% → and so on ... 

A figure to sum up the calculation:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (9)

And an example to illustrate all curves together:

Yes, you see four curves! If you look closely, you will notice that I am using two different animations, one going to 49.9% (an increment of -0.01%) and another going to 50.1% (an increment of +0.01%). By changing the sign of the increment, we control the direction of the curve. We can also control the other parameters of the cubic bezier (not the V one that should remain a big value) to create more variations from the same curves.

And below, an interactive demo:

Getting back to our example

Let’s get back to our initial example of a ball moving around in the shape of an infinity symbol. I simply combined two sinusoidal animations to make it work.

If we combine what we did previously with the concept of multiple animations, we can get astonishing results. Here again is the initial example, this time as an interactive demo. Change the values and see the magic:

Let’s go further and add a little CSS Houdini to the mix. We can animate a complex transform declaration thanks to @property (but CSS Houdini is limited to Chrome and Edge support at the moment).

What kind of drawings can you make with that? Here is a few that I was able to make:

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (10)

And here is a spirograph animation:

And a version without CSS Houdini:

There’s a few things to take away from these examples:

  • Each keyframe is defined using only one declaration that contain the increment.
  • The position of the element and the animation are independent. We can easily place the element anywhere without the need to adjust the animation.
  • We made no calculations. There isn’t a ton of angles or pixel values. We only need a tiny value within the keyframe and a big value within the cubic-bezier() function.
  • The whole animation can be controlled just by adjusting the duration value.

What about transition?

The same technique can also be used with the CSS transition property since it follows the same logic when it comes to timing functions. This is great because we’re able to avoid keyframes when creating some complex hover effect.

Here’s what I made without keyframes:

Mario is jumping thanks to the parabolic curve. We needed no keyframes at all to create that shake animation on hover. The sinusoidal curve is perfectly capable of doing all the work.

Here is another version of Mario, this time using CSS Houdini. And, yep, he’s still jumping thanks to the parabolic curve:

For good measure, here are more fancy hover effects without keyframes (again, Chrome and Edge only):

That’s it!

Now you have some magic cubic-bezier() curves and the math behind them. The benefit, of course, is that custom timing functions like this let us do fancy animations without the complex keyframes we generally reach for.

I understand that not everyone is math-minded and that’s okay. There are tools to help, like Matthew Lein’s Ceaser, which lets you drag the curve points around to get what you need. And, if you don’t already have it bookmarked, cubic-bezier.com is another one. If you want to play with cubic-bezier outside the CSS world, I recommend desmos where you can see some math formulas.

Regardless of how you get your cubic-bezier() values, hopefully now you have a sense of their powers and how they can help make for nicer code in the process.

Advanced CSS Animation Using Cubic-bezier() | CSS-Tricks (2024)

FAQs

How do you write cubic Bezier in CSS? ›

CSS cubic-bezier() Function

P0 is (0, 0) and represents the initial time and the initial state, P3 is (1, 1) and represents the final time and the final state. The cubic-bezier() function can be used with the animation-timing-function property and the transition-timing-function property.

How to improve CSS animation performance? ›

How to create high-performance CSS animations
  1. Browser compatibility.
  2. Move an element.
  3. Resize an element.
  4. Change an element's visibility.
  5. Avoid properties that trigger layout or paint.
  6. Force layer creation.
  7. Debug slow or glitchy animations. Check whether an animation triggers layout. Check for dropped frames. ...
  8. Conclusion.

What is a cubic Bezier? ›

A cubic Bézier curve is defined by four points: P0, P1, P2, and P3. The points P0 and P3 represent the start and the end of the curve. In CSS, these points are fixed as the coordinates progress (the abscissa the input progress, the ordinate the output progress).

How do you understand cubic Bezier? ›

Cubic Bézier curves

Four points P0, P1, P2 and P3 in the plane or in higher-dimensional space define a cubic Bézier curve. The curve starts at P0 going toward P1 and arrives at P3 coming from the direction of P2. Usually, it will not pass through P1 or P2; these points are only there to provide directional information.

How do you find the equation of a cubic Bézier curve? ›

P = ( 1 − t ) 3 P 0 + 3 ( 1 − t ) 2 t P 1 + 3 ( 1 − t ) t 2 P 2 + t 3 P 3 . In the case of the first-degree curve the point P lies on the straight line defined by the control points, and for t ∈ [ 0 1 ] between P 0 and P 1 .

How do you make a cube in CSS? ›

First, we will rotate each face so that it's facing the proper direction. The next step will be to move each face out from the center to the edges of the cube. Let's do the left and right next. These need to be rotated 90 degrees around the y-axis so that they are both facing outwards on each side.

Does CSS animation slow down a website? ›

Leveraging animation libraries for their complex effects can inadvertently slow down your site. Before reaching for these comprehensive solutions, consider if simpler CSS animations or lightweight JavaScript libraries could achieve similar results more efficiently.

How do I make animations move smoothly? ›

Utilize Easing

Easing, also known as 'slow in and slow out', is a technique where the animation starts and ends slowly, while the middle part is faster. This results in a more natural and smoother animation. Let's see why easing is so effective: Natural Movement: Easing mimics the way things move in the real world.

Is it better to animate with CSS or JS? ›

👍 Performance: CSS animations tend to perform better because they're typically hardware-accelerated by the browser, leading to smoother performance.

What is the Bézier () function used for? ›

The cubic-bezier() function is an inbuilt function in CSS that is used to define a Cubic Bezier curve. A Bezier curve is a mathematically defined curve used in two-dimensional graphic applications like adobe illustrator, Inkscape, etc.

Why are Bézier curves better? ›

Computer graphics. Bézier curves enable us to model smooth curves since the curve is contained in the convex hull created by the control points. You can then apply affine transformations to the curve, such as rotation and translation, by applying these transforms to the control points.

How to draw a Bezier curve? ›

Let's get started.
  1. Bezier Curve.
  2. Three Steps to be Done.
  3. Step 1 : Calculating Data Points.
  4. Step 2 : Calculating Connection Points.
  5. Step 3 : Define Path and Draw in Canvas.
  6. Second Curve with Fill Background Color.

What is the math behind the Bézier curves? ›

Bézier curves are parametric curves that are defined by a set of control ponts. Their mathematical origins come from the Bernstein polynomials, which are a way to approximate real functions. Bézier curves are Bernstein polynomials with the control points taking the place of Bernstein coefficients.

What are the limitations of the Bezier curve? ›

However, the Bézier method has some limitations, which mainly include two aspects: (1) the shapes of Bézier curves and surfaces are only defined by their control points, and (2) the Bézier method cannot represents conic curves and surfaces accurately [5].

How do you code a circle in CSS? ›

Creating Circles with CSS
  1. Apply the rule border-radius: 50%; to a block-level element (such as a div ).
  2. Apply a height and width to that block-level element and give both properties the same value.

How do you write a Bézier curve equation? ›

A cubic bezier defined by p1,p2,p3,p4 has parametric equation B(t)=(1−t)3p1+3(1−t)2tp2+3(1−t)t2p3+t3p4.

What is ease in out CSS? ›

ease-in - specifies a transition effect with a slow start. ease-out - specifies a transition effect with a slow end. ease-in-out - specifies a transition effect with a slow start and end.

References

Top Articles
Latest Posts
Article information

Author: Rev. Porsche Oberbrunner

Last Updated:

Views: 5811

Rating: 4.2 / 5 (53 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Rev. Porsche Oberbrunner

Birthday: 1994-06-25

Address: Suite 153 582 Lubowitz Walks, Port Alfredoborough, IN 72879-2838

Phone: +128413562823324

Job: IT Strategist

Hobby: Video gaming, Basketball, Web surfing, Book restoration, Jogging, Shooting, Fishing

Introduction: My name is Rev. Porsche Oberbrunner, I am a zany, graceful, talented, witty, determined, shiny, enchanting person who loves writing and wants to share my knowledge and understanding with you.