Optimizing complex animations means reducing the work the browser does on every frame: animate GPU-friendly x, y, and scale transforms instead of left, top, width, and height, stagger large groups of elements, kill animations you no longer need, and cap the frame rate. The more objects an animation involves, the greater the load on the processor, and the result is lags and compromised smoothness.
In this article, we’ll show you 11 ways to improve the performance of your animations using the popular GreenSock library as an example. You can also use most of the tips with other animation libraries as well.
The tips cover will-change, the stagger parameter, left/top and width/height transformations, from() and to() instead of fromTo(), kill(), blending filters, SVG count, costly CSS properties, lagSmoothing(), and FPS limits, plus a bonus section on the MorphSVGPlugin.
#1 Use will-change for complex transformations
The will-change CSS property lets browsers know in advance which element is going to be changed and in what way so that they can set up their optimizations before the element actually gets changed. These kinds of optimizations can improve page responsiveness by doing potentially expensive work before it’s needed.
To apply it, you only need to indicate what element parameters will change:
will-change: auto;
will-change: scroll-position;
will-change: contents;
will-change: transform; /* Example of <custom-ident> */
will-change: opacity; /* Example of <custom-ident> */
will-change: left, top;
Important: the will-change property is not for frequent use but rather as a last resort to try and resolve existing performance issues. Don’t use it “just in case”. The thing is, the default browser itself should normally perform optimization. If it doesn’t cope, then you can use will-change. Otherwise, if you use it many times on one page, the browser will not understand what to do first, since there’ll be a lot of things to optimize at once.
#2 Use the stagger parameter when animating a lot of elements at the same time
The stagger parameter starts your animations at the specified time interval. Even the smallest playback delay greatly affects the animation, making it slow and twitchy.
Usage example: gsap.to(".item", {opacity:0, stagger: 0.01}); instead of gsap.to(".item", {opacity:0});.
For example, let’s create 1000 elements and try to animate them simultaneously:
See the Pen
Greensock without stagger example by Lokolin (@lokolin)
on CodePen.
and using the stagger parameter:
See the Pen
Greensock stagger example by Lokolin (@lokolin)
on CodePen.
In the first example, the blocks don’t fall smoothly because those 1000 objects are simultaneously animated at once.
#3 Refrain from left/top CSS property transformations
When you animate the left/top properties, it triggers the CSS layout recalculation which takes up quite a lot of resources, since it happens in the main thread of the HTML page. While the X and Y transformation uses the GPU and does not recalculate the layout.
For example, instead of gsap.to ("# item", {left: "150px", top: "150px", duration: 3,}); it’s better to use gsap.to ("# item", {x: "150px", y: "150px", duration: 3,}).
#4 If possible, better use from() or to() methods instead of fromTo()
We recommend not to use fromTo() often to not force the processor to make additional calculations of the initial state of the animation. Better set the initial/final state of the element in HTML/CSS, and use the GSAP library to set the final/initial state transformation.
In the first example, we use the fromTo() method and set the initial and final state of gsap.fromTo (". Box", {opacity: 0}, {opacity: 1, duration: 2}) animation in GSAP:
See the Pen
Greensock fromTo() example by Lokolin (@lokolin)
on CodePen.
In the second example, we set only the final state of the animation using gsap.to (".box", {opacity: 1, duration: 2}), and the initial one is specified in the CSS class .box opacity:0:
See the Pen
Greensock to() example by Lokolin (@lokolin)
on CodePen.
#5 Don’t forget to remove animations with kill()
Calling animation.kill() immediately stops the animation, removes it from the timeline, and places it in the garbage collection, unloading the processor.
Tip: don’t delete animations that you are going to use in the future. Use pause() for those.
#6 Try to avoid width/height transformations
It’s best to use scaleX/scaleY for resizing where possible. As with the left/top case, when you animate the width/height properties it recalculates the layout, while scaleX and scaleY transformation uses the GPU and doesn’t trigger the layout recalculations.
For example, if you need to hover an element, instead of changing the width/height:
See the Pen
Greensock width/height change by Lokolin (@lokolin)
on CodePen.
use scaleX/scaleY:
See the Pen
Greensock scaleX/scaleY change by Lokolin (@lokolin)
on CodePen.
#7 Try to avoid mix-blend-mode and other blending filters
They are too resource-intensive for optimized animations. Take a look at this example:
See the Pen
GSAP mix-blend-mode example by Lokolin (@lokolin)
on CodePen.
#8 Don’t animate too many SVGs
SVGs are good for most animations because of the image quality, but if you have a lot of SVG images at once they can have a significant impact on the performance of your animations.
There’s no exact number though. It depends not only on the quantity but also on the weight of each piece. A lot of 16×16 px images don’t affect the performance as much as a single 800×800 px complex one could.
#9 Pay attention to what CSS properties you are animating
By animating the CSS property, you resize the element, which entails recalculating the Layout, redrawing the Paint, and composing the Composite. You can learn more about CSS properties on CSS Triggers website.
#10 Set the gsap.ticker.lagSmoothing() method
gsap.ticker.lagSmoothing(1000, 16); lets you avoid animation lags when the CPU starts to freeze.
The first parameter is the threshold (in milliseconds). When the lag becomes greater than this threshold, the engine starts the internal delay clock. As a result, there are no lags at the beginning of the animation.
#11 Set the maximum number of FPS
In video game development, you may have come across a trick of limiting frames per second (FPS). Thus, the animation runs smoothly. Try doing it by adding gsap.ticker.fps(30).
Bonus: Several tips on using the MorphSVGPlugin with GreenSock library
When animating, this plugin allows you to transform the initial state of the first SVG picture into the final state of the second SVG picture. Note that fine-tuned transformation of the initial coordinates into the final coordinates requires a lot of calculations, so here are some tips to help you simplify the calculations:
- Calculate and manually set the
shapeIndex(the number of points by which the transformation will be performed). You can calculate the optimal number in advance usingshapeIndex: "log"and then substitute this value, so the plugin does not calculate this number in real-time, but will immediately know how many shapes it has to transform. - Choose a simpler SVG map redrawing algorithm:
"size" | "position" | "complexity", wherecomplexityis the fastest way. - Precompile. If the animation does not change depending on user behavior, then you can use
precompile:"log".
gsap.to("#id", 1, {morphSVG:{shape:"#otherID", precompile:"log"}});
//now you can grab the value from the console and drop it in...
gsap.to("#id", 1, {morphSVG:{shape:"#otherID", precompile:["M0,0 C100,200 120,500 300,145 34,245 560,46","M0,0 C200,300 100,400 230,400 100,456 400,300"]}});
FAQ
How do you improve animation performance?
Animate properties that don’t trigger layout recalculation: x and y instead of left and top, and scaleX and scaleY instead of width and height, since these transformations use the GPU. Stagger animations of many elements, set initial states in CSS and use to() or from() instead of fromTo(), remove finished animations with kill(), and avoid mix-blend-mode and heavy SVGs.
When should you use the will-change CSS property?
Use will-change as a last resort to resolve existing performance issues, not “just in case”. It tells the browser in advance which element will change and how, so it can prepare optimizations. Browsers normally optimize on their own, and if you use will-change many times on one page, the browser won’t know what to optimize first.
How do you animate many elements at once without lags?
Use the stagger parameter, which starts each element’s animation at a specified interval, for example gsap.to(“.item”, {opacity:0, stagger: 0.01}). In the demo with 1000 elements, animating all of them simultaneously makes the blocks fall unevenly, while a tiny stagger keeps the movement smooth.
Why should you avoid animating left, top, width, and height?
Animating left, top, width, or height triggers a layout recalculation in the main thread of the page, which takes up a lot of resources. X and Y transforms and scaleX and scaleY use the GPU and don’t recalculate the layout. Any CSS property that resizes an element forces the browser to recalculate Layout, redraw Paint, and compose the Composite layer.
How do you limit FPS and smooth out lags in GSAP?
Call gsap.ticker.fps(30) to cap the number of frames per second, a trick borrowed from video game development that keeps animation running smoothly. To avoid lags when the CPU starts to freeze, set gsap.ticker.lagSmoothing(1000, 16): when the lag exceeds the 1000 ms threshold, the engine adjusts its internal clock, so there are no jumps at the start of the animation.
Do SVG animations slow down a page?
They can. SVGs are good for most animations because of image quality, but many SVGs at once significantly affect performance. There is no exact limit: it depends on both the number and the weight of each file. Lots of 16×16 px images affect performance less than a single complex 800×800 px one.
Related reading
