Visual jitter ruins the experience of navigating a site. Addy Osmani reveals how to use Chrome DevTools to get your designs running at a steady 60fps.
Whether it’s on desktop or mobile, users want their web experience to be snappy, smooth and delightful. Even if the browser is busy rendering the page or loading in content, the user should still be able to scroll around and interact with it without any slow-down. No one likes seeing visual glitches.
Low or inconsistent frame rates affect not only user experience but user engagement: something that large sites like Flickr are increasingly starting to address. In this article, we will explore how to apply the lessons they have learned to your own sites.
Measurement is the most important part of any performance-profiling work. This article focuses on how to do this within Chrome DevTools. However, always test your sites and apps using the tools in other browsers to check if any issues are browser-specific.
What is jank?
The human eye perceives a continuous stream of information. It does not naturally see motion as a series of frames. In the worlds of animation, film and gaming, using a series of still frames to simulate motion creates some interesting perceptual artifacts – especially if those frames are played back too slowly, or at an inconsistent rate. When the frame rate varies, movements can look jerky, and images can appear to jitter.
For an optimal user experience, animations must be silky, scrolling must be buttery-smooth, and your page must contain little or no ‘jank’ – visual disruption caused by variation in frame rate.
On the web, a low frame rate (or a janky experience) means that the human eye can make out individual frames. Giving users a jank-free experience often comes down to creating sites and applications that can run at a steady 60fps, similar to videogames.
At 60fps, you have 16.66ms for Chrome to complete every task necessary to display one frame of your webpage, including logic processing, painting, layout, image decoding and compositing – and that’s in an ideal world. Factor in miscellaneous browser processes, and the real figure is probably 8-10ms. Go over that limit, and the user will start to experience jank.
What’s magical about the number 60? Well, the frame rates of animations should match the refresh rates of the hardware they are displayed on – which, for most modern devices, is around 60Hz.
Phones usually refresh at a rate of 55-60Hz, laptops at 58-60Hz (although 50Hz in low power mode), while most monitors usually refresh at a rate of 50-62Hz.
What causes jank?
- Long paint times for DOM elements
- Unnecessary image resizes (because you haven’t pre-scaled the image to the size that you require)
- Long image-decoding times
- Unexpected layer invalidations
- Garbage collector runs
- Network requests (for example, processing an XHR)
- Heavy animation or data processing.
Diagnosing slow paint times
Let’s quickly run through what the paint process involves. In the life of a web page the browser generally performs three core tasks: fetching resources, parsing and tokenizing these resources (the HTML/CSS/JS code), and finally drawing things to screen.
During the final task, the browser traverses the render tree – a tree of the visual elements making up the web page – and calls a paint method to display content to the screen. Painting can either be global (against the whole tree) or incremental (partial). The diagram below shows the order in which tasks are completed. It is taken from Tali Garsiel’s How Browsers Work.
Who should you care about this? Well, it’s important to be aware that the browser has to do a lot of work in order to draw things to the screen. Anything you do to increase the complexity of that task (for example, forcing the browser to recalculate the layout of the page) has the potential to introduce jank. You want to avoid this. So let’s talk about tools that you can use to identify potential bottlenecks.
Introducing the Chrome DevTools Timeline
Chrome DevTools’ Timeline panel provides an overview of all the activity in an application as it runs: for example, processing DOM events, rendering page layouts or painting elements to the screen. It can break this information down in three different ways: by Events, Frames or Memory usage.
For this article, we’re interested in Frames mode, which shows the tasks Chrome had to perform to generate a single frame – that is, a single update to the way the application is presented onscreen.
The Timeline won’t display any data by default, so to begin a recording session, you need to open your app and click on the grey circle at the bottom of the pane (or just use the Cmd/Ctrl+E shortcut). The record button will now turn red, and the Timeline will begin to capture information. If you don’t have a site or app of your own to hand, try http://inception-explained.com as this is currently a site with jank.
Complete a few actions inside your app (for example, scrolling) and after a few seconds, click the button again to stop recording.
Hovering over a record will display an extended tooltip with details about the time taken to complete it. Pay attention to these, since they contain a lot of useful information, especially the Call Stack. The Timeline identifies when your app causes a forced asynchronous layout and marks these records with yellow warning icon.
Diagnosing long paint times Last year, Google shared its advice for diagnosing the causes of long paint times. To uncover what styles are slow, Google advised developers to do the following:
- Navigate to a page and open up the Chrome DevTools.
- Take a Timeline recording, noting down the paint times.
- Inspect individual elements, starting with the larger ones more likely to cause significant slow-downs.
- Repeat this process, checking if paint times have gone down. If they have, the last style removed is the culprit, and the others can be added back in.
- Or: use different styles to try to recreate the overall look of the page in a way that reduces total calculation time.
The process for establishing which elements are slow is similar, only rather than disabling styles, it means setting those parts of the DOM to display:none. This works fairly well – but thankfully, Chrome DevTools now contains some newer features we can use to help to troubleshoot paints and repaints. Before we look at them, let’s review what we mean by a ‘repaint’.
What is a repaint?
Each time a user interacts with a page, only parts of it will be changed: for example, they may perform an action that requires the browser to change the visibility of an element, or add an outline to it. Chrome keeps an eye on which parts of the screen need to be changed, creating a ‘damage rectangle’ around the affected area.
Before making the changes, it saves the rectangle as a bitmap, then only paints the delta between the old rectangle and the new one.
The process of updating the page is known as a repaint. In performance terms, a repaint is an expensive operation, and one that, ideally, you want to avoid. If you notice that there are particular areas of a page that require a lot of repainting, it’s useful to investigate what can be done to reduce this.
Diagnosing long paint times: the new way
Google recently added a couple of new features to Chrome DevTools to make it easier to diagnose the causes of long paint times. These are available in Chrome Canary.
First, a new helper enables you to toggle the visibility:hidden setting on an element. When this style is applied to an element, the browser doesn’t paint that element, but otherwise preserves the layout of the page unchanged. To use the shortcut, select a DOM element in the Elements panel and press H.
Second, the Enable continuous page repainting option in the Settings panel helps identify elements that have a high paint cost. It forces Chrome to repaint the page continuously, providing a counter that shows just how long this is taking. To diagnose what is causing the slowdown, keep your eye on this counter, and use H to toggle individual styles on and off.
Let’s look at what a workflow for diagnosing paint issues that makes use of these new tools might look like:
- Open up your page, launch Chrome DevTools and switch to the Timeline panel. Hit record and interact with your page the same way your user would.
- Check the Timeline for any frames that went over budget: that is, that took longer than 16.6ms to calculate. If you’re close to this figure, you’re probably way over budget for mobile devices. Aim to complete all of your work within 10ms to have some margin for error. (If you’re building for mobile – which you should be – you should run this analysis using remote debugging.)
- If it was a paint or layout issue:
a) Go to Settings and check Enable continuous page repainting.
b) Walk through the DOM tree, hiding nonessential elements using the H shortcut. Identify which elements make a big difference to paint times.
c) Once you know there is something about an element that’s slowing the painting down, uncheck styles that could have an impact on paint time (such as box-shadow) and look at frame rate again.
d) Continue until you’ve located the style responsible for the slow-down.
- 5 Rinse and repeat.
Especially on sites that rely heavily on scrolling, you might discover that your main content is relying on overflow:scroll. This is a real challenge, as this scrolling isn’t GPU-accelerated in any way so the content is repainted whenever your user scrolls. You can work around such issues using normal page scroll (overflow:visible) as well as using position:fixed.
Other useful tools
Chrome DevTools also has several other features that can help you to troubleshoot your web apps.
The Rendering section of the Settings panel now includes an option marked Show paint rectangles. Enabling it highlights the part of the screen being repainted in each frame. This provides a simple visual workflow for minimising slow-down: you want to keep the areas being repainted as small as possible.
An older, but equally useful, tool for visualising jank is the real-time FPS meter. Again, you can find this in the Rendering section of the Settings panel: look for the Show FPS meter checkbox. When activated, you will see a dark box in the top-right corner of your page with frame statistics. This can be used during live editing to diagnose what is causing frame rate to drop off without having to switch in and out of the Timeline view.
However, keep in mind that it is easy to miss frames with intermittent jank when using only the FPS meter. You should also note that FPS on desktop differs from that on devices, so be sure to profile performance there too.
Pro tips for troubleshooting
To round off the article, let’s run through a few tips to make troubleshooting pages quicker as well as easier.
- Your code can also use console.time() and console.timeEnd() to mark ranges in DevTools Timeline recordings.
- If you check Show CPU activity on the ruler in the Timeline section of the Settings panel, you can overlay the CPU activity in your Timeline recordings. Light bars indicate the CPU was busy. If you hover over a CPU bar, this highlights the region during which the CPU was active.
- You can drill down to records of a particular type in the Timeline using the Cmd/Ctrl+F shortcut. Just enter the name of a particular record type (for example, scroll) in the search field, and the Timeline will only display the records containing that term.
While I don’t suggest you purely focus on paint or layout, it is useful to be aware of the cost of using certain styles in the wrong way.