<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Tom Wiltzius on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Tom Wiltzius</name>
  </author>
  <link href="https://web.dev/authors/tomwiltzius/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/pfj6ogyKu63pn6IOoJEA.png?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Chrome Product Manager at Google</subtitle>
  
  
  <entry>
    <title>Accelerated Rendering in Chrome</title>
    <link href="https://web.dev/speed-layers/"/>
    <updated>2013-03-11T00:00:00Z</updated>
    <id>https://web.dev/speed-layers/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For most web developers the fundamental model of a web page is the DOM. Rendering is the often obscure process of turning this representation of a page into a picture on the screen. Modern browsers have changed the way rendering works in recent years to take advantage of graphics cards: this is often vaguely referred to as “hardware acceleration”. When talking about a normal web page (i.e. not Canvas2D or WebGL), what does that term really mean? This article explains the basic model that underpins hardware accelerated rendering of web content in Chrome.&lt;/p&gt;
&lt;h3 id=&quot;big,-fatty-caveats&quot;&gt;Big, Fatty Caveats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#big,-fatty-caveats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We’re talking about WebKit here, and more specifically we’re talking about the Chromium port of WebKit. This article covers implementation details of Chrome, not web platform features. The web platform and standards don’t codify this level of implementation detail, so there are no guarantees anything in this article will apply to other browsers, but knowledge of internals can nevertheless be useful for advanced debugging and performance tuning.&lt;/p&gt;
&lt;p&gt;Also, note that this entire article is discussing a core piece of Chrome’s rendering architecture that’s changing very fast. This article attempts to cover only stuff that’s unlikely to change, but no guarantees that it’ll all still apply in six months.&lt;/p&gt;
&lt;p&gt;It’s important to understand that Chrome has had two different rendering paths for a while now: the hardware-accelerated path and the older software path. As of this writing all pages go down the hardware accelerated path on Windows, ChromeOS, and Chrome for Android. On Mac and Linux only pages that need compositing for some of their content go down the accelerated path (see below for more on what would require compositing), but soon all pages will go down the accelerated path there, too.&lt;/p&gt;
&lt;p&gt;Lastly, we’re peeking under the hood of the rendering engine and looking at features of it that have a big impact on performance. When trying to improve the performance of your own site it can be helpful to understand the layer model, but it’s also easy to shoot yourself in the foot: layers are useful constructs, but creating lots of them can introduce overhead throughout the graphics stack. Consider yourself forewarned!&lt;/p&gt;
&lt;h2 id=&quot;from-the-dom-to-the-screen&quot;&gt;From the DOM to the Screen &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#from-the-dom-to-the-screen&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;introducing-layers&quot;&gt;Introducing Layers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#introducing-layers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once a page is loaded and parsed, it&#39;s represented in the browser as a structure many web developers are familiar with: DOM. When rendering a page, however, the browser has a series of intermediate representations that aren’t directly exposed to developers. The most important of these structures is a layer.&lt;/p&gt;
&lt;p&gt;In Chrome there are actually several different types of layers: RenderLayers, which are responsible for subtrees of the DOM, and GraphicsLayers, which are responsible for subtrees of RenderLayers. The latter is most interesting to us here, because GraphicsLayers are what get uploaded to the GPU as textures. I’ll just say “layer” from here on out to mean GraphicsLayer.&lt;/p&gt;
&lt;p&gt;Quick aside on GPU terminology: what’s a texture? Think of it as a bitmap image that’s moved from main memory (i.e. RAM) to video memory (i.e. VRAM, on your GPU). Once it’s on the GPU, you can map it to a mesh geometry -- in video games or CAD programs, this technique is used to give skeletal 3D models “skin.” Chrome uses textures to get chunks of web page content onto the GPU. Textures can be cheaply mapped to different positions and transformations by applying them to a really simple rectangular mesh. This is how 3D CSS works, and it’s also great for fast scrolling -- but more on both of these later.&lt;/p&gt;
&lt;p&gt;Let’s look at a couple examples to illustrate the layer concept.&lt;/p&gt;
&lt;p&gt;A very useful tool when studying layers in Chrome is the “show composited layer borders” flag in the settings (i.e. little cog icon) in Dev Tools, under the “rendering” heading. It very simply highlights where layers are on-screen. Let’s turn it on. These screenshots and examples are all taken from the latest Chrome Canary, Chrome 27 at the time of this writing.&lt;/p&gt;
&lt;h4 id=&quot;figure-1-a-single-layer-page&quot;&gt;Figure 1: A single-layer page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#figure-1-a-single-layer-page&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;I am a strange root.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figure&gt;
&lt;img alt=&quot;Screenshot of composited layer render borders around the page&amp;#x27;s base layer&quot; decoding=&quot;async&quot; height=&quot;546&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8MgP6ksmtGYtdhxpOD3d.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Screenshot of composited layer render borders around the page&#39;s base layer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This page has just one layer. The blue grid represents tiles, which you can think of as sub-units of a layer that Chrome uses to upload parts of a large layer at a time to the GPU. They aren’t really important here.&lt;/p&gt;
&lt;h4 id=&quot;figure-2-an-element-in-its-own-layer&quot;&gt;Figure 2: An element in its own layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#figure-2-an-element-in-its-own-layer&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-30deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    I am a strange root.&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figure&gt;
&lt;img alt=&quot;Screenshot of rotated layer&amp;#x27;s render borders&quot; decoding=&quot;async&quot; height=&quot;546&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RPnnbpETWy8imXGcdyzB.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Screenshot of rotated layer&#39;s render borders&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By putting a 3D CSS property on the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that rotates it, we can see what it looks like when an element gets its own layer: note the orange border, which outlines a layer in this view.&lt;/p&gt;
&lt;h3 id=&quot;layer-creation-criteria&quot;&gt;Layer Creation Criteria &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#layer-creation-criteria&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What else gets its own layer? Chrome’s heuristics here have evolved over time and continue to, but currently any of the following trigger layer creation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3D or perspective transform CSS properties&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements using accelerated video decoding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; elements with a 3D (WebGL) context or accelerated 2D context&lt;/li&gt;
&lt;li&gt;Composited plugins (i.e. Flash)&lt;/li&gt;
&lt;li&gt;Elements with CSS animation for their opacity or using an animated transform&lt;/li&gt;
&lt;li&gt;Elements with accelerated CSS filters&lt;/li&gt;
&lt;li&gt;Element has a descendant that has a compositing layer (in other words if the element has a child element that’s in its own layer)&lt;/li&gt;
&lt;li&gt;Element has a sibling with a lower z-index which has a compositing layer (in other words the it’s rendered on top of a composited layer)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;practical-implications-animation&quot;&gt;Practical Implications: Animation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#practical-implications-animation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We can move layers around, too, which makes them very useful for animation.&lt;/p&gt;
&lt;h4 id=&quot;figure-3-animated-layers&quot;&gt;Figure 3: Animated Layers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#figure-3-animated-layers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slide&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-iteration-count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; alternate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gray&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;120deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;I am a strange root.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As mentioned earlier, layers are really useful for moving around static web content. In the basic case, Chrome paints the contents of a layer into a software bitmap before uploading it to the GPU as a texture. If that content doesn’t change in the future, it doesn’t need to be repainted. This is a Good Thing: repainting takes time that can be spent on other stuff, like running JavaScript, and if the paint is long it causes hitches or delays in animations.&lt;/p&gt;
&lt;p&gt;See, for instance, this view of Dev Tools timeline: there are no paint operations while this layer is rotating back and forth.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Dev Tools timeline during animation&quot; decoding=&quot;async&quot; height=&quot;613&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/DR2XNrza5wIEtmUvOCOV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Screenshot of Dev Tools timeline during animation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;invalid-repainting&quot;&gt;Invalid! Repainting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#invalid-repainting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;But if the layer’s content changes, it has to be repainted.&lt;/p&gt;
&lt;h4 id=&quot;figure-4-repainting-layers&quot;&gt;Figure 4: Repainting Layers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#figure-4-repainting-layers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slide&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-iteration-count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;animation-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; alternate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gray&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;120deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;I am a strange root.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;paint&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;repaint&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; w &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;paint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;px&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Every time the input element is clicked the rotating element gets 1px wider. This causes relayout and repainting of the entire element, which in this case is an entire layer.&lt;/p&gt;
&lt;p&gt;A good way to see what’s getting painted is with the “show paint rects” tool in Dev Tools, also under the “Rendering” heading of Dev Tools’ settings. After turning it on, notice that the animated element and the button both flash red when the button is clicked.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of show paint rects checkbox&quot; decoding=&quot;async&quot; height=&quot;613&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y5Hc9g99nIZKq20FpkIH.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Screenshot of show paint rects checkbox&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The paint events show up in the Dev Tools timeline, too. Sharp-eyed readers might notice there are two paint events there: one for the layer, and one for the button itself, which gets repainted when it changes to/from its depressed state.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Dev Tools Timeline repainting a layer&quot; decoding=&quot;async&quot; height=&quot;876&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kjuxPtqQY01XKyP9oLkL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Screenshot of Dev Tools Timeline repainting a layer&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Note that Chrome doesn’t always need to repaint the entire layer, it tries to be smart about only repainting the part of the DOM that’s been invalidated. In this case, the DOM element that we modified is the size of the entire layer. But in many other cases, there will be lots of DOM elements in a layer.&lt;/p&gt;
&lt;p&gt;An obvious next question is what causes an invalidation and forces a repaint. This is tricky to answer exhaustively because there are a lot of edge cases that can force invalidations. The most common cause is dirtying the DOM by manipulating CSS styles or causing relayout. Tony Gentilcore has a great &lt;a href=&quot;http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html&quot; rel=&quot;noopener&quot;&gt;blog post on what causes relayout&lt;/a&gt;, and Stoyan Stefanov has an &lt;a href=&quot;http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot; rel=&quot;noopener&quot;&gt;article that covers painting&lt;/a&gt; in more detail (but it ends with just painting, not this fancy compositing stuff).&lt;/p&gt;
&lt;p&gt;The best way to figure out if its affecting something you’re working on is to use the Dev Tools Timeline and Show Paint Rects tools to see if you’re repainting when you wish you weren’t, then try to identify where you dirtied the DOM right before that relayout/repaint. If painting is inevitable but seems to be taking unreasonably long, check out &lt;a href=&quot;http://updates.html5rocks.com/2013/02/Profiling-Long-Paint-Times-with-DevTools-Continuous-Painting-Mode&quot; rel=&quot;noopener&quot;&gt;Eberhard Gräther’s article&lt;/a&gt; on continuous painting mode in Dev Tools.&lt;/p&gt;
&lt;h3 id=&quot;putting-it-together-dom-to-screen&quot;&gt;Putting it together: DOM to Screen &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#putting-it-together-dom-to-screen&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So how does Chrome turn the DOM into a screen image? Conceptually, it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Takes the DOM and splits it up into layers&lt;/li&gt;
&lt;li&gt;Paints each of these layers independently into software bitmaps&lt;/li&gt;
&lt;li&gt;Uploads them to the GPU as textures&lt;/li&gt;
&lt;li&gt;Composites the various layers together into the final screen image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That all needs to happen the first time Chrome produces a frame of a web page. But then it can take some shortcuts for future frames:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If certain CSS properties change, it isn’t necessary to repaint anything. Chrome can just recomposite the existing layers that are already sitting on the GPU as textures, but with different compositing properties (e.g. in different positions, with different opacities, etc).&lt;/li&gt;
&lt;li&gt;If part of a layer gets invalidated, it gets repainted and re-uploaded. If its content remains the same but its composited attributes change (e.g. it gets translated or its opacity changes), Chrome can leave it on the GPU and recomposite to make a new frame.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As should now be clear, the layer-based compositing model has deep implications for rendering performance. Compositing is comparably cheap when nothing needs to be painted, so avoiding repaints of layers is a good overall goal when trying to debug rendering perf. Savvy developers will take a look at the list of compositing triggers above and realize that its possible to easily force the creation of layers. But beware just blindly creating them, as they’re not free: they take up memory in system RAM and on the GPU (particularly limited on mobile devices) and having lots of them can introduce other overhead in the logic keeps track of which are visible. Many layers can also actually increase time spent rasterizing if they layers are large and overlap a lot where they didn’t previously, leading to what’s sometimes referred to as “overdraw”. So use your knowledge wisely!&lt;/p&gt;
&lt;p&gt;That’s all for now. Stay tuned for a couple more articles on practical implications of the layer model.&lt;/p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-layers/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/scrolling/&quot; rel=&quot;noopener&quot;&gt;Scrolling Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://updates.html5rocks.com/2013/02/Profiling-Long-Paint-Times-with-DevTools-Continuous-Painting-Mode&quot; rel=&quot;noopener&quot;&gt;Profiling Long Paint Times with DevTools&#39; Continuous Painting Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://aerotwist.com/blog/on-translate3d-and-layer-creation-hacks/&quot; rel=&quot;noopener&quot;&gt;http://aerotwist.com/blog/on-translate3d-and-layer-creation-hacks/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Tom Wiltzius</name>
    </author>
  </entry>
  
  <entry>
    <title>Jank busting for better rendering performance</title>
    <link href="https://web.dev/speed-rendering/"/>
    <updated>2012-11-02T00:00:00Z</updated>
    <id>https://web.dev/speed-rendering/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You want your web app to feel responsive and smooth when doing animations, transitions, and other small UI effects. Making sure these effects are jank-free can mean the difference between a &amp;quot;native&amp;quot; feel or a clunky, unpolished one.&lt;/p&gt;
&lt;p&gt;This is the first in a series of articles covering rendering performance optimization in the browser. To kick things off, we&#39;ll cover why smooth animation is difficult and what needs to happen to achieve it, as well as a few easy best practices. Many of these ideas were originally presented in &amp;quot;Jank Busters,&amp;quot; a talk Nat Duca and I gave at Google I/O talk (&lt;a href=&quot;https://www.youtube.com/watch?v=hAzhayTnhEI&amp;amp;feature=plcp&quot; rel=&quot;noopener&quot;&gt;video&lt;/a&gt;) this year.&lt;/p&gt;
&lt;h2 id=&quot;introducing-v-sync&quot;&gt;Introducing V-sync &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#introducing-v-sync&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PC gamers might be familiar with this term, but it&#39;s uncommon on the web: what is &lt;a href=&quot;http://en.wikipedia.org/wiki/Screen_tearing#V-sync&quot; rel=&quot;noopener&quot;&gt;v-sync&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;Consider your phone&#39;s display: it refreshes on a regular interval, usually (but not always!) about 60 times a second. V-sync (or vertical synchronization) refers to the practice of generating new frames only between screen refreshes. You might think of this like a race condition between the process that writes data into the screen buffer and the operating system reading that data to put it on the display. We want the buffered frame contents to change in between these refreshes, not during them; otherwise the monitor will display half of one frame and half of another, leading to &amp;quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Screen_tearing&quot; rel=&quot;noopener&quot;&gt;tearing&lt;/a&gt;&amp;quot;.&lt;/p&gt;
&lt;p&gt;To get a smooth animation you need a new frame to be ready every time a screen refresh happens. This has two big implications: frame timing (that is, when the frame needs to be ready by) and frame budget (that is, how long the browser has to produce a frame). You only have the time between screen refreshes to complete a frame (~16ms on a 60Hz screen), and you want to start producing the next frame as soon as the last one was put up on the screen.&lt;/p&gt;
&lt;h2 id=&quot;timing-is-everything-requestanimationframe&quot;&gt;Timing is Everything: requestAnimationFrame &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#timing-is-everything-requestanimationframe&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many web developers use &lt;code&gt;setInterval&lt;/code&gt; or &lt;code&gt;setTimeout&lt;/code&gt; every 16 milliseconds to create animations. This is a problem for a variety of reasons (and we&#39;ll discuss more in a minute), but of particular concern are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Timer resolution from JavaScript is only on the order of several milliseconds&lt;/li&gt;
&lt;li&gt;Different devices have different refresh rates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recall the frame timing problem mentioned above: you need a completed animation frame, finished with any JavaScript, DOM manipulation, layout, painting, etc, to be ready before the next screen refresh occurs. Low timer resolution can make it difficult to get animation frames completed before the next screen refresh, but variation in screen refresh rates makes it impossible with a fixed timer. No matter what the timer interval is you&#39;ll slowly drift out of the timing window for a frame and end up dropping one. This would happen even if the timer fired with millisecond accuracy, which it won&#39;t (as developers have &lt;a href=&quot;http://www.nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/&quot; rel=&quot;noopener&quot;&gt;discovered&lt;/a&gt;) -- timer resolution varies depending on whether the machine is on battery vs. plugged in, can be affected by background tabs hogging resources, etc. Even if this is rare (say, every 16 frames because you were off by a millisecond) you&#39;ll notice: you&#39;ll be dropping several frames a second. You&#39;ll also be doing the work to generate frames that never get displayed, which wastes power and CPU time you could be spending doing other things in your application.&lt;/p&gt;
&lt;p&gt;Different displays have different refresh rates: 60Hz is common, but some phones are 59Hz, some laptops drop to 50Hz in low-power mode, some desktop monitors are 70Hz.&lt;/p&gt;
&lt;p&gt;We tend to focus on frames per second (FPS) when discussing rendering performance, but variance can be an even bigger problem. Our eyes notice the tiny, irregular hitches in animation that a poorly timed animation can produce.&lt;/p&gt;
&lt;p&gt;The way to get correctly timed animation frames is with &lt;a href=&quot;http://docs.webplatform.org/wiki/apis/timing/methods/requestAnimationFrame&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/a&gt;. When you use this API, you&#39;re asking the browser for an animation frame. Your callback gets called when the browser is soon going to produce a new frame. This happens no matter what the refresh rate is.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt; has other nice properties too:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Animations in background tabs get paused, conserving system resources and battery life.&lt;/li&gt;
&lt;li&gt;If the system can&#39;t handle rendering at the screen&#39;s refresh rate, it can throttle animations and produce the callback less frequently (say, 30 times a second on a 60Hz screen). While this drops framerate in half, it keeps the animation consistent -- and as stated above, our eyes are much more attuned to variance than framerate. A steady 30Hz looks better than 60Hz that misses a few frames a second.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;requestAnimationFrame&lt;/code&gt; has been discussed all over the place already, so refer to articles like &lt;a href=&quot;http://creativejs.com/resources/requestanimationframe/&quot; rel=&quot;noopener&quot;&gt;this one from creative JS&lt;/a&gt; for more info on it, but it&#39;s an important first step to smooth animation.&lt;/p&gt;
&lt;h2 id=&quot;frame-budget&quot;&gt;Frame Budget &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#frame-budget&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since we want a new frame ready on every screen refresh, there&#39;s only the time in between refreshes to do all the work to create a new frame. On a 60Hz display, that means we&#39;ve got about 16ms to run all JavaScript, perform layout, paint, and whatever else the browser has to do to get the frame out. This means if the JavaScript inside your &lt;code&gt;requestAnimationFrame&lt;/code&gt; callback takes longer than 16ms to run, you don&#39;t have any hope of producing a frame in time for v-sync!&lt;/p&gt;
&lt;p&gt;16ms isn&#39;t a lot of time. Luckily Chrome&#39;s Developer Tools can help you track down if you&#39;re blowing your frame budget during the requestAnimationFrame callback.&lt;/p&gt;
&lt;p&gt;Opening up the Dev Tools timeline and taking a recording of this animation in action quickly shows that we&#39;re way over budget when animating. In Timeline switch to &amp;quot;Frames&amp;quot; and take a look:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A demo with far too much layout&quot; decoding=&quot;async&quot; height=&quot;586&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XD8Gwg2C8AabKcspC6uS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A demo with far too much layout&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Those requestAnimationFrame (rAF) callbacks are taking &amp;gt;200ms. That&#39;s an order of magnitude too long to tick out a frame every 16ms! Opening up one of those long rAF callbacks reveals what&#39;s going on inside: in this case, lots of layout.&lt;/p&gt;
&lt;p&gt;Paul&#39;s video goes into more detail about the specific cause of the relayout (it&#39;s reading &lt;code&gt;scrollTop&lt;/code&gt;) and how to avoid it. But the point here is that you can dive into the callback and investigate what&#39;s taking so long.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;An updated demo with much reduced layout&quot; decoding=&quot;async&quot; height=&quot;346&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Y7fNy2kfVQTZhqpqbGgW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;An updated demo with much reduced layout&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Notice the 16ms frame times. That blank space in the frames is the headroom you have to do more work (or let the browser do work it needs to do in the background). That blank space is a Good Thing.&lt;/p&gt;
&lt;h2 id=&quot;other-source-of-jank&quot;&gt;Other Source of Jank &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#other-source-of-jank&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The biggest cause of trouble when trying to run JavaScript-powered animations
is that other stuff can get in the way of your rAF callback, and even prevent it
from running at all. Even if your rAF callback is lean and runs in just a few
milliseconds, other activities (like processing an XHR that just came in,
running input event handlers, or running scheduled updates on a timer) can
suddenly come in and run for any period of time without yielding. On mobile
devices sometimes processing these events can take hundreds of milliseconds,
during which time your animation will be completely stalled. We call those
animation hitches &lt;strong&gt;jank&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There&#39;s no magic bullet to avoid these situations, but there are a few architectural best practices to set yourself up for success:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t do a lot of processing in input handlers! Doing a lot of JS or trying to rearrange the whole page during e.g. an onscroll handler is a very common cause of terrible jankiness.&lt;/li&gt;
&lt;li&gt;Push as much processing (read: anything that&#39;ll take a long time to run) into your rAF callback or &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/workers/basics/&quot; rel=&quot;noopener&quot;&gt;Web Workers&lt;/a&gt; as possible.&lt;/li&gt;
&lt;li&gt;If you push work into the rAF callback, try to chunk it up so you&#39;re only processing a little bit each frame or delay it until after an important animation is over -- this way you can continue to run short rAF callbacks and animate smoothly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a great tutorial covering how to push processing into requestAnimationFrame callbacks rather than input handlers, see Paul Lewis&#39;s article &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/animations/&quot; rel=&quot;noopener&quot;&gt;Leaner, Meaner, Faster Animations with requestAnimationFrame&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;css-animation&quot;&gt;CSS Animation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#css-animation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What&#39;s better than lightweight JS in your event and rAF callbacks? No JS.&lt;/p&gt;
&lt;p&gt;Earlier we said there&#39;s no silver bullet for avoiding interrupting your rAF callbacks, but you can use CSS animation to avoid the need for them entirely. On Chrome for Android in particular (and other browsers are working on similar features), CSS animations have the very desirable property that the browser can often run them even if JavaScript is running.&lt;/p&gt;
&lt;p&gt;There&#39;s an implicit statement in the above section on jank: browsers can only do one thing at a time. This isn&#39;t strictly true, but it&#39;s a good working assumption to have: at any given time the browser can be running JS, performing layout, or painting, but only one at a time. This can be verified in the Timeline view of Dev Tools. One of the exceptions to this rule is CSS animations on Chrome for Android (and soon desktop Chrome, though not yet).&lt;/p&gt;
&lt;p&gt;When possible, using a CSS animation both simplifies your application and lets animations run smoothly, even while JavaScript runs.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ for info on rAF polyfills&lt;/span&gt;&lt;br /&gt;  rAF &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestAnimationFrame&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; degrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webkitTransform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rotate(&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; degrees &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deg)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;updated to degrees &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; degrees&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    degrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; degrees &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;rAF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;rAF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you click the button JavaScript runs for 180ms, causing jank. But if instead we drive that animation with CSS animations the jank no longer occurs.&lt;/p&gt;
&lt;p&gt;(Remember at the time of this writing, CSS animation is only jank-free on Chrome for Android, not desktop Chrome.)&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;/* tools like Modernizr (http://modernizr.com/) can help with CSS polyfills */&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;#foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    +&lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    +&lt;span class=&quot;token property&quot;&gt;animation-timing-function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    +&lt;span class=&quot;token property&quot;&gt;animation-animation-iteration-count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    +&lt;span class=&quot;token property&quot;&gt;animation-animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rotate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  @+&lt;span class=&quot;token property&quot;&gt;keyframes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rotate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      +&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      +&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;360deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For more information on using CSS Animations, see articles like &lt;a href=&quot;https://developer.mozilla.org/docs/CSS/Using_CSS_animations&quot; rel=&quot;noopener&quot;&gt;this one on MDN&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;wrapup&quot;&gt;Wrapup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#wrapup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The short of it is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When animating, producing frames for every screen refresh matters.
Vsync&#39;d   animation makes a huge positive impact on the way an app feels.&lt;/li&gt;
&lt;li&gt;The best way to get vsync&#39;d animation in Chrome and other modern browsers is
to use CSS animation. When you need more flexibility than CSS animation
provides, the best technique is requestAnimationFrame-based animation.&lt;/li&gt;
&lt;li&gt;To keep rAF animations healthy and happy, make sure other event handlers
aren&#39;t getting in the way of your rAF callback running, and keep rAF callbacks
short (&amp;lt;15ms).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lastly, vsync&#39;d animation doesn&#39;t only apply to simple UI animations -- it applies to Canvas2D animation, WebGL animation, and even scrolling on static pages. In the next article in this series we&#39;ll dig into scrolling performance with these concepts in mind.&lt;/p&gt;
&lt;p&gt;Happy animating!&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-rendering/#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.digitalbackcountry.com/2012/05/using-requestanimationframe-to-optimize-dragging-events/&quot; rel=&quot;noopener&quot;&gt;Using requestAnimationFrame for drag events&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Tom Wiltzius</name>
    </author>
  </entry>
</feed>
