Improving browser rendering performance

Updated . Posted . Visible to the public.

As the web is being used for more and more tasks, expectations rise. Not only should web pages offer rich interaction, they must be responsive in both size and interaction.

This imposes a paradoxon that needs to be solved by building performing applications. It's not enough any more to have your web site do crazy stuff, it is also required to do it crazy fast. This card is intended to give you an introduction to this emerging aspect of web development.

Read this introductory performance study on Pinterest Show archive.org snapshot by Smashing Magazine.

Frame Budget

Most screens have a redraw rate of 60Hz, i.e. a new frame is rendered 60 times a second. Thus, a single frame is 1000ms / 60 = 16.6...ms long. Once a frame renders longer than 16ms, the page will begin to feel staggering and slow.

Google Chrome DevTools

The Chrome DevTools enable you to precisely investigate and pinpoint rendering performance bottlenecks. Your friends are:

a) the timeline tool and
b) the rendering settings (Open DevTools (F12) -> ESC -> "Rendering" tab in drawer)

The timeline tool is particularly helpful, as it helps debugging performance bottlenecks. See this in-depth introduction to the DevTools' timeline tool Show archive.org snapshot . Also, it's well searchable when you just have a question about some function.

Performance issue 1: Slow JavaScript

If an event occurs and JavaScript has to run, the browser have to process the JavaScript first, before it can redraw the screen.

While JavaScript is very fast, you need to be very careful with JavaScript code that is called frequently:

  • Code that runs on very common events like window.scroll has to be fast
  • Code that needs to rerun when your application state changes has to be fast. This applies to everything in an Angular digest cylce for example.

When adding an external library, you need to have a rough understand what it does. Especially dangerous are libraries that were designed for a static page and you have to constantly rerun when the page changes. A good example is the clamp.js library which is not optimized for dynamic pages.

When adding some external library, you need to have a very rough understanding of how it works, and be wary of potentially costly operations.

How to debug: Look for long yellow lines in the Chrome timeline.

Performance issue 2: Layout thrashing

Browser try to first execute all JavaScript and then render and paint.

However, if you manipulate the DOM in a way that causes rendering, and later query layout properties on the same elements, the browser needs to do an extra rendering step just for your JavaScript.

An example would be:

function logBoxHeight() {
  box.classList.add('super-big');
  console.log(box.offsetHeight); // Forces the browser to re-render *now*
}

Verdict: Make sure you always first read, then write style-relevant values.

How to debug: Chrome shows warning icons in the event list in the Timeline for scripts that cause layout trashing.

Performance issue 3: Rendering

Rendering in this context only refers to updating the DOM and calculating styles and layout for DOM elements. It does not refer to actually drawing elements on the screen.

Rendering happens when large parts of the DOM change, when DOM nodes high up in the hierarchy change, or when your resize your browser.

It is slow, when your CSS is very complex. Deeply nested rules and slow selectors are the main cost factors. Using a BEM pattern for CSS should produce relatively fast CSS.

Performance issue 4: Slow repaints

Not all CSS effects are equally fast. Obviously, complicated effects like round borders, gradients, shadows cost performance.

Unfortunately, which effect is how fast depends heavily on browsers and changes constantly with new versions.

One notable example that keeps being very slow are box shadows with high blur radius. Try to avoid them, or keep the blur radius at 1-3 pixels.

How to debug: In the Chrome rendering setting, enable "continuous page repainting". If repaints take longer than a couple of ms, you can try experimenting with disabling effects and comparing the rendering time.

Performance issue 5: Unnecessary repaints

Sometimes, your JavaScript code can cause repaints for parts of your page that have not changed at all. There is no good rule when it happens, but it is easy to check:

How to debug: Enable "paint rectangles" (and disable "continuous repainting") in Chrome rendering settings. Does the page repaint on its own? Do parts of the page repaint where you don't expect it?

Animations: Use requestAnimationFrame()

The requestAnimationFrame function tells the browser to run an animation (basically any piece of code) at the next opportunity. It will also collect all animations you register and run them all in the same frame.

Other resources

Dominik Schöler
Last edit
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Dominik Schöler to makandra dev (2015-09-24 10:08)