Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Upgrading CSS Animation With Motion Curves

There is UI animation, and then there is good UI animation. Good animation makes you go “Wow!” — it’s smooth, beautiful and, most of all, natural, not blocky, rigid or robotic. If you frequent Dribbble or UpLabs1, you’ll know what I am talking about.

Further Reading on Smashing: Link

With so many amazing designers creating such beautiful animations, any developer would naturally want to recreate them in their own projects. Now, CSS does provide some presets for transition-timing-function6, such as ease-in, ease-out and ease-in-out, which add some level of smoothness and realism, but they are very generic, aren’t they? How boring would it be if every animation on the web followed the same three timing functions?

One of the properties of transition-timing-function is cubic-bezier(n1, n2, n3, n4), in which you can pass four numbers to create your very own timing function. Towards the end of this article, you’ll know exactly what these four numbers represent — still, believe me, coming up with four numbers to capture the transition you are imagining in your head is nowhere close to being easy. But thanks to cubic-bezier8 and Ceasar9, you don’t have to. These tools bring motion curves to the web.

(Credit: m-2-h10)

Motion curves are primarily used by animators (for example, in Adobe After Effects11) to create advanced, realistic animations. With cubic-bezier and Ceasar, you can simply manipulate the shape of a curve, and those four numbers (n1, n2, n3, n4) will be filled in for you, which is absolutely great! Still, to use and make the most out of motion curves, you need to understand how they work, and that’s what we’re going to do in this article. Let’s begin.

Understanding Motion Curves Link

A motion curve is nothing but a plot between any animatable property12 and time. A motion curve defines how the speed of an animation running under its influence varies over time.

Motion curve is a plot between animatable property and time.13
A motion curve is a plot between an animatable property and time. (View large version14)

Let’s take distance (translateX)15 as an example of an animatable property. (The explanation holds true for any other animatable property.)

Calculating speed at time t1 on a distance-time plot.16
Calculating speed at time t1 on the distance-time plot. (View large version17)

If you’ve had any experience with physics and basic calculus, you’ll know that deciphering speed from a distance-time graph is very simple. The first derivative of distance as a function of time, with respect to time, is speed, which means that an object following a distance-time curve would have greater speed in places where the curve is steep and lower in places where the curve is flatter. If you know how that works, great! You’re all set and can skip to the next section.

Now, I am aware that design and development is a diverse field, and not everyone has the same background. Perhaps the two paragraphs above were all jargon to you. Don’t fret. We’ll keep going and make sense of the jargon.

Consider the red box below. Let’s get a little callow here and call the red box “Boxy”; it’ll be easier to refer to it that way. All right, so Boxy is going to move from one edge of the screen to the other in a linear fashion, and we are going to analyze its motion.

One of the presets of transition-timing-function is linear. To make Boxy move, all we do is add the following class.

 .moveForward {
	transform: translateX(1000px);

To control the animation, we would set the transition property for Boxy as follows:

#boxy {
	width: 200px;
	height: 200px;
	background: red;
	transition-property: transform; 
	transition-duration: 1s; 
	transition-timing-function: linear; 

That’s a very verbose way to specify transition. In reality, you will almost always find transition written in its shorthand form:

#boxy {
	width: 200px;
	height: 200px;
	background: red;
	transition: transform 1s linear;

Let’s see it go.

Boxy undergoing linear motion18
Box undergoing linear motion.

Robotic, isn’t it? You could say that this motion feels robotic because it’s linear, which is a perfectly plausible answer. But could you explain why? We can see that setting linear results in robotic motion, but what exactly is happening behind the scene? That’s what we’ll figure out first; we’re going to get to the innards and understand why this motion feels robotic, blocky and not natural.

Let’s start by graphing Boxy’s motion to see if we can gain some insight. Our graph will have two axes, the first being distance, and the second time. Boxy covers a total distance of 1000 pixels (distance) in 1 second (time). Now, don’t get scared by all the math below — it’s very simple.

Here is our very simple graph, with the axes as mentioned.

Empty graph with axes19
Empty graph with axes (View large version20)

Right now, it’s empty. Let’s fill it up with some data.

To start off with, we know that at 0 seconds, when the animation has not yet started, Boxy is in its initial position (0 pixels). And after 1 second has passed, Boxy has travelled a total of 1000 pixels, landing at the opposite edge of the display.

Boxy's initial and final positions21
Boxy’s initial and final positions (View large version22)

Let’s plot this data on the graph.

Graph with Boxy's initial and final positions plotted23
Graph with Boxy’s initial and final positions plotted (View large version24)

So far so good. But two data points are not enough — we need more. The following figure shows the positions of Boxy at different points of time (all thanks to my high-speed camera).

Boxy's positions at different points of time25
Boxy’s positions at different points of time (View large version26)

Let’s add this data to our graph.

Graph with different positions plotted27
Graph with different positions plotted (View large version28)

You could, of course, have many more data points for different times (for example, 0.375 seconds, 0.6 seconds, etc.) but what we have is enough to complete our graph. By joining all of the points, we have completed the graph. High five!

Final graph29
Final graph (View large version30)

Cool, but what does this tell us? Remember that we started our investigation with the goal of understanding why Boxy’s linear motion feels unnatural and robotic? At a glance, this graph we’ve just constructed doesn’t tell us anything about that. We need to go deeper.

Keep the graph in mind and let’s talk for a minute about speed. I know you know what speed is — I’d just like to put it in mathematical terms. As it goes, the formula for speed is this:

Mathematical formula for speed31
Mathematical formula for speed (View large version32)

Therefore, if a car covers a distance of 100 kilometers in 1 hour, we say its speed is 100 kilometers per hour.

Representing speed33
Representing speed (View large version34)

If the car doubles its speed, it will start covering double the distance (200 kilometers) in the same interval (1 hour), or, in other words, it will cover the original distance of 100 kilometers in half the time (0.5 hours). Make sense?

Similarly, if the car halved its speed (that is, slowed down by half), it would start covering a distance of 50 kilometers in the same interval (1 hour), or, in other words, it would cover the original distance of 100 kilometers in twice the time (2 hours).

Great! With that out of the way, let’s pick up where we left off. We were trying to figure out how the graph between distance and time can help us understand why Boxy’s linear motion feels robotic.

Hey, wait a second! We have a graph between distance and time, and speed can be calculated from distance and time, can’t it? Let’s try to calculate Boxy’s speed at different time intervals.

Calculating speed at different intervals35
Calculating speed at different intervals (View large version36)

Here, I’ve chosen three different time intervals: one near the start, one in the middle and one at the end near the final position. As is evident, at all three intervals, Boxy has exactly the same speed (s1 = s2 = s3) of 1000 pixels per second; that is, no matter what interval you choose in the graph above, you will find Boxy moving at 1000 pixels per second. Isn’t that odd? Things in real life don’t move at a constant speed; they start out slowly, gradually increase their speed, move for a while, and then slow down again before stopping, but Boxy abruptly starts with a speed of 1000 pixels per second, moving with the same speed and abruptly stopping at exactly the same speed. This is why Boxy’s movement feels robotic and unnatural. We are going to have to change our graph to fix this. But before diving in, we’ll need to know how changes to the speed will affect the graph drawn between distance and time. Ready? This is going to be fun.

Let’s double Boxy’s speed and see how the appearance of the graph changes in response. Boxy’s original speed, as we calculated above, is 1000 pixels per second. Because we have doubled the speed, Boxy will now be able to cover the distance of 1000 pixels in half the time — that is, in 0.5 seconds. Let’s put that on a graph.

Graph showing double speed37
Graph showing double speed (View large version38)

What if we tripled the speed? Boxy now covers 1000 pixels in one third of the time (a third of a second).

Graph showing triple speed39
Graph showing triple speed (View large version40)

Hmm, notice something? Notice how, when the graph changes, the angle that the line makes with the time axis increases as the speed increases.

All right, let’s go ahead and halve Boxy’s speed. Halving its speed means that Boxy will be able to cover only 500 pixels (half the original distance) in 1 second. Let’s put this on a graph.

Graph showing half speed41
Graph showing half speed (View large version42)

Let’s slow down Boxy a little more, making the speed one third of the original. Boxy will be able to cover one third of the original distance in 1 second.

Graph showing a third of the speed43
Graph showing a third of the speed (View large version44)

See a pattern? The line gets steeper and steeper as we increase Boxy’s speed, and starts to flatten out as we slow Boxy down.

Line gets steeper as speed increases and flattens out as speed decreases45
Line gets steeper as speed increases and flattens out as speed decreases. (View large version46)

This makes sense because, for a steeper line, a little progress in time produces a much higher change in distance, implying greater speed.

A small change in time produces a relatively large change in distance, making for a steeper graph.47
A small change in time produces a relatively large change in distance, making for a steeper graph. (View large version48)
A small change in time produces a relatively large change in distance, making for a steeper graph.49
A small change in time produces a relatively large change in distance, making for a steeper graph. (View large version50)

On the other hand, for a line that is less steep, a large change in time produces only a little change in distance, meaning a lower speed.

Change in time verus change in distance in a graph that is less steep51
Change in time versus change in distance in a graph that is less steep (View large version52)
Change in time versus change in distance in a graph that is less steep53
Change in time versus change in distance in a graph that is less steep (View large version54)

With all of the changes we have made, Boxy is still moving in a linear fashion, just at different speeds. However, with our newly gained knowledge of how changes to distance versus time can affect speed, we can experiment and draw a graph that makes Boxy move in a way that looks natural and realistic.

Let’s take it step by step. First, things in real life start out slow and slowly increase in speed. So, let’s do that.

In all of the iterations of the graph shown below, you will notice that the points at opposite corners remain fixed. This is because we are not changing the duration for which the animation runs, nor are we changing the distance that Boxy travels.

Constructing a custom motion curve55
Constructing a custom motion curve (View large version56)

If Boxy is to follow the graph above, it will move at a slower speed for 0.25 seconds, because the line is less steep starting from 0 to 0.25 seconds, and then it will abruptly switch to a higher speed after 0.25 seconds (the reason being that the line in the graph gets steeper after 0.25 seconds). We will need to smoothen this transition, though; we don’t want any corners — it’s called a motion curve, after all. Let’s convert that corner to a curve.

Constructing a custom motion curve57
Constructing a custom motion curve (View large version58)

Notice the smooth transition that Boxy undergoes from being at rest to gradually increasing in speed.

Boxy following the motion curve above59
Box following the motion curve above (View large version60)

Good! Next, objects in real life progressively slow down before stopping. Let’s change the graph to make that happen. Again, we’ll pick up a point in time after which we would like Boxy to start slowing down. How about around 0.6 seconds? I have smoothened out the transition’s corner to a curve here already.

Final custom motion curve61
Final custom motion curve (View large version62)

Look at Boxy go! A lot more natural, isn’t it?

Boxy following the custom motion curve63
Boxy following the custom motion curve (View large version64)

The curve we drew in place of the corner is actually a collection of many small line segments; and, as you already know, the steeper the line on the graph, the higher the speed, and the flatter the line, the slower the speed. Notice how in the left part of the image, the line segments that make up the curve get steeper and steeper, resulting in a gradual increase in speed, and progressively flatten out on the right side, resulting in the speed progressively decreasing?

A curve is nothing but a collection of many line segments.65
A curve is nothing but a collection of many line segments. (View large version66)

With all of this knowledge, making sense of motion curves becomes much easier. Let’s look at a few examples.

(View Large version)68
Example 169
Example 1 (View large version70)
(View Large version)72
Example 273
Example 2 (View large version74)
(View Large version)76
Example 377
Example 3 (View large version78)

Using Motion Curves In UI Animation Link

The next time you have to animate a UI element, you will have the power of motion curves at your disposal. Whether it’s a slide-out bar, a modal window or a dropdown menu, adding the right amount of animation and making it look smooth and natural will increase the quality of your user interface greatly. It will make the user interface just feel good. Take the slide-out menu below:

See the Pen nJial80 by Nash Vail (@nashvail9681) on CodePen9782.

Clicking on the hamburger menu brings in the menu from left, but the animation feels blocky. Line 51 of the CSS shows that the animation has transition-timing-function set to linear. We can improve this. Let’s head on over to cubic-bezier9183 and create a custom timing function.

If you’re reading this, it’s safe to assume that you’re a designer or a developer or both and, hence, no stranger to cubic bezier curves; there’s a good chance you’ve encountered them at least once. Bezier curves are a marvel. They are used primarily in computer graphics to draw shapes and are used in tools such as Sketch84 and Adobe Illustrator85 to draw vector graphics. The reason why cubic bezier curves are so popular is that they are so easy to use: Just modify the positions of the four different points, and create the kind of curve you need.

Because we always know the initial and final states of the animated object, we can fix two of the points. That leaves just two points whose positions we have to modify. The two fixed points are called anchor points, and the remaning two are control points.

Parts of a bezier curve86
Parts of a bezier curve (View large version87)

As you remember, cubic-bezier accepts four numbers (n1, n2, n3, n4) when you create a custom transition-timing-function. These four numbers represent nothing but the positions of the two control points: n1, n2 represents the x and y coordinates of the first control point, and n3, n4 represents the coordinates of the second control point. Because changing the position of the control points will change the shape of the curve and, hence, our animation overall, the result is the same when any or all of n1, n2, n3, n4 is modified. For example, the figure below represents cubic-bezier(.14, .78, .89, .35):

A cubic bezier curve representing (.14, .78, .89, .35).88
A cubic bezier curve representing (.14, .78, .89, .35) (View large version89)

The math behind these seemingly simple curves90 is fascinating.

All right, all right, let’s get back to where we were going with cubic-bezier9183: creating a custom transition-timing-function. I want the kind of animation in which the menu slides in very quickly and then gracefully slows down and ends:

Adjusting the cubic bezier curve92
Adjusting the cubic bezier curve (View large version93)

This looks good. The animation will start out fast and then slow down, rather than move at a constant speed throughout. I am simply going to copy cubic-bezier(.05, .69, .14, 1) from the top of the page and replace linear with it.

See the Pen nJial95 by Nash Vail (@nashvail9681) on CodePen9782.

See the difference? The second iteration feels much more natural and appealing. Imagine if every animation in your UI followed a natural timing function. How great would that be?

As we’ve seen, motion curves aren’t tricky at all. They are very easy to understand and use. With them, you can take your UI to the next level.

I hope you’ve learned how motion curves work. If you were going through a lot of trial and error to get motion curves to work the way you want, or if you were not using them at all, you should now be comfortable bending them to your will and creating beautiful animations. Because, after all, animation matters.


Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80 ''
  81. 81 ''
  82. 82 ''
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95 ''
  96. 96 ''
  97. 97 ''

↑ Back to top Tweet itShare on Facebook

Nash Vail has a passion for designing building user interfaces. He is currently a Computer Science undergrad and actively looking for internship opportunities. He has his own little blog you should check out, you can find him on Twitter, GitHub, Dribbble or shoot him a mail at

  1. 1

    Sigh… while this is a great article it reminds me of where Flash was about a decade ago. Glad to see things are finally progressing though! Maybe it would be helpful to mine some old ActionScript articles from the early 2000’s?

    • 2

      This is sadly accurate. Flash is not good for many reasons, but back then, when working with Flash, it seems people are encouraged to care more about subtle things like this.

    • 3

      A bit irrelevant but I think what React is giving us now with separation of layout/presentation and code/business logic is what used to be available in Flex or MXML. I still feel like HTML technologies are catching up. The technology back then was solid. I mean Actionscript was a decent language but the delivery method was questionable to some people so we basically threw the baby out with the bathwater.

  2. 4

    Thanks a lot for explaining everything. I’d been using defaults like linear, ease and others timing functions and ran into some amazing animations using cubic beziers, I tried some myself but the result wasn’t as expected. That might be because although I was familiar with the curves and how it worked, I wasn’t completely clear how they actually work.
    Thanks to you, now I have a crystal clear understanding of what’s actually happening and how to use “caesar”. By the way, the sites in example video, do they exist?
    If they do, can I get the link ? Thanks

  3. 5

    One of the best articles in recent time. Thanks Nash!

  4. 6

    I can’t believe I am saying this, but this article makes me miss the ease of Flash. :(

  5. 7

    Great article. I’m recently trying to learn more on making animated layouts such as the examples you’ve shown above, but failed to really find references in terms of what the proper approach of doing so is. Would you happen to know of any good resources to making those types of layouts, whether it’s just to use JavaScript to reposition things or whether there are other methods?


  6. 8

    Rachel Nabors

    August 29, 2016 5:23 am

    Might be of interest while we’re on the topic of cubic-bezier curves and friends, but there’s a complex timing function proposal in the works:

    It would allow spring animations and hopefully the export of timing graphs from After Effects. Feel free to weigh in! Hopefully another step in the right direction for Web Animation.

  7. 9

    Awesome….thanks for writing this.

  8. 10

    Ranjeet Golekar

    August 31, 2016 2:36 pm

    Before reading this article, I used to think Bezier curve is something complex and I will never use them… Now everything is clear! Thanks for a Illustrative article.

  9. 11

    For all us reminiscing the glorious days of flash, greensock is still here and works in html5. All css-transitions, fully hardware accelerated. Just like the good ol days.

    So for anyone doing any kind of serious animation within the browser, check out:

  10. 12

    Awesome, thank you.

  11. 13

    I started my job as a Flash developer some years ago. ActionScript has been my first “programming” language.
    Back then having a web site built entirely in Flash was very cool, simply because it allowed things that was simply impossible to achieve otherwise.
    I’m firmly convinced that Flash kicked off the development of modern standards in both CSS and Javascript, in the effort to catch up with it.

  12. 14

    Daan Weustenraad

    September 28, 2016 11:41 am

    Whoops, wrong post, my bad.


↑ Back to top