Treehouse

CSS vs. JS Animation: Which is Faster?

By on  

How is it possible that JavaScript-based animation has secretly always been as fast — or faster — than CSS transitions? And, how is it possible that Adobe and Google consistently release media-rich mobile sites that rival the performance of native apps?

This article serves as a point-by-point walkthrough of how JavaScript-based DOM animation libraries, such as Velocity.js and GSAP, are more performant than jQuery and CSS-based animation libraries.

jQuery

Let's start with the basics: JavaScript and jQuery are falsely conflated. JavaScript animation is fast. jQuery slows it down. Why? Because — despite jQuery being tremendously powerful — it was never jQuery's design goal to be a performant animation engine:

Note that layout thrashing is what causes stuttering at the start of animations, garbage collection is what causes stuttering during animations, and the absence of RAF is what generally produces low frame rates.

Implementation Examples

Avoiding layout thrashing consists of simply batching together DOM queries and DOM updates:

var currentTop,
	currentLeft;

/* With layout thrashing. */
currentTop = element.style.top; /* QUERY */
element.style.top = currentTop + 1; /* UPDATE */

currentLeft = element.style.left; /* QUERY */
element.style.left = currentLeft + 1; /* UPDATE */

/* Without layout thrashing. */
currentTop = element.style.top; /* QUERY */
currentLeft = element.style.left; /* QUERY */

element.style.top = currentTop + 1; /* UPDATE */
element.style.left = currentLeft + 1; /* UPDATE */

Queries that take place after an update force the browser to recalculate the page’s computed style data (while taking the new update’s effects into consideration). This produces significant overhead for animations that are running over tiny intervals of just 16ms.

Similarly, implementing RAF doesn’t necessitate a significant reworking of your existing codebase. Let’s compare the basic implementation of RAF against that of setInterval:

var startingTop = 0;

/* setInterval: Runs every 16ms to achieve 60fps (1000ms/60 ~= 16ms). */
setInterval(function() {
	/* Since this ticks 60 times a second, we divide the top property's increment of 1 unit per 1 second by 60. */
    element.style.top = (startingTop += 1/60);
}, 16);

/* requestAnimationFrame: Attempts to run at 60fps based on whether the browser is in an optimal state. */
function tick () {
    element.style.top = (startingTop += 1/60);
}

window.requestAnimationFrame(tick);

RAF produces the biggest possible boost to animation performance that you could make with a single change to your code.

CSS Transitions

CSS transitions outperform jQuery by offloading animation logic to the browser itself, which is efficient at 1) optimizing DOM interaction and memory consumption to avoid stuttering, 2) leveraging the principles of RAF under the hood and 3) forcing hardware acceleration (leveraging the power of the GPU to improve animation performance).

The reality, however, is that these optimizations can also be performed directly within JavaScript. GSAP has been doing it for years. Velocity.js, a new animation engine, not only leverages these same techniques but also goes several steps beyond -- as we'll explore shortly.

Coming to terms with the fact that JavaScript animation can rival CSS animation libraries is only step one in our rehab program. Step two is realizing that JavaScript animation can actually be faster than them.

Let’s start by examining the weaknesses of CSS animation libraries:

  • Transitions' forced hardware acceleration taxes GPU's, resulting in stuttering and banding in high-stress situations. These effects are exacerbated on mobile devices. (Specifically, the stuttering is a result of the overhead that occurs when data is transferred between the browser's main thread and its compositor thread. Some CSS properties, like transforms and opacity, are immune to this overhead.) Adobe elaborates on this issue here.
  • Transitions do not work below Internet Explorer 10, causing accessibility problems for desktop sites since IE8 and IE9 remain very popular.
  • Because transitions aren't natively controlled by JavaScript (they are merely triggered by JavaScript), the browser does not know how to optimize transitions in sync with the JavaScript code that manipulates them.

Conversely: JavaScript-based animation libraries can decide for themselves when to enable hardware acceleration, they inherently work across all versions of IE, and they’re perfectly suited for batched animation optimizations.

My recommendation is to use raw CSS transitions when you're exclusively developing for mobile and your animations consist solely of simple state changes. In such circumstances, transitions are a performant and native solution that allow you to retain all animation logic inside your stylesheets and avoid bloating your page with JavaScript libraries. However, if you're designing intricate UI flourishes or are developing an app with a stateful UI, always use an animation library so that your animations remain performant and your workflow remains manageable. One library in particular that does a fantastic job at managing CSS transitions is Transit.

JavaScript Animation

Okay, so JavaScript can have the upper hand when it comes to performance. But exactly how much faster can JavaScript be? Well — to start — fast enough to build an intense 3D animation demo that you typically only see built with WebGL. And fast enough to build a multimedia teaser that you typically only see built with Flash or After Effects. And fast enough to build a virtual world that you typically only see built with canvas.

To directly compare the performance of leading animation libraries, including Transit (which uses CSS transitions), head on over to Velocity's documentation at VelocityJS.org.

The question remains: How exactly does JavaScript reach its high levels of performance? Below is a short list of optimizations that JavaScript-based animation is capable of performing:

  • Synchronizing the DOM → tween stack across the entirety of the animation chain in order to minimize layout thrashing.
  • Caching property values across chained calls in order to minimize the occurrence of DOM querying (which is the Achilles' heel of performant DOM animation).
  • Caching unit conversion ratios (e.g. px to %, em, etc.) across sibling elements in the same call.
  • Skipping style updating when updates would be visually imperceptible.

Revisiting what we learned earlier about layout thrashing, Velocity.js leverages these best practices to cache the end values of an animation to be reused as the start values of the ensuing animation — thus avoiding requerying the DOM for the element’s start values:

$element
	/* Slide the element down into view. */
	.velocity({ opacity: 1, top: "50%" })
	/* After a delay of 1000ms, slide the element out of view. */
	.velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });

In the above example, the second Velocity call knows that it should automatically start with an opacity value of 1 and a top value of 50%.

The browser could ultimately perform many of these same optimizations itself, but doing so would entail aggressively narrowing the ways in which animation code could be crafted by the developer. Accordingly, for the same reason that jQuery doesn't use RAF (see above), browsers would never impose optimizations that have even a tiny chance of breaking spec or deviating from expected behavior.

Finally, let’s compare the two JavaScript animation libraries (Velocity.js and GSAP) against one another.

  • GSAP is a fast, richly-featured animation platform. Velocity is a lightweight tool for drastically improving UI animation performance and workflow.
  • GSAP requires a licensing fee for various types of businesses. Velocity is freely open-sourced via the ultra-permissive MIT license.
  • Performance-wise, GSAP and Velocity are indistinguishable in real-world projects.

My recommendation is to use GSAP when you require precise control over timing (e.g. remapping, pause/resume/seek), motion (e.g. bezier curve paths), or complex grouping/sequencing. These features are crucial for game development and certain niche applications, but are less common in web app UI’s.

Velocity.js

Referencing GSAP’s rich feature set is not to imply that Velocity itself is light on features. To the contrary. In just 7Kb when zipped, Velocity not only replicates all the functionality of jQuery’s $.animate(), but it also packs in color animation, transforms, loops, easings, class animation, and scrolling.

In short, Velocity is the best of jQuery, jQuery UI, and CSS transitions combined.

Further, from a convenience viewpoint, Velocity uses jQuery's $.queue() method under the hood, and thus interoperates seamlessly with jQuery's $.animate(), $.fade(), and $.delay() functions. And, since Velocity's syntax is identical to $.animate()'s, none of your page’s code needs to change.

Let’s take a quick look at Velocity.js. At a basic level, Velocity behaves identically to $.animate():

$element
	.delay(1000)
	/* Use Velocity to animate the element's top property over a duration of 2000ms. */
	.velocity({ top: "50%" }, 2000)
	/* Use a standard jQuery method to fade the element out once Velocity is done animating top. */
	.fadeOut(1000);

At its most advanced level, complex scrolling scenes with 3D animations can be created — with merely two simple lines of code:

$element
	/* Scroll the browser to the top of this element over a duration of 1000ms. */
	.velocity("scroll", 1000)
	/* Then rotate the element around its Y axis by 360 degrees. */
	.velocity({ rotateY: "360deg" }, 1000);

Wrapping Up

Velocity's goal is to remain a leader in DOM animation performance and convenience. This article has focused on the former. Head on over to VelocityJS.org to learn more about the latter.

Before we conclude, remember that a performant UI is about more than just choosing the right animation library. The rest of your page should also be optimized. Learn more from these fantastic Google talks:

Julian Shapiro

About Julian Shapiro

Julian Shapiro is a startup founder and full-stack developer from Vancouver. Read more about him at Julian.com.

ydkjs-3.png

Recent Features

Incredible Demos

  • Fancy FAQs with jQuery Sliders

    Frequently asked questions can be super boring, right? They don't have to be! I've already shown you how to create fancy FAQs with MooTools -- here's how to create the same effect using jQuery. The HTML Simply a series of H3s and DIVs wrapper...

  • dwProgressBar v2:  Stepping and Events

    dwProgressBar was a huge hit when it debuted. For those of you who didn't catch my first post, dwProgressBar is a MooTools 1.2-based progress bar which allows for as much flexibility as possible. Every piece of dwProgressBar can be controlled by CSS...

Discussion

  1. Moritz

    Nice article!
    Sadly your link to “3D animation demo” under “JavaScript Animation” headline is broken :(

  2. Great work debunking the ideas I think a lot of mid-weight developers have heard/believe about JavaScript just being bad at animation. Just enough depth to get me excited and not too much to lose my attention! :)

    Can’t wait to try this out in a project Julian! Is it going to be on bower.io any time soon? :)

  3. A large amount of this comes down to exactly which properties are being animated. If an element’s animation requires layout (width, height, top, left) then you are often better animating via a requestAnimationFrame on the main thread. The alternative is to use a CSS transition / animation, but the problem here is that those are often started on a separate thread — the compositor — so you end up doing a hop from the compositor (CSS anim) to main thread (to do layout) and then back again (because the compositor runs the final composition of the page). This is less advantageous than simply main to compositor, which is what a rAF-based animation will give you.

    If, however, you are animating transforms or opacity (as two examples) then those operations can be carried out by the compositor alone, and that gives you two benefits: 1) the browser can do main thread work *while* your animation is handled on the compositor and 2) you avoid two thread hops.

    It’s very tempting to say CSS is faster than JS or vice versa, but the truth is much more nuanced and can change depending on which browser you’re talking about and what architecture it sports. My advice is always the same: profile your options (tools, not rules) and then choose the right approach.

    • Exactly. In fact, some of my tests show Firefox edging out everything else when it comes to CSS transitions performance. Many nuances across browsers and devices, and I’m excited to see speeds continue to increase across the board.

  4. Markus Staab

    For reference: there is a plugin from one of the jquery devs, which makes jQuery use requestAnimationFrame for .animate():

    https://github.com/gnarf/jquery-requestAnimationFrame

    • This plugin has long since been a *fantastic* one-step quick fix that allows you to avoid adding a new library to your project. On the flip side, since it doesn’t address jQuery’s layout thrashing and memory consumption, it doesn’t bring $.animate() to the performance levels of CSS transitions, Velocity, or GSAP. And, on mobile devices, every bit of performance counts.

      For those who don’t mind adding 7kb to their project, Velocity provides more than just a performance boost: its class, transform, color, and loop animation make it a worthwhile consideration as a jQuery $.animate() replacement. If I may say so myself :)

  5. As someone who doesn’t pay much attention to animation, because it doesn’t figure prominently in my work these days – just some user-interface flourishes here and there – I found this post quite informative. Thanks.

  6. Any plans of making it a stand-alone library?

    • That was actually my original intention. But, once I found myself recreating jQuery’s $.queue(), $.offset, and IE8 dimensional value calculations, I threw my hands up in the air and accepted the fact that Velocity would be a jQuery plugin :-p

      It was ultimately the right decision. I speak more about jQuery under “Basic: jQuery” in the documentation.

    • You could still break out the non-jQuery-specific functionality to a separate library, and it’d make it a lot more interesting to many people. Have the best of both worlds.

    • Fair point :) I’m interested in seeing this happen.

      Wouldn’t mind having someone contribute toward it. Otherwise, I’ll do it myself if I have the time.

    • Zack Argyle

      I agree. I love the idea of what you are doing, but I use jQuery less and less these days. It would be awesome if you made an AngularJS Service/Factory for this! People (including me) would eat that crap up!

    • Hey Zack & slikts,

      Some good news! We’re looking into removing jQuery dependence now: https://github.com/julianshapiro/velocity/issues/5

    • Julian,
      I’d be glad to contribute in decoupling Velocity from jQuery. So count on me if there’s still need.

  7. max

    transitions do work on IE9. animations don’t.

    • Jozef Remen

      Not true. Transformations work, but any changes made in time (like transitions and animations do) DON’T work in IE 9.

      Yes, you can get css transformations (2D) like rotate, scale, skew in IE 9. But without changing in time with transitions. Only with JS.

      http://caniuse.com/#feat=css-transitions

  8. Ricky

    “Conversely: JavaScript-based animation libraries can decide for themselves when to enable hardware acceleration, they inherently work across all versions of IE, and they’re perfectly suited for batched animation optimizations.”

    Can they? via translate3d?

  9. Maximo

    Hey david Walsh do you know why css animation and transitions are bit slow on mobile devices i have a galaxy s4 and it performs better on desktop and dont know why

  10. Greggman

    I’m sure you know this but neither setTimeout(.., 16) nor requestAnimationFrame guarantee 60 frames a second. If you’re on a 70hz monitor or a 50hz TV or 90hz Oculus Rift 2 or something is running in the background that’s slowing your PC down, or you’re doing something heavy and your mobile device can’t keep up you’re not going to get 60 fps.

    Just pointing it out because people are going to come by and copy-paste your example, it’s going to work on their local machine but not work as expected in other environments.

    • Great point. This is actually the single biggest issue I’ve had with performance testing Velocity — rAF & browser perf are very sensitive to the current environment. WebGL, in contrast, tends to perform very solidly.

  11. Tony

    This is amazing

  12. thanks. good question answered.

  13. Stuart

    Fantastic article, I really enjoyed reading this and it cleared up some of my previous assumptions

  14. Great article david. And yea as Paul said correctly it always depends on what gets animated where – but to break the believe that CSS is always faster – is a good step forward :)

  15. Kirk Bater

    I just want to give a shoutout to GSAP, or Green-Sock Animation Platform. It’s a really lean javascript driven animation that has no dependancies. It’s also super easy to set up and get started, and I’ve used it for a project or two at work.

    It’s free for everything unless you’re selling to multiple end users or your service is behind a paywall such as Netflix or Hulu.

    http://www.greensock.com/gsap-js/

  16. Great post. Considering your past coverage of Mootools, is there any reason you excluded it from the post?

  17. wow awesome post julian

  18. Denis

    Nice article, thanks!

    I saw demo (http://julian.com/research/velocity/demo.html) of Velocity.js and wondering why they applying styles for every animated element (balls), instead of giving unique id attribute for every element, separately create one style tag and made all changes there? That way we will affect only styles tag and all css changes browser will do under the hood.

    Wouldn’t it be faster?

  19. Awesome post man! And thanks for clarifying all in detail with demo.

  20. Thank you for writing about the technical reasons behind animation performance issues. It’s something I wish we’d talk more about in the community, and I’m glad to see more posts on the topic! Good show!

  21. Wow this was helpful. From now on i know what to use on specific situations

  22. Jonas Windey

    Do you have an example of one of those web applications of Google or Adobe that rival a native app’s performance?

  23. CSS is always faster then jquery.

  24. This is good news as I always felt uncomfortable with animations being solely CSS, after all they are often triggered and controlled by JS, except in the case of UI flourishes. It makes perfect sense to use JS animations and this article does a great job of explaining why, and when, you should.

  25. Thanks for this article. It changes a lot my vision of animations (especially about JS vs CSS).

    Does the ‘will-change’ css property (being well used) should now be taken in consideration? As we now can tell the browser how and when to optimize animations.

  26. Alan

    Would you say Velocity is a better option than GSAP? It looks easier to implement

  27. Actually you have answered the best question to everyone.I was using the J query very less in my site because we also used to prefer animations in most of my site but after reading this article i decided to proceed with your work.Great man thank for sharing this wonderful article to me.

  28. Hi,

    First of all i want to say very good work. Now.

    Very lightweight plugin. Your package is better just for the file size.

    Velocity: 7kb
    TweenLite: 25kb

    I just tested Velocity, jQuery, Greensock on dots animations:

    100 dots
    200
    300

    1000

    Your plugin runs almost the same as jQuery.

    Greensock is the winner.

    Yes you may say that “simple animations”

    I guess you never done flash animations. If you are talking about 1 animation per turn. Any plugin is good, even old schooled JS.

    But often, you may use 1-10-20 animations with different properties and if on quantity tests you score low then you might have to re-design.

    For novice users, beginners i do recomment Velocity. But once you have a test of what another competitor is doing( this case Greensock ) is the best choice.

    Oh an by the way: we are not in 2005 we are in 2014 bandwidth has grown and something like 25kb or 100kb with a good server is nothing, piece of cake.

    Keep up the good work and if one day i will see Velocity on top i will definitely start using it.

    Andrew

    • “Andrew,”

      This is the tenth time this month that a GSAP fanboy has spammed one of my articles with GSAP propaganda hidden as false Velocity praise. (For anyone unfamiliar, GSAP *charges* certain businesses for a license fee whereas Velocity is entirely free — so GSAP people are motivated to spread rumors about its superiority. Very childish.)

      Velocity and GSAP have been speed-compared numerous times, including by me. There is no speed difference.

    • Ritchie

      “Oh an by the way: we are not in 2005 we are in 2014 bandwidth has grown and something like 25kb or 100kb with a good server is nothing, piece of cake.”

      Hefty price to pay if your building for mobile!

  29. Seba

    Excellent article! Very very useful. I use GSAP, and will test Velocity.

  30. To avoid layout thrashing, I use fastDom – https://github.com/wilsonpage/fastdom

    it’s amazing

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!