Achieving 60FPS

Emily Hayman

  • Awkward
  • Unnatural
  • Janky
  • Slow


prevent poor User experience

"Silky smooth" feels fast

  • Frame rate measures a site's responsiveness
  • 60fps is the target for a natural feel
  • =16.7ms to achieve all necessary updates
  • Caveat: More frames, more processing - might result in dropped frames
  • Better to deliver lower frame rates more consistently (30fps)



On initial page load:

  • Download and parse HTML, CSS, JS
  • Evaluate JS
  • Calculate element styles

Let's take a step back.

How is the browser generating pixels from your code?

  • Generate layout of the page from the render tree
  • Where elements are placed is correlated to other elements
  • Forced synchronous layouts (layout thrashing) caused by multiple read / writes 
  • Re-layouts are expensive


geometry of the page

  • Calculation of visual styles
  • Re-paint will only occur once per frame - will only re-draw "dirty" elements
  • Browser will attempt to consolidate re-paint regions in a single "dirty" rectangle - may need to promote layers to prevent side effects
  • Re-paints are expensive


FIlling in the pixels

  • Default is to paint elements on a single memory layer
  • Separating elements onto compositor layers allows non-destructive changes
  • Only necessary work is to calculate the new position for each layer 
  • CPU draws the layers; GPU composites the layers
  • GPU is very efficient at basic drawing operations
  • Changes on GPU-composited layers are the least expensive


generating the layers

  • Transform
  • Opacity
  • Seriously, that's it.

How do i take advantage of this?

only change properties that trigger compositing  

Transform allows:

  • Position (transform: translateX(5px))
  • Scale (transform: scale(2))
  • Rotation (transform: rotate(90deg))
  • Skew (transform: skewX(50deg))
  • Matrix (transform: matrix3d(...))

Get Creative


Instead of:

Get Creative


.box {
    left: 10px;
    position: absolute;
    transition: .2s left;
    &.active {
        left: 20px;

Try this:

.box {
    transform: translateX(10px);
    transition: .2s transform;
    &.active {
        transform: translateX(20px);

Instead of:

Get Creative


.box {
    background-color: rgba(0,0,0,.9);
    transition: .2s background-color;
    &.active {
        background-color: rgba(0,0,0,1);

Try this:

.box {
    opacity: .9;
    background-color: #000;
    transition: .2s opacity;
    &.active {
        opacity: 1;
  • GPU handles opacity changes by painting the element texture with a lower alpha value
  • Must be on its own layer

Get Creative


  • Forcing promotion ensures layer is painted and ready
  • Consider promoting expensive paint elements (position: fixed; overflow: scroll;)
  • Will fix "flickering" or "shimmy"

What else can i do?

manually promote elements to their own compositor layer

  • backface-visibility: hidden;

  • transform: translate3d(0,0,0);
  • "Tricks" the browser into promoting the element

the old way

"tricking" the browser

  • Provides method for informing the browser what types of optimizations will be needed ahead of time 
  • Current support: Chrome + Firefox

The shiny, new method


  • will-change: auto - default; standard optimizations
  • will-change: scroll-position - indicates upcoming animation of an element's scroll position
  • will-change: contents - indicates content is expected to change
  • will-change: <property> - define property (for example, "transform")

The shiny, new method


  • The browser already does its best to optimize - stronger will-change optimizations are resource heavy
  • If animating non-GPU properties, browser forced to re-paint on the CPU and then upload to the GPU - can potentially be even more costly
  • Using will-change implies element is always moments away from changing

as in all things, moderation

there can be too many composited layers

quick example

what not to do

quick example

what to do

imperative vs. declarative

how do i best accomplish my goal?

Two methods of creating web animations:

  • CSS (Declarative)
  • Javascript (Imperative)

declarative animations

utilize css

  • Browser can make optimizations - it knows the end point of the operation
  • Can run operations off the main thread
  • Missing the expressive power of JS animations; may grow overly complex
  • Recommend toggling a class using JS for basic user input actions
  • Generally, the more performant option

Imperative animations

utilize Javascript

  • Runs on the main thread - more likely to result in dropped frames
  • More control - responsive to user input; start/ stop easily
  • Generally, the less performant option

What if i need to use JS?


  • The setTimeout of the future - native API for running an animation
  • Typically called at 60fps; requests animation drawing at the next available opportunity - not a set interval
  • Browser optimizes performance and groups into a single repaint (saves CPU cycles)
  • IE10+ support


function doSomething() {
    // Do stuff

What if i need to use JS?

avoid layout thrashing

  • Read, then write
  • Back and forth read / writing will cause reflows
  • Browser tracks "dirty" elements and queues up changes until necessary; reading certain properties forces premature re-calculations
for(i = 0; i < el.length; i++){
  var width = element.offsetWidth * 2;
  el[i].style.width = width + 'px';
var width = element.offsetWidth * 2;
for(i = 0; i < el.length; i++){
  el[i].style.width = width + 'px';



upcoming optimizations

css containment

  • "contain" property: indicate an element's subtree is independent from the rest of the page
  • Lets the browser know it's safe to optimize an element
  • Can indicate "layout", "style", or "paint" - or "strict" for all
  • Ensure DOM updates in the subtree do not trigger reflows on parent document

testing animations

chrome developer tools

Rendering Tools:

  • Enable paint flashing
  • Show layer borders
  • Show FPS meter




Made with