<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Paul Lewis on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Paul Lewis</name>
  </author>
  <link href="https://web.dev/authors/paullewis/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/FqkHLK1y5d0ob6JJAQkR.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Paul Lewis.</subtitle>
  
  
  <entry>
    <title>Avoid large, complex layouts and layout thrashing</title>
    <link href="https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Layout is where the browser figures out the geometric information for elements: their size and location in the page. Each element will have explicit or implicit sizing information based on the CSS that was used, the contents of the element, or a parent element. The process is called Layout in Chrome (and derived browsers such as Edge), and Safari. In Firefox it&#39;s called Reflow, but the process is effectively the same.&lt;/p&gt;
&lt;p&gt;Similarly to style calculations, the immediate concerns for layout cost are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The number of elements that require layout, which is a byproduct of the page&#39;s &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The complexity of those layouts.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Layout has a direct effect on interaction latency&lt;/li&gt;
&lt;li&gt;Layout is normally scoped to the whole document.&lt;/li&gt;
&lt;li&gt;The number of DOM elements will affect performance; you should avoid triggering layout wherever possible.&lt;/li&gt;
&lt;li&gt;Avoid forced synchronous layouts and layout thrashing; read style values then make style changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-effects-of-layout-on-interaction-latency&quot;&gt;The effects of layout on interaction latency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#the-effects-of-layout-on-interaction-latency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a user interacts with the page, those interactions should be as fast as possible. The amount of time it takes for an interaction to complete—ending when the browser presents the next frame to show the results of the interaction—is known as &lt;em&gt;interaction latency&lt;/em&gt;. This is an aspect of page performance that the &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint&lt;/a&gt; metric measures.&lt;/p&gt;
&lt;p&gt;The amount of time it takes for the browser to present the next frame in response to a user interaction is known as the interaction&#39;s &lt;em&gt;presentation delay&lt;/em&gt;. The goal of an interaction is to provide visual feedback in order to signal to the user that something has occurred, and visual updates can involve some amount of layout work in order to achieve that goal.&lt;/p&gt;
&lt;p&gt;In order to keep your website&#39;s INP as low as possible, it&#39;s important to avoid layout when possible. If it&#39;s not possible to avoid layout entirely, it&#39;s important to limit that layout work so that the browser can present the next frame quickly.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;avoid-layout-wherever-possible&quot;&gt;Avoid layout wherever possible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-layout-wherever-possible&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you change styles the browser checks to see if any of the changes require layout to be calculated, and for that render tree to be updated. Changes to &amp;quot;geometric properties&amp;quot;, such as widths, heights, left, or top all require layout.&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 selector&quot;&gt;.box&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&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; 20px&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 comment&quot;&gt;/**&lt;br /&gt;  * Changing width and height&lt;br /&gt;  * triggers layout.&lt;br /&gt;  */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.box--expanded&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;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; 350px&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;Layout is almost always scoped to the entire document. If you have a lot of elements, it&#39;s going to take a long time to figure out the locations and dimensions of them all.&lt;/p&gt;
&lt;p&gt;If it&#39;s not possible to avoid layout then the key is to once again use Chrome DevTools to see how long it&#39;s taking, and determine if layout is the cause of a bottleneck. Firstly, open DevTools, go to the Timeline tab, hit record and interact with your site. When you stop recording you&#39;ll see a breakdown of how your site performed:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools showing a long time in Layout.&quot; decoding=&quot;async&quot; height=&quot;602&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When digging into the trace in the above example, we see that over 28 milliseconds is spent inside layout for each frame, which, when we have 16 milliseconds to get a frame on screen in an animation, is far too high. You can also see that DevTools will tell you the tree size (1,618 elements in this case), and how many nodes were in need of layout (5 in this case).&lt;/p&gt;
&lt;p&gt;Keep in mind that the general advice here is to avoid layout &lt;em&gt;whenever possible&lt;/em&gt;—but it isn&#39;t always possible to avoid layout. In cases where you can&#39;t avoid layout, know that the cost of layout has a relationship with the size of the DOM. Although the relationship between the two isn&#39;t tightly coupled, larger DOMs will generally incur higher layout costs.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size and interactivity&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;avoid-forced-synchronous-layouts&quot;&gt;Avoid forced synchronous layouts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-forced-synchronous-layouts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shipping a frame to screen has this order:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Using flexbox as layout.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;First the JavaScript runs, &lt;em&gt;then&lt;/em&gt; style calculations, &lt;em&gt;then&lt;/em&gt; layout. It is, however, possible to force a browser to perform layout earlier with JavaScript. This is called &lt;strong&gt;forced synchronous layout&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The first thing to keep in mind is that as the JavaScript runs all the old layout values from the previous frame are known and available for you to query. So if, for example, you want to write out the height of an element (let&#39;s call it &amp;quot;box&amp;quot;) at the start of the frame you may write some code like this:&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;// Schedule our function to run at the start of the frame:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logBoxHeight&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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logBoxHeight&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;  &lt;span class=&quot;token comment&quot;&gt;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Things get problematic if you&#39;ve changed the styles of the box &lt;em&gt;before&lt;/em&gt; you ask for its height:&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logBoxHeight&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;  box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;super-big&#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;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now, in order to answer the height question, the browser must &lt;em&gt;first&lt;/em&gt; apply the style change (because of adding the &lt;code&gt;super-big&lt;/code&gt; class), and &lt;em&gt;then&lt;/em&gt; run layout. Only then will it be able to return the correct height. This is unnecessary and potentially expensive work.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; While the example above uses the &lt;code&gt;offsetHeight&lt;/code&gt; property, there are &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot;&gt;many properties to be aware of&lt;/a&gt; that can trigger forced synchronous layout. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Because of this, you should always batch your style reads and do them first (where the browser can use the previous frame&#39;s layout values) and then do any writes:&lt;/p&gt;
&lt;p&gt;Done correctly the above function would be:&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logBoxHeight&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;  &lt;span class=&quot;token comment&quot;&gt;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;br /&gt;  box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;super-big&#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;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 the most part you shouldn&#39;t need to apply styles and then query values; using the last frame&#39;s values should be sufficient. Running the style calculations and layout synchronously and earlier than the browser would like are potential bottlenecks, and not something you will typically want to do.&lt;/p&gt;
&lt;h2 id=&quot;avoid-layout-thrashing&quot;&gt;Avoid layout thrashing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-layout-thrashing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s a way to make forced synchronous layouts even worse: &lt;em&gt;do lots of them in quick succession&lt;/em&gt;. Take a look at this code:&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resizeAllParagraphsToMatchBlockWidth&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;  &lt;span class=&quot;token comment&quot;&gt;// Puts the browser into a read-write-read-write cycle.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; paragraphs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    paragraphs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&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 template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetWidth&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&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;This code loops over a group of paragraphs and sets each paragraph&#39;s width to match the width of an element called &amp;quot;box&amp;quot;. It looks harmless enough, but the problem is that each iteration of the loop reads a style value (&lt;code&gt;box.offsetWidth&lt;/code&gt;) and then immediately uses it to update the width of a paragraph (&lt;code&gt;paragraphs[i].style.width&lt;/code&gt;). On the next iteration of the loop, the browser has to account for the fact that styles have changed since &lt;code&gt;offsetWidth&lt;/code&gt; was last requested (in the previous iteration), and so it must apply the style changes, and run layout. This will happen on &lt;em&gt;every single iteration!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The fix for this sample is to once again &lt;em&gt;read&lt;/em&gt; and then &lt;em&gt;write&lt;/em&gt; values:&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;// Read.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetWidth&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resizeAllParagraphsToMatchBlockWidth&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;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; paragraphs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Now write.&lt;/span&gt;&lt;br /&gt;    paragraphs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&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 template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;width&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&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;If you want to guarantee safety, consider using &lt;a href=&quot;https://github.com/wilsonpage/fastdom&quot; rel=&quot;noopener&quot;&gt;FastDOM&lt;/a&gt;, which automatically batches your reads and writes for you, and should prevent you from triggering forced synchronous layouts or layout thrashing accidentally.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@halacious&quot; rel=&quot;noopener&quot;&gt;Hal Gatewood&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Reduce the scope and complexity of style calculations</title>
    <link href="https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/</id>
    <content type="html" mode="escaped">&lt;p&gt;Changing the DOM, through adding and removing elements, changing attributes, classes, or through animation, will all cause the browser to recalculate element styles and, in many cases, layout (or reflow) the page, or parts of it. This process is called &lt;em&gt;computed style calculation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The first part of computing styles is to create a set of matching selectors, which is essentially the browser figuring out which classes, pseudo-selectors, and IDs apply to any given element.&lt;/p&gt;
&lt;p&gt;The second part of the process involves taking all the style rules from the matching selectors and figuring out what final styles the element has.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Roughly half of the time used in Blink (the rendering engine used in Chromium and derived browsers) to calculate the computed style for an element is used to match selectors, and the other half of the time is used to construct the RenderStyle (computed style representation) from the matched rules.  Rune Lillesveen, Opera / &lt;a href=&quot;https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8/view&quot;&gt;Style Invalidation in Blink&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;How reducing style calculation costs can lower interaction latency.&lt;/li&gt;
&lt;li&gt;Reduce the complexity of your selectors; use a class-centric methodology (BEM, for example).&lt;/li&gt;
&lt;li&gt;Reduce the number of elements on which style calculation must be calculated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;style-recalculation-time-and-interaction-latency&quot;&gt;Style recalculation time and interaction latency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#style-recalculation-time-and-interaction-latency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; is a user-centric runtime performance metric that assesses a page&#39;s overall responsiveness to user input. When interaction latency is assessed by this metric, it measures the time starting from when the user interacts with the page, up until the browser paints the next frame showing the corresponding visual updates made to the user interface.&lt;/p&gt;
&lt;p&gt;A significant component of an interaction is the time it takes to paint the next frame. Rendering work done to present the next frame is made up of many parts, including calculation of page styles that occur just prior to layout, paint, and compositing work. While this article focuses solely on style calculation costs, it&#39;s important to emphasize that reducing any part of the rendering phase inherent to interaction will reduce its total latency—style calculation included.&lt;/p&gt;
&lt;h2 id=&quot;reduce-the-complexity-of-your-selectors&quot;&gt;Reduce the complexity of your selectors &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#reduce-the-complexity-of-your-selectors&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the simplest case, you can reference an element in your CSS with just a class:&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 selector&quot;&gt;.title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* styles */&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;But, as any project grows, it will likely result in more complex CSS, such that you may end up with selectors that look like this:&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 selector&quot;&gt;.box:nth-last-child(-n+1) .title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* styles */&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;In order to know how these styles apply to the page, the browser has to effectively ask &amp;quot;is this an element with a class of &lt;code&gt;title&lt;/code&gt; which has a parent who happens to be the minus nth child plus 1 element with a class of &lt;code&gt;box&lt;/code&gt;?&amp;quot; Figuring this out &lt;em&gt;can&lt;/em&gt; take a lot of time, depending on the selector used as well as the browser in question. The intended behavior of the selector could instead be changed to a class:&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 selector&quot;&gt;.final-box-title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* styles */&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;You can take issue with the name of the class, but the job just got a lot simpler for the browser. In the previous version, in order to know, for example, that the element is the last of its type, the browser must first know everything about all the other elements and whether the are any elements that come after it that would be the &lt;code&gt;nth-last-child&lt;/code&gt;, which is potentially more expensive than simply matching up the selector to the element because its class matches.&lt;/p&gt;
&lt;h2 id=&quot;reduce-the-number-of-elements-being-styled&quot;&gt;Reduce the number of elements being styled &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#reduce-the-number-of-elements-being-styled&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another performance consideration—which is typically &lt;em&gt;the more important factor for many style updates&lt;/em&gt;—is the sheer volume of work that needs to be carried out when an element changes.&lt;/p&gt;
&lt;p&gt;In general terms, the worst case cost of calculating the computed style of elements is the number of elements multiplied by the selector count, because each element needs to be at least checked once against every style to see if it matches.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; It used to be the case that if you changed a class on—say—the body element, that all the children in the page would need to have their computed styles recalculated. Thankfully that is no longer the case; some browsers instead maintain a small collection of rules unique to each element that, if changed, cause the element&#39;s styles to be recalculated. That means that an element may or may not need to be recalculated depending on where it is in the tree, and what specifically got changed. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Style calculations can often be targeted to a few elements directly rather than invalidating the page as a whole. In modern browsers, this tends to be less of an issue because the browser doesn&#39;t necessarily need to check all the elements potentially affected by a change. Older browsers, on the other hand, aren&#39;t necessarily as optimized for such tasks. Where you can, you should &lt;strong&gt;reduce the number of invalidated elements&lt;/strong&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re into Web Components, it&#39;s worth noting that style calculations here are a little different, since by default styles do not cross the Shadow DOM boundary, and are scoped to individual components rather than the tree as a whole. Overall, however, the same concept still applies: smaller trees with simpler rules are more efficiently processed than larger trees with more complex rules. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure-your-style-recalculation-cost&quot;&gt;Measure your style recalculation cost &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#measure-your-style-recalculation-cost&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One way to measure the cost of style recalculations is to use the performance panel in Chrome DevTools. To begin, open DevTools, go to the tab labeled &lt;strong&gt;Performance&lt;/strong&gt;, hit record, and interact with the page. When you stop recording, you&#39;ll see something like the image below:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools showing style calculations.&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/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The strip at the top is a miniature flame chart that also plots frames per second. The closer the activity is to the bottom of the strip, the faster frames are being painted by the browser. If you see the flame chart leveling out at the top with red strips above it, then you have work that&#39;s causing long running frames.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Zooming in on a trouble area in Chrome DevTools in the activity summary of the populated performance panel in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;126&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 769px) 769px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1538 1538w&quot; width=&quot;769&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If you have a long running frame during an interaction like scrolling, then it bears further scrutiny. If you have a large purple block, zoom in on the activity and select any work labeled &lt;strong&gt;Recalculate Style&lt;/strong&gt; to get more information on potentially expensive style recalculation work.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Getting the details of long-running style calculations, including vital information such as the amount of elements affected by the style recalculation work.&quot; decoding=&quot;async&quot; height=&quot;218&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 647px) 647px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1294 1294w&quot; width=&quot;647&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In this grab there is long-running style recalculation work that is taking just over 25ms.&lt;/p&gt;
&lt;p&gt;If you click the event itself, you are given a call stack. If the rendering work was due to a user interaction, the place in your JavaScript that is responsible for triggering the style change will be called out. In addition to that, you also get the number of elements that have been affected by the change—just over 900 elements in this case—and how long it took to do the style calculation work. You can use this information to start trying to find a fix in your code.&lt;/p&gt;
&lt;h2 id=&quot;use-block,-element,-modifier&quot;&gt;Use Block, Element, Modifier &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#use-block,-element,-modifier&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Approaches to coding like &lt;a href=&quot;https://bem.info/&quot; rel=&quot;noopener&quot;&gt;BEM (Block, Element, Modifier)&lt;/a&gt; actually bake in the selector matching performance benefits above, because it recommends that everything has a single class, and, where you need hierarchy, that gets baked into the name of the class as well:&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 selector&quot;&gt;.list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Styles */&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 selector&quot;&gt;.list__list-item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Styles */&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;If you need some modifier, like in the above where we want to do something special for the last child, you can add that like so:&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 selector&quot;&gt;.list__list-item--last-child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Styles */&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;If you&#39;re looking for a good way to organize your CSS, BEM is a good starting point, both from a structure point-of-view, and also because of the simplifications of style lookup the methodology promotes.&lt;/p&gt;
&lt;p&gt;If you don&#39;t like BEM, there are other ways to approach your CSS, but the performance considerations should be assessed alongside the ergonomics of the approach.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8/edit&quot; rel=&quot;noopener&quot;&gt;Style invalidation in Blink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bem.info/&quot; rel=&quot;noopener&quot;&gt;BEM (Block, Element, Modifier)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@markusspiske&quot; rel=&quot;noopener&quot;&gt;Markus Spiske&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Simplify paint complexity and reduce paint areas</title>
    <link href="https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/</id>
    <content type="html" mode="escaped">&lt;p&gt;Paint is the process of filling in pixels that eventually get composited to
the users&#39; screens. It is often the longest-running of all tasks in the
pipeline, and one to avoid if at all possible.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Changing any property apart from transforms or opacity always triggers paint.&lt;/li&gt;
&lt;li&gt;Paint is often the most expensive part of the pixel pipeline; avoid it where you can.&lt;/li&gt;
&lt;li&gt;Reduce paint areas through layer promotion and orchestration of animations.&lt;/li&gt;
&lt;li&gt;Use the Chrome DevTools paint profiler to assess paint complexity and cost; reduce where you can.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;triggering-layout-and-paint&quot;&gt;Triggering Layout And Paint &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#triggering-layout-and-paint&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you trigger layout, you will &lt;em&gt;always trigger paint&lt;/em&gt;, since changing the geometry of any element means its pixels need fixing!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The full pixel pipeline.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VG9K1de3NXF6wIz5vAaM.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;You can also trigger paint if you change non-geometric properties, like backgrounds, text color, or shadows. In those cases layout won’t be needed and the pipeline will look like this:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The pixel pipeline without layout.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3NbVKqelM3UPaIwlneYU.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;use-chrome-devtools-to-quickly-identify-paint-bottlenecks&quot;&gt;Use Chrome DevTools to quickly identify paint bottlenecks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#use-chrome-devtools-to-quickly-identify-paint-bottlenecks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use Chrome DevTools to quickly identify areas that are being painted. &lt;a href=&quot;https://developer.chrome.com/docs/devtools/evaluate-performance/reference/#rendering&quot; rel=&quot;noopener&quot;&gt;Open the Rendering tab&lt;/a&gt;
and then enable &lt;strong&gt;Paint Flashing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With this option switched on Chrome will flash the screen green whenever painting happens. If you’re seeing the whole screen flash green, or areas of the screen that you didn’t think should be painted, then you should dig in a little further.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The page flashing green whenever painting occurs.&quot; decoding=&quot;async&quot; height=&quot;521&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FslYBjuttmtVUCAq3y0n.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;promote-elements-that-move-or-fade&quot;&gt;Promote elements that move or fade &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#promote-elements-that-move-or-fade&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Painting is not always done into a single image in memory. In fact, it’s possible for the browser to paint into multiple images, or compositor layers, if necessary.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A representation of compositor layers.&quot; decoding=&quot;async&quot; height=&quot;688&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/z821UCTV7erbUxsIABkf.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The benefit of this approach is that elements that are regularly repainted, or are moving on screen with transforms, can be handled without affecting other elements. This is the same as with art packages like Sketch, GIMP, or Photoshop, where individual layers can be handled and composited on top of each other to create the final image.&lt;/p&gt;
&lt;p&gt;The best way to create a new layer is to use the &lt;code&gt;will-change&lt;/code&gt; CSS property. This will work in Chrome, Opera and Firefox, and, with a value of &lt;code&gt;transform&lt;/code&gt;, will create a new compositor layer:&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 selector&quot;&gt;.moving-element&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;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&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 browsers that don’t support &lt;code&gt;will-change&lt;/code&gt;, but benefit from layer creation, such as Safari and Mobile Safari, you need to (mis)use a 3D transform to force a new layer:&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 selector&quot;&gt;.moving-element&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;translateZ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Care must be taken not to create too many layers, however, as each layer requires both memory and management. There is more information on this in the &lt;a href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/&quot;&gt;Stick to compositor-only properties and manage layer count&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;If you have promoted an element to a new layer, use DevTools to confirm that doing so has given you a performance benefit. &lt;strong&gt;Don&#39;t promote elements without profiling.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;reduce-paint-areas&quot;&gt;Reduce paint areas &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#reduce-paint-areas&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes, however, despite promoting elements, paint work is still necessary. A large challenge of paint issues is that browsers union together two areas that need painting, and that can result in the entire screen being repainted. So, for example, if you have a fixed header at the top of the page, and something being painted at the bottom of the screen, the entire screen may end up being repainted.&lt;/p&gt;
&lt;p&gt;Note: On High DPI screens elements that are fixed position are automatically promoted to their own compositor layer. This is not the case on low DPI devices because the promotion changes text rendering from subpixel to grayscale, and layer promotion needs to be done manually.&lt;/p&gt;
&lt;p&gt;Reducing paint areas is often a case of orchestrating your animations and transitions to not overlap as much, or finding ways to avoid animating certain parts of the page.&lt;/p&gt;
&lt;h2 id=&quot;simplify-paint-complexity&quot;&gt;Simplify paint complexity &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/#simplify-paint-complexity&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The time taken to paint part of the screen.&quot; decoding=&quot;async&quot; height=&quot;181&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 248px) 248px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/NMsTYj8K5odtsK3FvEVU.jpg?auto=format&amp;w=496 496w&quot; width=&quot;248&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When it comes to painting, some things are more expensive than others. For example, anything that involves a blur (like a shadow, for example) is going to take longer to paint than -- say -- drawing a red box. In terms of CSS, however, this isn’t always obvious: &lt;code&gt;background: red;&lt;/code&gt; and &lt;code&gt;box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5);&lt;/code&gt; don’t necessarily look like they have vastly different performance characteristics, but they do.&lt;/p&gt;
&lt;p&gt;The paint profiler above will allow you to determine if you need to look at other ways to achieve effects. Ask yourself if it’s possible to use a cheaper set of styles or alternative means to get to your end result.&lt;/p&gt;
&lt;p&gt;Where you can you always want to avoid paint during animations in particular, as the &lt;strong&gt;10ms&lt;/strong&gt; you have per frame is normally not long enough to get paint work done, especially on mobile devices.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Debounce your input handlers</title>
    <link href="https://web.dev/debounce-your-input-handlers/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/debounce-your-input-handlers/</id>
    <content type="html" mode="escaped">&lt;p&gt;Input handlers are a potential cause of performance problems in your apps, as
they can block frames from completing, and can cause additional and unnecessary
layout work.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debounce-your-input-handlers/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Avoid long-running input handlers; they can block scrolling.&lt;/li&gt;
&lt;li&gt;Do not make style changes in input handlers.&lt;/li&gt;
&lt;li&gt;Debounce your handlers; store event values and deal with style changes in the next requestAnimationFrame callback.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;avoid-long-running-input-handlers&quot;&gt;Avoid long-running input handlers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debounce-your-input-handlers/#avoid-long-running-input-handlers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the fastest possible case, when a user interacts with the page, the page’s compositor thread can take the user’s touch input and simply move the content around. This requires no work by the main thread, where JavaScript, layout, styles, or paint are done.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Lightweight scrolling; compositor only.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/3gVWdQyhrcRbaCge89hX.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If, however, you attach an input handler, like &lt;code&gt;touchstart&lt;/code&gt;, &lt;code&gt;touchmove&lt;/code&gt;, or &lt;code&gt;touchend&lt;/code&gt;, the compositor thread must wait for this handler to finish executing because you may choose to call &lt;code&gt;preventDefault()&lt;/code&gt; and stop the touch scroll from taking place. Even if you don’t call &lt;code&gt;preventDefault()&lt;/code&gt; the compositor must wait, and as such the user’s scroll is blocked, which can result in stuttering and missed frames.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Heavy scrolling; compositor is blocked on JavaScript.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hPl1pZ4w1M5d7HJ7ZeQO.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In short, you should make sure that any input handlers you run should execute quickly and allow the compositor to do its job.&lt;/p&gt;
&lt;h2 id=&quot;avoid-style-changes-in-input-handlers&quot;&gt;Avoid style changes in input handlers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debounce-your-input-handlers/#avoid-style-changes-in-input-handlers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Input handlers, like those for scroll and touch, are scheduled to run just before any &lt;code&gt;requestAnimationFrame&lt;/code&gt; callbacks.&lt;/p&gt;
&lt;p&gt;If you make a visual change inside one of those handlers, then at the start of the &lt;code&gt;requestAnimationFrame&lt;/code&gt;, there will be style changes pending. If you &lt;em&gt;then&lt;/em&gt; read visual properties at the start of the requestAnimationFrame callback, as the advice in “&lt;a href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/&quot;&gt;Avoid large, complex layouts and layout thrashing&lt;/a&gt;” suggests, you will trigger a forced synchronous layout!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Heavy scrolling; compositor is blocked on JavaScript.&quot; decoding=&quot;async&quot; height=&quot;324&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qQ2ymRuMt1rdAhhIXyLy.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;debounce-your-scroll-handlers&quot;&gt;Debounce your scroll handlers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debounce-your-input-handlers/#debounce-your-scroll-handlers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The solution to both of the problems above is the same: you should always debounce visual changes to the next &lt;code&gt;requestAnimationFrame&lt;/code&gt; callback:&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onScroll&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&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;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Store the scroll value for laterz.&lt;/span&gt;&lt;br /&gt;    lastScrollY &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollY&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Prevent multiple rAF callbacks.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scheduledAnimationFrame&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    scheduledAnimationFrame &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readAndUpdatePage&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;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;scroll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onScroll&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;Doing this also has the added benefit of keeping your input handlers light, which is awesome because now you’re not blocking things like scrolling or touch on computationally expensive code!&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize JavaScript execution</title>
    <link href="https://web.dev/optimize-javascript-execution/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/optimize-javascript-execution/</id>
    <content type="html" mode="escaped">&lt;p&gt;JavaScript often triggers visual changes. Sometimes that&#39;s directly
through style manipulations, and sometimes it&#39;s calculations that
result in visual changes, like searching or sorting data. Badly-timed
or long-running JavaScript is a common cause of performance issues.
You should look to minimize its impact where you can.&lt;/p&gt;
&lt;p&gt;JavaScript performance profiling can be something of an art, because the JavaScript you write is
nothing like the code that is actually executed. Modern browsers use JIT compilers and all manner
of optimizations and tricks to try and give you the fastest possible execution, and this
substantially changes the dynamics of the code.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you really want to see JIT in action you should check out &lt;a href=&quot;http://mrale.ph/irhydra/2/&quot;&gt;IRHydra2 by Vyacheslav Egorov&lt;/a&gt;. It shows the intermediate state of JavaScript code when Chrome’s JavaScript engine, V8, is optimizing it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;With all that said, however, there are some things you can definitely do to help your apps execute
JavaScript well.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-javascript-execution/#summary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Avoid setTimeout or setInterval for visual updates; always use requestAnimationFrame instead.&lt;/li&gt;
&lt;li&gt;Move long-running JavaScript off the main thread to Web Workers.&lt;/li&gt;
&lt;li&gt;Use micro-tasks to make DOM changes over several frames.&lt;/li&gt;
&lt;li&gt;Use Chrome DevTools’ Timeline and JavaScript Profiler to assess the impact of JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;use-requestanimationframe-for-visual-changes&quot;&gt;Use &lt;code&gt;requestAnimationFrame&lt;/code&gt; for visual changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-javascript-execution/#use-requestanimationframe-for-visual-changes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When visual changes are happening on screen you want to do your work at the right time for the
browser, which is right at the start of the frame. The only way to guarantee that your JavaScript
will run at the start of a frame is to use &lt;code&gt;requestAnimationFrame&lt;/code&gt;.&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;/**&lt;br /&gt;    * If run as a requestAnimationFrame callback, this&lt;br /&gt;    * will be run at the start of the frame.&lt;br /&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;updateScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;time&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;    &lt;span class=&quot;token comment&quot;&gt;// Make visual updates here.&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 function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updateScreen&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;Frameworks or samples may use &lt;code&gt;setTimeout&lt;/code&gt; or &lt;code&gt;setInterval&lt;/code&gt; to do visual changes like animations,
but the problem with this is that the callback will run at &lt;em&gt;some point&lt;/em&gt; in the frame, possibly right
at the end, and that can often have the effect of causing us to miss a frame, resulting in jank.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;setTimeout causing the browser to miss a frame.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/iq5yVSd4wRskoD8GywR7.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In fact, jQuery used to use &lt;code&gt;setTimeout&lt;/code&gt; for its &lt;code&gt;animate&lt;/code&gt; behavior. It was changed to use
&lt;code&gt;requestAnimationFrame&lt;/code&gt; in version 3.
If you are using older version of jQuery, you can
&lt;a href=&quot;https://github.com/gnarf/jquery-requestAnimationFrame&quot; rel=&quot;noopener&quot;&gt;patch it to use &lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/a&gt;,
which is strongly advised.&lt;/p&gt;
&lt;h2 id=&quot;reduce-complexity-or-use-web-workers&quot;&gt;Reduce complexity or use Web Workers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-javascript-execution/#reduce-complexity-or-use-web-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript runs on the browser’s main thread, right alongside style calculations, layout, and, in
many cases, paint. If your JavaScript runs for a long time, it will block these other tasks,
potentially causing frames to be missed.&lt;/p&gt;
&lt;p&gt;You should be tactical about when JavaScript runs, and for how long. For example, if you’re in an
animation like scrolling, you should ideally be looking to keep your JavaScript to something in the
region of &lt;strong&gt;3-4ms&lt;/strong&gt;. Any longer than that and you risk taking up too much time. If you’re in an idle
period, you can afford to be more relaxed about the time taken.&lt;/p&gt;
&lt;p&gt;In many cases you can move pure computational work to
&lt;a href=&quot;https://developer.mozilla.org//docs/Web/API/Web_Workers_API/basic_usage&quot; rel=&quot;noopener&quot;&gt;Web Workers&lt;/a&gt;,
if, for example, it doesn’t require DOM access. Data manipulation or traversal,
like sorting or searching, are often good fits for this model, as are loading and model generation.&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 keyword&quot;&gt;var&lt;/span&gt; dataSortWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sort-worker.js&quot;&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;dataSortWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMesssage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataToSort&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The main thread is now free to continue working on other things...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;dataSortWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&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 parameter&quot;&gt;evt&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;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sortedData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Update data on screen...&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Not all work can fit this model: Web Workers do not have DOM access. Where your work must be on the main thread, consider a batching approach, where you segment the larger task into micro-tasks, each taking no longer than a few milliseconds, and run inside of &lt;code&gt;requestAnimationFrame&lt;/code&gt; handlers across each frame.&lt;/p&gt;
&lt;p&gt;There are UX and UI consequences to this approach, and you will need to ensure that the user knows
that a task is being processed, either by &lt;a href=&quot;https://www.google.com/design/spec/components/progress-activity.html&quot; rel=&quot;noopener&quot;&gt;using a progress or activity indicator&lt;/a&gt;.
In any case this approach will keep your app&#39;s main thread free, helping it to stay responsive to
user interactions.&lt;/p&gt;
&lt;h2 id=&quot;know-your-javascripts-frame-tax&quot;&gt;Know your JavaScript’s “frame tax” &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-javascript-execution/#know-your-javascripts-frame-tax&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When assessing a framework, library, or your own code, it’s important to assess
how much it costs to run the JavaScript code on a frame-by-frame basis. This is
especially important when doing performance-critical animation work like
transitioning or scrolling.&lt;/p&gt;
&lt;p&gt;The Performance panel of Chrome DevTools is the best way to measure your
JavaScript&#39;s cost. Typically you get low-level records like this:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A performance recording in Chrome DevTools&quot; decoding=&quot;async&quot; height=&quot;636&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ExUgS4YIvS4Klx9Ssn1q.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;strong&gt;Main&lt;/strong&gt; section provides a flame chart of JavaScript calls so you
can analyze exactly which functions were called and how long each took.&lt;/p&gt;
&lt;p&gt;Armed with this information you can assess the performance impact of the
JavaScript on your application, and begin to find and fix any hotspots where
functions are taking too long to execute. As mentioned earlier you should seek
to either remove long-running JavaScript, or, if that’s not possible, move it
to a Web Worker freeing up the main thread to continue on with other tasks.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://developer.chrome.com/docs/devtools/evaluate-performance/&quot; rel=&quot;noopener&quot;&gt;Get Started With Analyzing Runtime Performance&lt;/a&gt; to learn how to use
the Performance panel.&lt;/p&gt;
&lt;h2 id=&quot;avoid-micro-optimizing-your-javascript&quot;&gt;Avoid micro-optimizing your JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-javascript-execution/#avoid-micro-optimizing-your-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It may be cool to know that the browser can execute one version of a thing 100 times faster than
another thing, like that requesting an element’s &lt;code&gt;offsetTop&lt;/code&gt; is faster than computing
&lt;code&gt;getBoundingClientRect()&lt;/code&gt;, but it’s almost always true that you’ll only be calling functions like
these a small number of times per frame, so it’s normally wasted effort to focus on this aspect of
JavaScript’s performance. You&#39;ll typically only save fractions of milliseconds.&lt;/p&gt;
&lt;p&gt;If you’re making a game, or a computationally expensive application, then you’re likely an exception
to this guidance, as you’ll be typically fitting a lot of computation into a single frame, and in
that case everything helps.&lt;/p&gt;
&lt;p&gt;In short, you should be very wary of micro-optimizations because they won’t typically map to the
kind of application you’re building.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Rendering Performance</title>
    <link href="https://web.dev/rendering-performance/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/rendering-performance/</id>
    <content type="html" mode="escaped">&lt;p&gt;Users of today’s web
&lt;a href=&quot;https://paul.kinlan.me/what-news-readers-want/&quot; rel=&quot;noopener&quot;&gt;expect that the pages they visit will be interactive and smooth&lt;/a&gt;
and that’s where you need to increasingly focus your time and effort. Pages
should not only load quickly, but also run well; scrolling should be
stick-to-finger fast, and animations and interactions should be silky smooth.&lt;/p&gt;
&lt;p&gt;To write performant sites and apps you need to understand how HTML, JavaScript and CSS is handled by the browser, and ensure that the code you write (and the other 3rd party code you include) runs as efficiently as possible.&lt;/p&gt;
&lt;h2 id=&quot;60fps-and-device-refresh-rates&quot;&gt;60fps and Device Refresh Rates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#60fps-and-device-refresh-rates&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt=&quot;User interacting with a website.&quot; decoding=&quot;async&quot; height=&quot;486&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 612px) 612px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hjgQvYAu7IMrwEkmLyT9.jpg?auto=format&amp;w=1224 1224w&quot; width=&quot;612&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Most devices today refresh their screens &lt;strong&gt;60 times a second&lt;/strong&gt;. If there’s
an animation or transition running, or the user is scrolling the pages, the
browser needs to match the device’s refresh rate and put up 1 new picture, or
frame, for each of those screen refreshes.&lt;/p&gt;
&lt;p&gt;Each of those frames has a budget of just over 16ms (1 second / 60 = 16.66ms).
In reality, however, the browser has maintenance work to do, so all of your
work needs to be completed inside &lt;strong&gt;10ms&lt;/strong&gt;. When you fail to meet this
budget the frame rate drops, and the content judders on screen. This is often
referred to as &lt;strong&gt;jank&lt;/strong&gt;, and it negatively impacts the user&#39;s experience.&lt;/p&gt;
&lt;h2 id=&quot;the-pixel-pipeline&quot;&gt;The pixel pipeline &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#the-pixel-pipeline&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are five major areas that you need to know about and be mindful of when
you work. They are areas you have the most control over, and key points in the
pixels-to-screen pipeline:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The full pixel pipeline.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;. Typically JavaScript is used to handle work that will result in visual changes, whether it’s jQuery’s &lt;code&gt;animate&lt;/code&gt; function, sorting a data set, or adding DOM elements to the page. It doesn’t have to be JavaScript that triggers a visual change, though: CSS Animations, Transitions, and the Web Animations API are also commonly used.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Style calculations&lt;/strong&gt;. This is the process of figuring out which CSS rules apply to which elements based on matching selectors, for example, &lt;code&gt;.headline&lt;/code&gt; or &lt;code&gt;.nav &amp;gt; .nav__item&lt;/code&gt;. From there, once rules are known, they are applied and the final styles for each element are calculated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout&lt;/strong&gt;. Once the browser knows which rules apply to an element it can begin to calculate how much space it takes up and where it is on screen. The web’s layout model means that one element can affect others, for example the width of the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element typically affects its children’s widths and so on all the way up and down the tree, so the process can be quite involved for the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paint&lt;/strong&gt;. Painting is the process of filling in pixels. It involves drawing out text, colors, images, borders, and shadows, essentially every visual part of the elements. The drawing is typically done onto multiple surfaces, often called layers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compositing&lt;/strong&gt;. Since the parts of the page were drawn into potentially multiple layers they need to be drawn to the screen in the correct order so that the page renders correctly. This is especially important for elements that overlap another, since a mistake could result in one element appearing over the top of another incorrectly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these parts of the pipeline represents an opportunity to introduce jank, so it&#39;s important to understand exactly what parts of the pipeline your code triggers.&lt;/p&gt;
&lt;p&gt;Sometimes you may hear the term &amp;quot;rasterize&amp;quot; used in conjunction with paint.
This is because painting is actually two tasks: 1) creating a list of draw
calls, and 2) filling in the pixels.&lt;/p&gt;
&lt;p&gt;The latter is called &amp;quot;rasterization&amp;quot; and so whenever you see paint records in
DevTools, you should think of it as including rasterization. (In some
architectures creating the list of draw calls and rasterizing are done in
different threads, but that isn&#39;t something under developer control.)&lt;/p&gt;
&lt;p&gt;You won’t always necessarily touch every part of the pipeline on every frame.
In fact, there are three ways the pipeline &lt;em&gt;normally&lt;/em&gt; plays out for a given
frame when you make a visual change, either with JavaScript, CSS, or Web
Animations:&lt;/p&gt;
&lt;h3 id=&quot;1-js-css-greater-style-greater-layout-greater-paint-greater-composite&quot;&gt;1. JS / CSS &amp;gt; Style &amp;gt; Layout &amp;gt; Paint &amp;gt; Composite &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#1-js-css-greater-style-greater-layout-greater-paint-greater-composite&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt=&quot;The full pixel pipeline.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/gfQC4uOnIbLVtkdvbSY4.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If you change a “layout” property, so that’s one that changes an element’s
geometry, like its width, height, or its position with left or top, the browser
will have to check all the other elements and “reflow” the page. Any affected
areas will need to be repainted, and the final painted elements will need to be
composited back together.&lt;/p&gt;
&lt;h3 id=&quot;2-js-css-greater-style-greater-paint-greater-composite&quot;&gt;2. JS / CSS &amp;gt; Style &amp;gt; Paint &amp;gt; Composite &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#2-js-css-greater-style-greater-paint-greater-composite&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt=&quot;The  pixel pipeline without layout.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/aD7UKMbegndQijj36PHi.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If you changed a “paint only” property, like a background image, text color, or
shadows, in other words one that does not affect the layout of the page, then the browser
skips layout, but it will still do paint.&lt;/p&gt;
&lt;h3 id=&quot;3-js-css-greater-style-greater-composite&quot;&gt;3. JS / CSS &amp;gt; Style &amp;gt; Composite &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#3-js-css-greater-style-greater-composite&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt=&quot;The pixel pipeline without layout or paint.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bfBPdciP9OSYPFI67xiY.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If you change a property that requires neither layout nor paint, and the
browser jumps to just do compositing.&lt;/p&gt;
&lt;p&gt;This final version is the cheapest and most desirable for high pressure points
in an app&#39;s lifecycle, like animations or scrolling.&lt;/p&gt;
&lt;p&gt;Note: If you want the fast track to high performance animations, read the section on &lt;a href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/&quot;&gt;changing compositor-only properties&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Performance is the art of avoiding work, and making any work you do as
efficient as possible. In many cases it&#39;s about working with the browser, not
against it. It’s worth bearing in mind that the work listed above in the
pipeline differ in terms of computational cost; some tasks are more expensive
than others!&lt;/p&gt;
&lt;p&gt;Let’s take a dive into the different parts of the pipeline. We’ll take a look
at the common issues, as well how to diagnose and fix them.&lt;/p&gt;
&lt;h2 id=&quot;browser-rendering-optimizations&quot;&gt;Browser Rendering Optimizations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/rendering-performance/#browser-rendering-optimizations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;a href=&quot;https://www.udacity.com/course/browser-rendering-optimization--ud860&quot;&gt;
&lt;figure&gt;
&lt;img alt=&quot;Udacity course screenshot&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BRtqtw4gW9q2VBcQC0aW.jpg?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
&lt;/figure&gt;
&lt;/a&gt;
&lt;p&gt;Performance matters to users. Web developers need to build apps that react
quickly and render smoothly. Google performance guru Paul Lewis is here
to help you destroy jank and create web apps that maintain 60 frames per
second performance. You&#39;ll leave this course with the tools you need to
profile apps and identify the causes of jank. You&#39;ll explore the browser&#39;s
rendering pipeline and uncover patterns that make it easy to build
performant apps.&lt;/p&gt;
&lt;p&gt;This is a free course offered through &lt;a href=&quot;https://www.udacity.com/&quot; rel=&quot;noopener&quot;&gt;Udacity&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.udacity.com/course/browser-rendering-optimization--ud860&quot; rel=&quot;noopener&quot;&gt;Take Course&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Stick to Compositor-Only Properties and Manage Layer Count</title>
    <link href="https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/</id>
    <content type="html" mode="escaped">&lt;p&gt;Compositing is where the painted parts of the page are put together for
displaying on screen.&lt;/p&gt;
&lt;p&gt;There are two key factors in this area that affect page performance: the number of compositor layers that need to be managed, and the properties that you use for animations.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/#summary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Stick to transform and opacity changes for your animations.&lt;/li&gt;
&lt;li&gt;Promote moving elements with &lt;code&gt;will-change&lt;/code&gt; or &lt;code&gt;translateZ&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Avoid overusing promotion rules; layers require memory and management.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;use-transform-and-opacity-changes-for-animations&quot;&gt;Use transform and opacity changes for animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/#use-transform-and-opacity-changes-for-animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best-performing version of the pixel pipeline avoids both layout and paint, and only requires compositing changes:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The pixel pipeline with no layout or paint.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/4to9zPKpICVNFCPl7qwV.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In order to achieve this you will need to stick to changing properties that can be handled by the compositor alone. Today there are only two properties for which that is true - &lt;code&gt;transform&lt;/code&gt;s and &lt;code&gt;opacity&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The properties you can animate without triggering layout or paint.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VoLzvR2akAnQfKFVyyM9.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The caveat for the use of &lt;code&gt;transform&lt;/code&gt;s and &lt;code&gt;opacity&lt;/code&gt; is that the element on which you change these properties should be on &lt;em&gt;its own compositor layer&lt;/em&gt;. In order to make a layer you must promote the element, which we will cover next.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re concerned that you may not be able to limit your animations to just those properties, take a look at the &lt;a href=&quot;https://aerotwist.com/blog/flip-your-animations&quot;&gt;FLIP principle&lt;/a&gt;, which may help you remap animations to changes in transforms and opacity from more expensive properties. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;promote-elements-that-you-plan-to-animate&quot;&gt;Promote elements that you plan to animate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/#promote-elements-that-you-plan-to-animate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As we mentioned in the &amp;quot;&lt;a href=&quot;https://web.dev/simplify-paint-complexity-and-reduce-paint-areas/&quot;&gt;Simplify paint complexity and reduce paint areas&lt;/a&gt;&amp;quot; section, you should promote elements that you plan to animate (within reason, don&#39;t overdo it!) to their own layer:&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 selector&quot;&gt;.moving-element&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;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&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;Or, for older browsers, or those that don&#39;t support will-change:&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 selector&quot;&gt;.moving-element&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;translateZ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;manage-layers-and-avoid-layer-explosions&quot;&gt;Manage layers and avoid layer explosions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/#manage-layers-and-avoid-layer-explosions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s perhaps tempting, then, knowing that layers often help performance, to promote all the elements on your page with something like the following:&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 selector&quot;&gt;*&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;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&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;translateZ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Which is a roundabout way of saying that you&#39;d like to promote every single element on the page. The problem here is that every layer you create requires memory and management, and that&#39;s not free. In fact, on devices with limited memory the impact on performance can far outweigh any benefit of creating a layer. Every layer&#39;s textures needs to be uploaded to the GPU, so there are further constraints in terms of bandwidth between CPU and GPU, and memory available for textures on the GPU.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Do not promote elements unnecessarily. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-chrome-devtools-to-understand-the-layers-in-your-app&quot;&gt;Use Chrome DevTools to understand the layers in your app &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/stick-to-compositor-only-properties-and-manage-layer-count/#use-chrome-devtools-to-understand-the-layers-in-your-app&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The toggle for the paint profiler in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;129&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 278px) 278px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QNAAH0VJDRjtu9iC6KVE.jpg?auto=format&amp;w=556 556w&quot; width=&quot;278&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To get an understanding of the layers in your application, and why an element has a layer you must enable the Paint profiler in Chrome DevTools&#39; Timeline:&lt;/p&gt;
&lt;p&gt;With this switched on you should take a recording. When the recording has finished you will be able to click individual frames, which is found between the frames-per-second bars and the details:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A frame the developer is interested in profiling.&quot; decoding=&quot;async&quot; height=&quot;229&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 397px) 397px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/7zZkTVCqwvqYrty8m8Oy.jpg?auto=format&amp;w=794 794w&quot; width=&quot;397&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Clicking on this will provide you with a new option in the details: a layer tab.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The layer tab button in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;260&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 333px) 333px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f2yHHn8kTJLm6WVn3vnR.jpg?auto=format&amp;w=666 666w&quot; width=&quot;333&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;This option will bring up a new view that allows you to pan, scan and zoom in on all the layers during that frame, along with reasons that each layer was created.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The layer view in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/spj7LxeccWat0sWddxo3.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Using this view you can track the number of layers you have. If you&#39;re spending a lot time in compositing during performance-critical actions like scrolling or transitions (you should aim for around &lt;strong&gt;4-5ms&lt;/strong&gt;), you can use the information here to see how many layers you have, why they were created, and from there manage layer counts in your app.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Animating Between Views</title>
    <link href="https://web.dev/animating-between-views/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/animating-between-views/</id>
    <content type="html" mode="escaped">&lt;p&gt;Often, you want to move users between views in your application, whether that&#39;s from a list to a details view, or show a sidebar navigation. Animations between these views keep the user engaged and add even more life to your projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use translations to move between views; avoid using &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, or any other property that triggers layout.&lt;/li&gt;
&lt;li&gt;Ensure that any animations you use are snappy and the durations are kept short.&lt;/li&gt;
&lt;li&gt;Consider how your animations and layouts change as the screen sizes go up; what works for a smaller screen may look odd when used in a desktop context.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What these view transitions look and behave like depends on the type of views you’re dealing with. For example, animating a modal overlay on top of a view should be a different experience from transitioning between a list and details view.&lt;/p&gt;
&lt;p&gt;Success: Try to maintain 60fps for all of your animations. That way, your users won&#39;t see stuttering animations that interfere with their experience. Ensure that any animating element has &lt;code&gt;will-change&lt;/code&gt; set for anything you plan to change well ahead of the animation starting. For view transitions, it’s highly likely you will want to use &lt;code&gt;will-change: transform&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;use-translations-to-move-between-views&quot;&gt;Use translations to move between views &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/animating-between-views/#use-translations-to-move-between-views&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class=&quot;float-left&quot;&gt;
&lt;img alt=&quot;Translating between two views.&quot; decoding=&quot;async&quot; height=&quot;404&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZbdixE36RlDboM08jwGH.gif?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To make life easier, assume that there are two views: a list view and a details view. As the user taps a list item inside the list view, the details view slides in, and the list view slides out.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot;&gt;
    &lt;img alt=&quot;View hierarchy.&quot; decoding=&quot;async&quot; height=&quot;148&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/5t4foOL1MBvOiAD4XbFD.svg&quot; width=&quot;309&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To achieve this effect, you need a container for both views that has &lt;code&gt;overflow: hidden&lt;/code&gt; set on it. That way, the two views can both be inside the container side-by-side without showing any horizontal scrollbars, and each view can slide side-to-side inside the container as needed.&lt;/p&gt;
&lt;p&gt;The CSS for the container is:&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 selector&quot;&gt;.container&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&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; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&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;The position of the container is set as &lt;code&gt;relative&lt;/code&gt;. This means that each view inside it can be positioned absolutely to the top left corner and then moved around with transforms. This approach is better for performance than using the &lt;code&gt;left&lt;/code&gt; property (because that triggers layout and paint), and is typically easier to rationalize.&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 selector&quot;&gt;.view&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&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; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* let the browser know we plan to animate&lt;br /&gt;     each view in and out */&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&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;Adding a &lt;code&gt;transition&lt;/code&gt; on the &lt;code&gt;transform&lt;/code&gt; property provides a nice slide effect. To give it a nice feel, it’s using a custom &lt;code&gt;cubic-bezier&lt;/code&gt; curve, which we discussed in the &lt;a href=&quot;https://web.dev/animating-between-views/custom-easing&quot;&gt;Custom Easing guide&lt;/a&gt;.&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 selector&quot;&gt;.view&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;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 0.3s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The view that is offscreen should be translated to the right, so in this case the details view needs to be moved:&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 selector&quot;&gt;.details-view&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;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;100%&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now a small amount of JavaScript is necessary to handle the classes. This toggles the appropriate classes on the views.&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 keyword&quot;&gt;var&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&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;.container&#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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; backButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&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;.back-button&#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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; listItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.list-item&#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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt;    * Toggles the class on the container so that&lt;br /&gt;    * we choose the correct view.&lt;br /&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;onViewChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&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;    container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;view-change&#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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// When you click a list item, bring on the details view.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; listItems&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    listItems&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&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&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onViewChange&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// And switch it back again when you click the back button&lt;/span&gt;&lt;br /&gt;backButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onViewChange&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;Finally, we add the CSS declarations for those classes.&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 selector&quot;&gt;.view-change .list-view&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;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-100%&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;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.view-change .details-view&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;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/inter-view-animation.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You could expand this to cover multiple views, and the basic concept should remain the same; each non-visible view should be offscreen and brought on as needed, and the currently onscreen view should be moved off.&lt;/p&gt;
&lt;p&gt;Caution: Making this kind of hierarchy in a cross-browser way can be challenging. For example, iOS requires an additional CSS property, &lt;code&gt;-webkit-overflow-scrolling: touch&lt;/code&gt;, to &amp;quot;reenable&amp;quot; fling scrolling, but you don’t get to control which axis that’s for, as you can with the standard overflow property. Be sure to test your implementation across a range of devices!&lt;/p&gt;
&lt;p&gt;In addition to transitioning between views, this technique can also be applied to other slide-in elements, like sidebar navigation elements. The only real difference is that you shouldn’t need to move the other views.&lt;/p&gt;
&lt;h2 id=&quot;ensure-that-your-animation-works-with-larger-screens&quot;&gt;Ensure that your animation works with larger screens &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/animating-between-views/#ensure-that-your-animation-works-with-larger-screens&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class=&quot;float-right&quot;&gt;
&lt;img alt=&quot;View hierarchy on a large screen.&quot; decoding=&quot;async&quot; height=&quot;292&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1unXXqFlFcKCktBXERJR.svg&quot; width=&quot;282&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;For a larger screen, you should keep the list view around all the time rather than removing it, and slide on the details view from the right-hand side. It’s pretty much the same as dealing with a navigation view.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Animations and performance</title>
    <link href="https://web.dev/animations-and-performance/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/animations-and-performance/</id>
    <content type="html" mode="escaped">&lt;p&gt;Maintain 60fps whenever you are animating, because any less results in stutters or stalls that will be noticeable to your users and negatively impact their experiences.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take care that your animations don’t cause performance issues; ensure that you know the impact of animating a given CSS property.&lt;/li&gt;
&lt;li&gt;Animating properties that change the geometry of the page (layout) or cause painting are particularly expensive.&lt;/li&gt;
&lt;li&gt;Where you can, stick to changing transforms and opacity.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;will-change&lt;/code&gt; to ensure that the browser knows what you plan to animate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Animating properties is not free, and some properties are cheaper to animate than others. For example, animating the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; of an element changes its geometry and may cause other elements on the page to move or change size. This process is called &lt;em&gt;layout&lt;/em&gt; (or &lt;em&gt;reflow&lt;/em&gt; in Gecko-based browsers like Firefox), and can be expensive if your page has a lot of elements. Whenever layout is triggered, the page or part of it will normally need to be painted, which is typically even more expensive than the layout operation itself.&lt;/p&gt;
&lt;p&gt;Where you can, you should avoid animating properties that trigger layout or paint. For most modern browsers, this means limiting animations to &lt;code&gt;opacity&lt;/code&gt; or &lt;code&gt;transform&lt;/code&gt;, both of which the browser can highly optimize; it doesn’t matter if the animation is handled by JavaScript or CSS.&lt;/p&gt;
&lt;p&gt;Read a full guide on creating &lt;a href=&quot;https://web.dev/animations-guide/&quot;&gt;high performance animations&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-the-will-change-property&quot;&gt;Using the &lt;code&gt;will-change&lt;/code&gt; property &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/animations-and-performance/#using-the-will-change-property&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 36, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      36
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 36, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      36
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      79
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 9.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      9.1
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/CSS/will-change#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Use the &lt;a href=&quot;https://dev.w3.org/csswg/css-will-change/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;will-change&lt;/code&gt;&lt;/a&gt; to ensure the browser knows that you intend to change an element’s property. This allows the browser to put the most appropriate optimizations in place ahead of when you make the change. Don&#39;t overuse &lt;code&gt;will-change&lt;/code&gt;, however, because doing so can cause the browser to waste resources, which in turn causes even more performance issues.&lt;/p&gt;
&lt;p&gt;The general rule of thumb is that if the animation might be triggered in the next 200ms, either by a user’s interaction or because of your application’s state, then having &lt;code&gt;will-change&lt;/code&gt; on animating elements is a good idea. For most cases, then, any element in your app’s current view that you intend to animate should have &lt;code&gt;will-change&lt;/code&gt; enabled for whichever properties you plan to change. In the case of the box sample we’ve been using throughout the previous guides, adding &lt;code&gt;will-change&lt;/code&gt; for transforms and opacity looks like this:&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 selector&quot;&gt;.box&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;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opacity&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;Now the browsers that support it, &lt;a href=&quot;https://caniuse.com/#feat=will-change&quot; rel=&quot;noopener&quot;&gt;currently most modern browsers&lt;/a&gt;, will make the appropriate optimizations under the hood to support changing or animating those properties.&lt;/p&gt;
&lt;h2 id=&quot;css-vs-javascript-performance&quot;&gt;CSS vs JavaScript performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/animations-and-performance/#css-vs-javascript-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many pages and comments threads around the web that discuss the relative merits of CSS and JavaScript animations from a performance perspective. Here are a few points to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CSS-based animations, and Web Animations where supported natively, are typically handled on a thread known as the &amp;quot;compositor thread&amp;quot;. This is different from the browser&#39;s &amp;quot;main thread&amp;quot;, where styling, layout, painting, and JavaScript are executed. This means that if the browser is running some expensive tasks on the main thread, these animations can keep going without being interrupted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Other changes to transforms and opacity can, in many cases, also be handled by the compositor thread.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If any animation triggers paint, layout, or both, the &amp;quot;main thread&amp;quot; will be required to do work. This is true for both CSS- and JavaScript-based animations, and the overhead of layout or paint will likely dwarf any work associated with CSS or JavaScript execution, rendering the question moot.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Sam Thorogood</name>
    </author>
  </entry>
  
  <entry>
    <title>Animating Modal Views</title>
    <link href="https://web.dev/animating-modal-views/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/animating-modal-views/</id>
    <content type="html" mode="escaped">  &lt;figure&gt;
    &lt;img alt=&quot;Animating a modal view.&quot; decoding=&quot;async&quot; height=&quot;405&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IysQk9ksWLJvaV3KhS83.gif?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
    &lt;figcaption&gt;
      &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/modal-view-animation.html&quot; target=&quot;_blank&quot; class=&quot;external&quot;&gt;Try it&lt;/a&gt;
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;Modal views are for important messages, and for which you have very good reasons to block the user interface. Use them carefully, because they&#39;re disruptive and can easily ruin the user’s experience if overused. But, in some circumstances, they’re the right views to use, and adding some animation will bring them to life.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use modal views sparingly; users get frustrated if you interrupt their experience unnecessarily.&lt;/li&gt;
&lt;li&gt;Adding scale to the animation gives a nice &amp;quot;drop on&amp;quot; effect.&lt;/li&gt;
&lt;li&gt;Get rid of the modal view quickly when the user dismisses it. However, bring the modal view onto the screen a little more slowly so that it doesn&#39;t surprise the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The modal overlay should be aligned to the viewport, so set its &lt;code&gt;position&lt;/code&gt; to &lt;code&gt;fixed&lt;/code&gt;:&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 selector&quot;&gt;.modal&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;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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; 100%&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; 100%&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;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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;will-change&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opacity&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;It has an initial &lt;code&gt;opacity&lt;/code&gt; of 0, so it&#39;s hidden from view, but then it also needs &lt;code&gt;pointer-events&lt;/code&gt; set to &lt;code&gt;none&lt;/code&gt; so that clicks and touches pass through. Without that, it blocks all interactions, rendering the whole page unresponsive. Finally, because it animates its &lt;code&gt;opacity&lt;/code&gt; and &lt;code&gt;transform&lt;/code&gt;, those need to be marked as changing with &lt;code&gt;will-change&lt;/code&gt; (see also &lt;a href=&quot;https://web.dev/animations-and-performance/#using-the-will-change-property&quot;&gt;Using the will-change property&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;When the view is visible, it needs to accept interactions and have an &lt;code&gt;opacity&lt;/code&gt; of 1:&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 selector&quot;&gt;.modal.visible&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;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&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;Now whenever the modal view is required, you can use JavaScript to toggle the &amp;quot;visible&amp;quot; class:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visible&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;At this point, the modal view appears without any animation, so you can now add that in
(see also &lt;a href=&quot;https://web.dev/custom-easing/&quot;&gt;Custom Easing&lt;/a&gt;):&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 selector&quot;&gt;.modal&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;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1.15&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;br /&gt;  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    transform 0.1s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;    opacity 0.1s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Adding &lt;code&gt;scale&lt;/code&gt; to the transform makes the view appear to drop onto the screen slightly, which is a nice effect. The default transition applies to both transform and opacity properties with a custom curve and a duration of 0.1 seconds.&lt;/p&gt;
&lt;p&gt;The duration is pretty short, though, but it&#39;s ideal for when the user dismisses the view and wants to get back to your app. The downside is that it’s probably too aggressive for when the modal view appears. To fix this, override the transition values for the &lt;code&gt;visible&lt;/code&gt; class:&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 selector&quot;&gt;.modal.visible&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;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&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;br /&gt;  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    transform 0.3s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;    opacity 0.3s &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now the modal view takes 0.3 seconds to come onto the screen, which is a bit less aggressive, but it is dismissed quickly, which the user will appreciate.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Custom easing</title>
    <link href="https://web.dev/custom-easing/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/custom-easing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Sometimes you won&#39;t want to use the easing keywords that are included with CSS, or you will be using Web Animations or a JavaScript framework. In these cases, you can typically define your own curves (or equations), and this provides a lot of control over the feel of your project&#39;s animations.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/custom-easing/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Custom easing allows you to give more personality to your projects.&lt;/li&gt;
&lt;li&gt;You can create cubic Bézier curves that resemble the default animation curves (ease-out, ease-in, etc.), but with emphasis in different places.&lt;/li&gt;
&lt;li&gt;Use JavaScript when you need more control over the animation timing and behavior, for example, elastic or bounce animations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;re animating with CSS, you&#39;ll find that you can define cubic Bézier curves to define the timing. In fact, the keywords &lt;code&gt;ease&lt;/code&gt;, &lt;code&gt;ease-in&lt;/code&gt;, &lt;code&gt;ease-out&lt;/code&gt;, and &lt;code&gt;linear&lt;/code&gt; map to predefined Bézier curves, which are detailed in the &lt;a href=&quot;https://www.w3.org/TR/css3-transitions/&quot; rel=&quot;noopener&quot;&gt;CSS transitions specification&lt;/a&gt; and the &lt;a href=&quot;https://w3c.github.io/web-animations/#scaling-using-a-cubic-bezier-curve&quot; rel=&quot;noopener&quot;&gt;Web Animations specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These Bézier curves take four values, or two pairs of numbers, with each pair describing the X and Y coordinates of a cubic Bézier curve’s control points. The starting point of the Bézier curve has coordinates (0, 0) and the ending point has coordinates (1, 1); you get to set the X and Y values of the two control points. The X values for the two control points must be between 0 and 1, and each control point’s Y value can exceed the [0, 1] limit, although the spec isn’t clear by how much.&lt;/p&gt;
&lt;p&gt;Changing the X and Y value of each control point gives you a vastly different curve, and therefore a vastly different feel to your animation. For example, if the first control point is in the lower right area, the animation will be slow to start. If it’s in the top left area, it’s going to be fast to start. Conversely, if the second control point is in the bottom right area of the grid, it’s going to be fast at the end; if it’s in the top left, it will be slow to end.&lt;/p&gt;
&lt;p&gt;For comparison, here are two curves: a typical ease-in-out curve and a custom curve:&lt;/p&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Ease-in-out animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/b6nf4Ki4PzKFxEDMIFnX.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Custom animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/37zeBdZRC3cZGORp151r.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-custom-curve.html&quot; rel=&quot;noopener&quot;&gt;See an animation with custom easing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The CSS for the custom curve is:&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.465&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.183&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.946&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;The first two numbers are the X and Y coordinates of the first control point, and the second two numbers are the X and Y coordinates of the second control point.&lt;/p&gt;
&lt;p&gt;Making a custom curve is a lot of fun, and it gives you significant control over the feel of the animation. For example, given the above curve, you can see that the curve resembles a classic ease-in-out curve, but with a shortened ease-in, or &amp;quot;getting started,&amp;quot; portion, and an elongated slowdown at the end.&lt;/p&gt;
&lt;p&gt;Experiment with this &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/curve-playground.html&quot; rel=&quot;noopener&quot;&gt;animation curve tool&lt;/a&gt; and see how the curve affects the feel of an animation.&lt;/p&gt;
&lt;h2 id=&quot;use-javascript-frameworks-for-more-control&quot;&gt;Use JavaScript frameworks for more control &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/custom-easing/#use-javascript-frameworks-for-more-control&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you need even more control than a cubic Bézier curve can provide. If you wanted an elastic bounce feel, you might consider using a JavaScript framework, because this is a difficult effect to achieve with either CSS or Web Animations.&lt;/p&gt;
&lt;h3 id=&quot;tweenmax&quot;&gt;TweenMax &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/custom-easing/#tweenmax&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One powerful framework is &lt;a href=&quot;https://github.com/greensock/GreenSock-JS/tree/master/src/minified&quot; rel=&quot;noopener&quot;&gt;GreenSock’s TweenMax&lt;/a&gt; (or TweenLite if you want to keep things really lightweight), because you get a lot of control from it in a small JavaScript library, and it’s a very mature codebase.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-elastic.html&quot; rel=&quot;noopener&quot;&gt;See an elastic ease animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To use TweenMax, include this script in your page:&lt;/p&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 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 attr-name&quot;&gt;src&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;https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js&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;span class=&quot;token script&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;After the script is in place, you can call TweenMax against your element and tell it which properties you’d like, along with any easing you’d like. There are many easing options that you can use; the code below uses an elastic ease-out:&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 keyword&quot;&gt;var&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&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;my-box&#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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; animationDurationInSeconds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    TweenMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; animationDurationInSeconds&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 literal-property property&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;100%&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;ease&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Elastic.easeOut&#39;&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;a href=&quot;https://greensock.com/docs/#/HTML5/GSAP/TweenMax/&quot; rel=&quot;noopener&quot;&gt;TweenMax documentation&lt;/a&gt; highlights all the options you have here, so it&#39;s well worth a read.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Sam Thorogood</name>
    </author>
  </entry>
  
  <entry>
    <title>The Basics of easing</title>
    <link href="https://web.dev/the-basics-of-easing/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/the-basics-of-easing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Nothing in nature moves linearly from one point to another. In reality, things tend to accelerate or decelerate as they move. Our brains are wired to expect this kind of motion, so when animating, you should use this to your advantage. Natural motion makes your users feel more comfortable with your apps, which in turn leads to a better overall experience.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#summary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Easing makes your animations feel more natural.&lt;/li&gt;
&lt;li&gt;Choose ease-out animations for UI elements.&lt;/li&gt;
&lt;li&gt;Avoid ease-in or ease-in-out animations unless you can keep them short; they tend to feel sluggish to end users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In classic animation, the term for motion that starts slowly and accelerates is &amp;quot;slow in,&amp;quot; and for motion that starts quickly and decelerates is &amp;quot;slow out.&amp;quot; The terminology most commonly used on the web for these are “ease in” and “ease out,” respectively. Sometimes the two are combined, which is called &amp;quot;ease in out.&amp;quot; Easing, then, is really the process of making the animation less severe or pronounced.&lt;/p&gt;
&lt;h2 id=&quot;easing-keywords&quot;&gt;Easing keywords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#easing-keywords&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CSS transitions and animations both let you &lt;a href=&quot;https://web.dev/the-basics-of-easing/choosing-the-right-easing&quot;&gt;choose the kind of easing you want to use for your animations&lt;/a&gt;. You can use keywords that affect the easing (or &lt;code&gt;timing&lt;/code&gt;, as it&#39;s sometimes called) of the animation in question. You can also &lt;a href=&quot;https://web.dev/the-basics-of-easing/custom-easing&quot;&gt;go completely custom with your easing&lt;/a&gt;, which gives you a lot more freedom to express your app&#39;s personality.&lt;/p&gt;
&lt;p&gt;Here are some of the keywords that you can use in CSS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;linear&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ease-in&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ease-out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ease-in-out&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Source: &lt;a href=&quot;http://www.w3.org/TR/css3-transitions/#transition-timing-function-property&quot; rel=&quot;noopener&quot;&gt;CSS Transitions, W3C&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also use a &lt;code&gt;steps&lt;/code&gt; keyword, which allows you to create transitions that have discrete steps, but the keywords listed above are the most useful for creating animations that feel natural.&lt;/p&gt;
&lt;h2 id=&quot;linear-animations&quot;&gt;Linear animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#linear-animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Linear ease animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fJVZYp1enwR3A0xHieyv.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
&lt;p&gt;Animations without any kind of easing are referred to as &lt;strong&gt;linear&lt;/strong&gt;. A graph of a linear transition looks like this:&lt;/p&gt;
&lt;p&gt;As time moves along, the value increases in equal amounts. With linear motion, things tend to feel robotic and unnatural, and this is something that users find jarring. Generally speaking, you should avoid linear motion.&lt;/p&gt;
&lt;p&gt;Whether you’re coding your animations using CSS or JavaScript, you’ll find that there is always an option for linear motion.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-linear.html&quot; rel=&quot;noopener&quot;&gt;See a linear animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To achieve the effect above with CSS, the code would look something like this:&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;ease-out-animations&quot;&gt;Ease-out animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#ease-out-animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt=&quot;Ease-out animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/J1CgJeztL7W6P7z5tvIs.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Easing out causes the animation to start more quickly than linear ones, and it also has deceleration at the end.&lt;/p&gt;
&lt;p&gt;Easing out is typically the best for user interface work, because the fast start gives your animations a feeling of responsiveness, while still allowing for a natural slowdown at the end.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-ease-out.html&quot; rel=&quot;noopener&quot;&gt;See an ease-out animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are many ways to achieve an ease out effect, but the simplest is the &lt;code&gt;ease-out&lt;/code&gt; keyword in CSS:&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;ease-in-animations&quot;&gt;Ease-in animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#ease-in-animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt=&quot;Ease-in animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uphxp5GQJId8zdmorpHe.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Ease-in animations start slowly and end fast, which is the opposite of ease-out animations.&lt;/p&gt;
&lt;p&gt;This kind of animation is like a heavy stone falling, where it starts slowly and hits the ground quickly with a deadening thump.&lt;/p&gt;
&lt;p&gt;From an interaction point of view, however, ease-ins can feel a little unusual because of their abrupt end; things that move in the real world tend to decelerate rather than simply stopping suddenly. Ease-ins also have the detrimental effect of feeling sluggish when starting, which negatively impacts the perception of responsiveness in your site or app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-ease-in.html&quot; rel=&quot;noopener&quot;&gt;See an ease-in animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To use an ease-in animation, similarly to ease-out and linear animations, you can use its keyword:&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms ease-in&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;ease-in-out-animations&quot;&gt;Ease-in-out animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/the-basics-of-easing/#ease-in-out-animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt=&quot;Ease-in-out animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CJejZhK53zGmYGoFCBSp.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Easing both in and out is akin to a car accelerating and decelerating and, used judiciously, can provide a more dramatic effect than just easing out.&lt;/p&gt;
&lt;p&gt;Do not use an overly long animation duration, because of the sluggishness of an ease-in start to the animation. Something in the range of 300-500ms is typically suitable, but the exact number depends heavily on the feel of your project. That said, because of the slow start, fast middle, and slow end, there is increased contrast in the animation, which can be quite satisfying for users.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-ease-in-out.html&quot; rel=&quot;noopener&quot;&gt;See an ease-in-out animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To get an ease-in-out animation, you can use the &lt;code&gt;ease-in-out&lt;/code&gt; CSS keyword:&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms ease-in-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>CSS versus JavaScript animations</title>
    <link href="https://web.dev/css-vs-javascript/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/css-vs-javascript/</id>
    <content type="html" mode="escaped">&lt;p&gt;There are two primary ways to create animations on the web: with CSS and with JavaScript. Which one you choose really depends on the other dependencies of your project, and what kinds of effects you&#39;re trying to achieve.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-vs-javascript/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use CSS animations for simpler &amp;quot;one-shot&amp;quot; transitions, like toggling UI element states.&lt;/li&gt;
&lt;li&gt;Use JavaScript animations when you want to have advanced effects like bouncing, stop, pause, rewind, or slow down.&lt;/li&gt;
&lt;li&gt;If you choose to animate with JavaScript, use the Web Animations API or a modern framework that you&#39;re comfortable with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most basic animations can be created with either CSS or JavaScript, but the amount of effort and time differs (see also &lt;a href=&quot;https://web.dev/animations-and-performance/#css-vs-javascript-performance&quot;&gt;CSS vs JavaScript Performance&lt;/a&gt;). Each has its pros and cons, but these are good guidelines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use CSS when you have smaller, self-contained states for UI elements.&lt;/strong&gt; CSS transitions and animations are ideal for bringing a navigation menu in from the side, or showing a tooltip. You may end up using JavaScript to control the states, but the animations themselves will be in your CSS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use JavaScript when you need significant control over your animations.&lt;/strong&gt; The Web Animations API is the standards-based approach, available today in most modern browsers. This provides real objects, ideal for complex object-oriented applications. JavaScript is also useful when you need to stop, pause, slow down, or reverse your animations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;requestAnimationFrame&lt;/code&gt; directly when you want to orchestrate an entire scene by hand.&lt;/strong&gt; This is an advanced JavaScript approach, but can be useful if you&#39;re building a game or drawing to an HTML canvas.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;WaNoqBAp8NI&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Alternatively, if you&#39;re already using a JavaScript framework that includes animation functionality, such as via jQuery&#39;s &lt;a href=&quot;https://api.jquery.com/animate/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;.animate()&lt;/code&gt;&lt;/a&gt; method or &lt;a href=&quot;https://github.com/greensock/GreenSock-JS/tree/master/src/minified&quot; rel=&quot;noopener&quot;&gt;GreenSock&#39;s TweenMax&lt;/a&gt;, then you may find it more convenient overall to stick with that for your animations.&lt;/p&gt;
&lt;h2 id=&quot;animate-with-css&quot;&gt;Animate with CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-vs-javascript/#animate-with-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Animating with CSS is the simplest way to get something moving on screen. This approach is described as &lt;em&gt;declarative&lt;/em&gt;, because you specify what you&#39;d like to happen.&lt;/p&gt;
&lt;p&gt;Below is some CSS that moves an element &lt;code&gt;100px&lt;/code&gt; in both the X and Y axes. It&#39;s done by using a CSS transition that&#39;s set to take &lt;code&gt;500ms&lt;/code&gt;. When the &lt;code&gt;move&lt;/code&gt; class is added, the &lt;code&gt;transform&lt;/code&gt; value is changed and the transition begins.&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 selector&quot;&gt;.box&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;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&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 property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 500ms&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 selector&quot;&gt;.box.move&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;translate&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; 100px&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-simple.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Besides the transition&#39;s duration, there are options for the &lt;em&gt;easing&lt;/em&gt;, which is essentially how the animation feels. For more information about easing, see &lt;a href=&quot;https://web.dev/the-basics-of-easing/&quot;&gt;The Basics of Easing&lt;/a&gt; guide.&lt;/p&gt;
&lt;p&gt;If, as in the above snippet, you create separate CSS classes to manage your animations, you can then use JavaScript to toggle each animation on and off:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;move&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Doing this provides a nice balance to your apps. You can focus on managing state with JavaScript, and simply set the appropriate classes on the target elements, leaving the browser to handle the animations. If you go down this route, you can listen to &lt;code&gt;transitionend&lt;/code&gt; events on the element, but only if you’re able to forego support for older versions of Internet Explorer; version 10 was the first version to support these events. All other browsers have supported the event for some time.&lt;/p&gt;
&lt;p&gt;The JavaScript required to listen for the end of a transition looks like this:&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 keyword&quot;&gt;var&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&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;.box&#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;br /&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;transitionend&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onTransitionEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onTransitionEnd&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;    &lt;span class=&quot;token comment&quot;&gt;// Handle the transition finishing.&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;In addition to using CSS transitions, you can also use CSS animations, which allow you to have much more control over individual animation keyframes, durations, and iterations.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you’re new to animations, keyframes are an old term from hand-drawn animations. Animators would create specific frames for a piece of action, called key frames, which would capture things like the most extreme part of some motion, and then they would set about drawing all the individual frames in between the keyframes. We have a similar process today with CSS animations, where we instruct the browser what values CSS properties need to have at given points, and it fills in the gaps. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can, for example, animate the box in the same way with transitions, but have it animate without any user interactions like clicking, and with infinite repetitions. You can also change multiple properties at the same time.&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 selector&quot;&gt;.box&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-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; movingBox&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;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1300ms&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;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;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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; movingBox&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;0%&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;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&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 property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.3&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 selector&quot;&gt;25%&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;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.9&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 selector&quot;&gt;50%&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;translate&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; 100px&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 property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.2&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 selector&quot;&gt;100%&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;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 30px&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 property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.8&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-keyframes.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With CSS animations you define the animation itself independently of the target element, and use the &lt;code&gt;animation-name&lt;/code&gt; property to choose the required animation.&lt;/p&gt;
&lt;p&gt;If you want your CSS animations to work on older browsers, you will need to add vendor prefixes. Many tools can help you create the prefixed versions of the CSS you need, allowing you to write the unprefixed version in your source files.&lt;/p&gt;
&lt;h2 id=&quot;animate-with-javascript-and-the-web-animations-api&quot;&gt;Animate with JavaScript and the Web Animations API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-vs-javascript/#animate-with-javascript-and-the-web-animations-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Creating animations with JavaScript is, by comparison, more complex than writing CSS transitions or animations, but it typically provides developers significantly more power. You can use the &lt;a href=&quot;https://w3c.github.io/web-animations/&quot; rel=&quot;noopener&quot;&gt;Web Animations API&lt;/a&gt; to either animate specific CSS properties or build composable effect objects.&lt;/p&gt;
&lt;p&gt;JavaScript animations are &lt;em&gt;imperative&lt;/em&gt;, as you write them inline as part of your code. You can also encapsulate them inside other objects. Below is the JavaScript that you would need to write to re-create the CSS transition described earlier:&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 keyword&quot;&gt;var&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&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;.box&#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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; player &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animate&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;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;translate(0)&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;translate(100px, 100px)&#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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&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;player&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;finish&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&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;    target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;translate(100px, 100px)&#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;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;By default, Web Animations only modify the presentation of an element. If you&#39;d like to have your object remain at the location it has moved to, then you should modify its underlying styles when the animation has finished, as per our sample.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-wa.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Web Animations API is a relatively new standard from the W3C. It is supported natively in most modern browsers. For non-supporting modern browsers, &lt;a href=&quot;https://github.com/web-animations/web-animations-js&quot; rel=&quot;noopener&quot;&gt;a polyfill is available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With JavaScript animations, you&#39;re in total control of an element&#39;s styles at every step. This means you can slow down animations, pause them, stop them, reverse them, and manipulate elements as you see fit. This is especially useful if you&#39;re building complex, object-oriented applications, because you can properly encapsulate your behavior.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Sam Thorogood</name>
    </author>
  </entry>
  
  <entry>
    <title>Asymmetric animation timing</title>
    <link href="https://web.dev/asymmetric-animation-timing/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/asymmetric-animation-timing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Asymmetric animation timing improves the user experience by allowing you to express personality while at the same time respond quickly to user interactions. It also provides contrast to the feel, which makes the interface more visually appealing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use asymmetric animation timing to add personality and contrast to your work.&lt;/li&gt;
&lt;li&gt;Always favor the user&#39;s interaction; use shorter durations when responding to taps or clicks, and reserve longer durations for times when you aren&#39;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like most &amp;quot;rules&amp;quot; of animation, you should experiment to find out what works for your application, but when it comes to the user experience, users are notoriously impatient. The rule of thumb is to &lt;strong&gt;always respond to a user interaction quickly&lt;/strong&gt;. That said, most of the time the user&#39;s action is asymmetric, and therefore the animation can be, too.&lt;/p&gt;
&lt;p&gt;For example, when a user taps to display a sidebar navigation, you should display it as quickly as possible, with a duration of around 100ms. When the user dismisses the menu, however, you can afford to animate the view out a little more slowly, say, around the 300ms mark.&lt;/p&gt;
&lt;p&gt;By contrast, when you bring on a modal view, this is normally to display an error or some other critical message. In such cases, you will want to bring on the view a little more slowly, again around the 300ms mark, but dismissal, which the user triggers, should happen very quickly.&lt;/p&gt;
&lt;p&gt;The general rule of thumb, then, is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For UI animations triggered by a user’s interaction, such as view transitions or showing an element, have a fast intro (short duration), but a slow outro (longer duration).&lt;/li&gt;
&lt;li&gt;For UI animations triggered by your code, such as errors or modal views, have a slower intro (longer duration), but a fast outro (short duration).&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Choosing the right easing</title>
    <link href="https://web.dev/choosing-the-right-easing/"/>
    <updated>2014-08-08T00:00:00Z</updated>
    <id>https://web.dev/choosing-the-right-easing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Having discussed the various options available for easing in animations, what kind should you use in your projects, and what kind of durations should your animations have?&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choosing-the-right-easing/#summary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use ease-out animations for UI elements; a Quintic ease-out is a very nice, albeit quick, ease.&lt;/li&gt;
&lt;li&gt;Be sure to use the animation duration; ease-outs and ease-ins should be 200ms-500ms, whereas bounces and elastic eases should clock in a longer duration of 800ms-1200ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt=&quot;A Quintic ease-out animation curve.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/dnk1t7HFI8g3zNiZw2gC.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Generally speaking, an &lt;strong&gt;ease-out&lt;/strong&gt; will be the right call, and certainly a good default. It is quick to start, giving your animations a feeling of responsiveness, which is desirable, but with a nice slowdown at the end.&lt;/p&gt;
&lt;p&gt;There is a group of well-known ease-out equations beyond the one specified with the &lt;code&gt;ease-out&lt;/code&gt; keyword in CSS, which range in their &amp;quot;aggressiveness.&amp;quot; For a fast ease-out effect, consider a &lt;a href=&quot;http://easings.net/#easeOutQuint&quot; rel=&quot;noopener&quot;&gt;Quintic ease-out&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/animations/box-move-quintic-ease-out.html&quot; rel=&quot;noopener&quot;&gt;See a Quintic ease-out animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Other easing equations, particularly bounces or elastic eases, should be used sparingly, and only when it’s appropriate to your project. There are few things that bring a user out of an experience like a jarring animation. If your project isn’t intended to be fun, then don’t have elements bouncing around the UI. Conversely, if you’re creating a site that is supposed to be lighthearted, then by all means use bounces!&lt;/p&gt;
&lt;p&gt;Play around with eases, see which ones match your project’s personality, and go from there. For a full list of easing types, along with demos, see &lt;a href=&quot;http://easings.net/&quot; rel=&quot;noopener&quot;&gt;easings.net&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;pick-the-right-animation-duration&quot;&gt;Pick the right animation duration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choosing-the-right-easing/#pick-the-right-animation-duration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is important that any animation added to your project has the correct duration. Too short and the animation will feel aggressive and sharp; too long and it will be obstructive and annoying.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ease-outs: around 200ms-500ms&lt;/strong&gt;. This gives the eye a chance to see the animation, but it doesn’t feel obstructive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ease-ins: around 200ms-500ms&lt;/strong&gt;. Bear in mind that it will jolt at the end, and no amount of timing changes will soften that impact.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bounce or elastic effects: around 800ms-1200ms&lt;/strong&gt;. You need to allow time for the elastic or bounce effect to &amp;quot;settle.&amp;quot; Without this extra time, the elastic bouncing part of the animation will be aggressive and unpleasant to the eye.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, these are just guidelines. Experiment with your own eases and choose what feels right for your projects.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Canvas inspection using Chrome DevTools</title>
    <link href="https://web.dev/canvas-inspection/"/>
    <updated>2013-09-16T00:00:00Z</updated>
    <id>https://web.dev/canvas-inspection/</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/canvas-inspection/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It doesn’t matter if you’re using a 2D or WebGL context, anyone who has ever worked with the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element knows that it can be extremely difficult to debug. Working with a canvas usually involves a long and difficult-to-follow list of calls:&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;draw&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;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clearRect&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;258&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;258&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;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fillStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#EEEEEE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;beginPath&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;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;129&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;129&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token number&quot;&gt;6.28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closePath&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;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&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;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// … and on and on&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;Sometimes you want to capture the instructions sent to a canvas context and step through them one by one. Fortunately there is a new Canvas Inspection feature in Chrome’s DevTools that lets us do just that!&lt;/p&gt;
&lt;p&gt;In this article I will be showing you how to use this feature to start debugging your canvas work. The inspector supports both 2D and WebGL contexts, so no matter which one you&#39;re using, you should be able to get useful debugging information straight away.&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/canvas-inspection/#getting-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get started, go to &lt;code&gt;about:flags&lt;/code&gt; in Chrome and toggle on &lt;strong&gt;“Enable Developer Tools experiments”&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Enabling Developer Tools Experiments in about:flags.&quot; decoding=&quot;async&quot; height=&quot;72&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z0FUMLVzTh4wRU68tTdb.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Figure 1 - Enabling Developer Tools Experiments in about:flags&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Next, head over to DevTools and hit the cog &lt;img alt=&quot;Cog icon&quot; decoding=&quot;async&quot; height=&quot;19&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 20px) 20px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=20 20w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=23 23w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=26 26w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=30 30w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=34 34w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=39 39w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RwBtZbOHKdOeubytuJVl.png?auto=format&amp;w=40 40w&quot; width=&quot;20&quot; /&gt; in the lower right corner. From there you can go to &lt;strong&gt;Experiments&lt;/strong&gt; and enable &lt;strong&gt;Canvas inspection&lt;/strong&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Enabling Canvas inspection in DevTools’ experiments&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 618px) 618px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Tg8sR7fQ2mRCrSSc8WuV.png?auto=format&amp;w=1236 1236w&quot; width=&quot;618&quot; /&gt;
  &lt;figcaption&gt;Figure 2 - Enabling Canvas inspection in DevTools’ experiments&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the changes to take effect, you need to &lt;strong&gt;close and reopen DevTools&lt;/strong&gt; (you can use &lt;code&gt;Alt+R&lt;/code&gt; or &lt;code&gt;Option+R&lt;/code&gt;, a handy alternative).&lt;/p&gt;
&lt;p&gt;When DevTools reopens, go to the Profiles section and you&#39;ll see a new Canvas Profiler option.&lt;/p&gt;
&lt;p&gt;To begin with you will notice that the Canvas Profiler is disabled. Once you have a page that contains a canvas you want to debug simply press &lt;strong&gt;Enable&lt;/strong&gt; and the page will reload ready to capture the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; calls:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Switching on the Canvas Profiler&quot; decoding=&quot;async&quot; height=&quot;95&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 434px) 434px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Uh7W5j55HPS32VWYup3F.png?auto=format&amp;w=868 868w&quot; width=&quot;434&quot; /&gt;
  &lt;figcaption&gt;Figure 3 - Switching on the Canvas Profiler&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You’ll need to decide whether you would like to capture a single frame, or consecutive frames, where a frame is exactly the same as you would see in the DevTools Timeline.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; A frame represents a single pass through the your page&#39;s event loop. This involves running JavaScript, handling events, updating the DOM, style changes, performing layout and painting &amp;amp; compositing the page. For smooth animation, you want each frame to take less than 1/60th of a second, or 16.6 ms. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Single frame&lt;/strong&gt; captures the calls until the end of the current frame then stops. &lt;strong&gt;Consecutive frames&lt;/strong&gt;, on the other hand, captures all frames of all &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; elements until you tell it to stop. Which mode you choose depends on how you&#39;re using the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. For an ongoing animation, you might want to capture a single frame. For a brief animation that occurs in response to a user event, you may need to capture consecutive frames.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Choosing how many frames to capture&quot; decoding=&quot;async&quot; height=&quot;216&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qihFJ7P7tDl2iRrAkwxm.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Figure 4 - Choosing how many frames to capture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With that we’re all set up and ready to start capturing!&lt;/p&gt;
&lt;h2 id=&quot;capturing-frames&quot;&gt;Capturing frames &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/canvas-inspection/#capturing-frames&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To capture you simply press &lt;strong&gt;Start&lt;/strong&gt; and then interact with your application as you normally would. After some time head back over to DevTools and, if you’re capturing consecutively, press &lt;strong&gt;Stop&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now you have a shiny new profile in the list on the left, complete with the number of captured context calls across all &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; elements. Click on the profile and you’ll see a screen that looks something like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A canvas profile in DevTools.&quot; decoding=&quot;async&quot; height=&quot;529&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ltHy97bB0vhQPVv9gFYR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Figure 5 - A canvas profile in DevTools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the lower pane you’ll see a list of all the captured frames you can step through and, as you click on each, the screenshot at the top will show you the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element’s state at the end of that frame. If you have multiple &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; elements you can choose which one is shown using the menu just below the screenshot.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Choosing your canvas context.&quot; decoding=&quot;async&quot; height=&quot;77&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 254px) 254px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LlTEUUpnLQmB5fu4sTbB.png?auto=format&amp;w=508 508w&quot; width=&quot;254&quot; /&gt;
  &lt;figcaption&gt;Figure 6 - Choosing your canvas context&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Inside the frame you will see draw call groups. Each draw call group contains a single draw call, which will be the last call in the group. So what’s a draw call? For a 2D context that would be things like &lt;code&gt;clearRect()&lt;/code&gt;, &lt;code&gt;drawImage()&lt;/code&gt;, &lt;code&gt;fill()&lt;/code&gt;, &lt;code&gt;stroke()&lt;/code&gt;, &lt;code&gt;putImageData()&lt;/code&gt; or any text rendering functions, and for WebGL it would be &lt;code&gt;clear()&lt;/code&gt;, &lt;code&gt;drawArrays()&lt;/code&gt; or &lt;code&gt;drawElements()&lt;/code&gt;. It’s essentially &lt;strong&gt;anything&lt;/strong&gt; that would change the current drawing buffer’s contents. (If you’re not into graphics you can think of a buffer as a bitmap with pixels that we&#39;re manipulating.)&lt;/p&gt;
&lt;p&gt;Now all you do is step through the list. You can do that at the frame, draw call group or call level. Whichever way you choose to step through the list (and there are buttons just below the screenshot that help you navigate quickly) you’ll see the context at that point, meaning you can quickly find and fix bugs as they crop up.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Navigation buttons for convenient list hopping.&quot; decoding=&quot;async&quot; height=&quot;135&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/g0HxUetrcruphUlD1JGP.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Figure 7 - navigation buttons for convenient list hopping&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;spot-the-difference&quot;&gt;Spot the difference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/canvas-inspection/#spot-the-difference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another useful feature is the ability to see which properties and variables have changed between two calls.&lt;/p&gt;
&lt;p&gt;To see that you simply click on the sidebar button (&lt;img alt=&quot;Sidebar icon.&quot; decoding=&quot;async&quot; height=&quot;19&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 20px) 20px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=20 20w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=23 23w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=26 26w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=30 30w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=34 34w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=39 39w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EXwoN9oit2uww4ASy1yR.png?auto=format&amp;w=40 40w&quot; width=&quot;20&quot; /&gt;) and a new view will pop out. As you step through the draw calls you will see the properties that have been updated. Any buffers or arrays will display their contents if you hover over them.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Spot the difference&quot; decoding=&quot;async&quot; height=&quot;422&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 602px) 602px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Yq8pY9qSV56EG6bPmd0d.gif?auto=format&amp;w=1204 1204w&quot; width=&quot;602&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;be-heard&quot;&gt;Be heard! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/canvas-inspection/#be-heard&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So now you know how to debug your canvas work in Chrome’s DevTools. If you have feedback on the Canvas Profiler tool, please do &lt;a href=&quot;http://crbug.com/new&quot; rel=&quot;noopener&quot;&gt;file a bug&lt;/a&gt; or post to the &lt;a href=&quot;https://groups.google.com/forum/#!forum/google-chrome-developer-tools&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools Group&lt;/a&gt;. Let us know if you find any bugs or if there’s anything else you would like to see when inspecting a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, because it’s really only through developer use and feedback that Chrome’s tools get better.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Antialiasing 101</title>
    <link href="https://web.dev/antialiasing-101/"/>
    <updated>2013-06-28T00:00:00Z</updated>
    <id>https://web.dev/antialiasing-101/</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/antialiasing-101/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Antialiasing is something of an unsung hero in web graphics; it’s the reason we have clear text and smooth vector shapes on our screens. There are actually a couple of approaches to antialiasing used in browsers today which are most obvious when it comes to text rendering. When the algorithm used for antialising switches it can lead to unexpected visual results. In this article we’ll take a look at the approaches to antialiasing and see how the pixels get drawn.&lt;/p&gt;
&lt;p&gt;All of our screens are made up, as we all know, of pixels. It’s a giant grid of blocks, and each one contains red, green and blue (RGB) components. At a distance we see images, text and icons, but up close we can actually see the grid of RGB components and how everything is made up.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Pixels of a screen up close. Each pixel has red, green and blue components&quot; decoding=&quot;async&quot; height=&quot;350&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/v3n2et6F22tiQgQeCw6O.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Figure 1 - Pixels of a screen up close. Each pixel has red, green and blue components.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;antialiasing&quot;&gt;Antialiasing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/antialiasing-101/#antialiasing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So what happens when we’re drawing a vector shape and it passes through “part” of a pixel? Let’s assume that the shape we’re drawing is black and the background is white. Should we color that pixel at all? If we color it, what color should it be? Black, gray, something else?&lt;/p&gt;
&lt;p&gt;The process of antialiasing determines which color we should use when we’re filling in pixels. The simplest version of it is called grayscale antialiasing, and it treats the three components of the pixels equally. So if the pixel is half covered  -  and let&#39;s assume black text on white for a second to keep it simple  -  you&#39;d think each component will be set to half brightness (I know I certainly did), but actually it&#39;s more complex than that: you have to account for gamma, which means you&#39;ll likely never set it to that exact value. That of course makes things a little trickier, but since this is an introduction to the topic I won&#39;t dive into that here. The important thing to know is that grayscale antialiasing is dealt with at the &lt;strong&gt;pixel level&lt;/strong&gt; and we can, in fact, do much better.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Figure 2 - Antialiased vs hard edges&quot; decoding=&quot;async&quot; height=&quot;339&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/FrvezWNFi0mc2fpkuaQT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Figure 2 - Antialiased vs hard edges
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In Figure 2 you can see the same triangle being drawn, but on the left it has antialiasing enabled and on the right it’s been disabled. As you can see, when antialiasing is enabled the pixels are shades of gray when the triangle only passes through part of the pixel. When disabled, however, the pixel is filled in as either solid black or solid white and the shape looks jagged.&lt;/p&gt;
&lt;h2 id=&quot;text-rendering&quot;&gt;Text Rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/antialiasing-101/#text-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whenever a browser is rendering text, which is essentially a vector shape, we will come up against the same problem: the characters of text will only partially fill some pixels so we will want to have a strategy for how to fill in those pixels. Ideally we want the text to be antialiased as it will make it easier and more pleasant to read.&lt;/p&gt;
&lt;p&gt;It turns out, however, that the grayscale approach to antialiasing is only &lt;strong&gt;one way&lt;/strong&gt; to handle it. An approach that is often taken is to be a bit more selective with how we enable the RGB components of the pixels. The process is called subpixel antialiasing and over the years the ClearType team at Microsoft in particular have invested a great deal of time and effort to make progress on it. It’s now much more widely used, and all the major browsers use it to a greater or lesser degree.&lt;/p&gt;
&lt;p&gt;Firstly, since we know that each pixel is in fact made up of separate red, green and blue components we detect how much of each of those components should be “switched on” for the pixel in question. So if a pixel is “half covered” from the left hand side then we might switch on the red component totally, the green component to half way, and keep the blue switched off. This process is often described as “tripling the horizontal resolution of the screen”, and relies on the fact that each pixel is really the three separate components side by side rather than a single unit.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Figure 3 - Antialiasing using grayscale vs subpixel&quot; decoding=&quot;async&quot; height=&quot;350&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nFgZpYAtMk1VyJaLXcsi.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Figure 3 - Antialiasing using grayscale vs subpixel
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In figure 3 above you can see that on the left we treat each component equally and each is turned on or off equally (grayscale). On the right hand side, however, we use the subpixel approach, enabling each component (red, green and blue) differently depending on &lt;strong&gt;how much&lt;/strong&gt; it overlaps with the shape being drawn.&lt;/p&gt;
&lt;p&gt;With all that said, however, human vision doesn’t actually weigh red, green and blue light equally. We are far more sensitive to green than either red or blue, and this means that while there is definite benefit over grayscale antialiasing, as &lt;a href=&quot;http://alienryderflex.com/sub_pixel/&quot; rel=&quot;noopener&quot;&gt;Darel Rex Finley notes&lt;/a&gt;, enabling each component separately is &lt;strong&gt;not&lt;/strong&gt; actually going to yield a 3x improvement in clarity. Subpixel antialiasing is definitely helpful, though, and it does mean that we see text more clearly than if grayscale antialiasing is used.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Figure 4 - Subpixel antialiased text. Individual components of the pixels are enabled to create the overall effect&quot; decoding=&quot;async&quot; height=&quot;288&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GdLxu4vlQ8xSH0ypdvIK.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Figure 4 - Subpixel antialiased text. Individual components of the pixels are enabled to create the overall effect
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;cutting-to-the-chase&quot;&gt;Cutting to the chase &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/antialiasing-101/#cutting-to-the-chase&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What does all this mean for us as developers? Well, from a Chrome perspective at least there is a mix of both grayscale and subpixel antialiasing used to render text, and which one you get depends on a few criteria. To begin with, however, we need to understand a little about layers, since that’s the main criterion at play. If you’ve not come across layers and how they are used internally by Chrome, Tom Wiltzius has written &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/layers/&quot; rel=&quot;noopener&quot;&gt;a fantastic introduction to the topic&lt;/a&gt; that you should read first.&lt;/p&gt;
&lt;p&gt;Assuming you’re familiar with layers, or you’ve just read about them, let’s continue on. If hardware compositing is enabled for the page, and you have text content on a layer that &lt;strong&gt;isn’t&lt;/strong&gt; the &lt;strong&gt;root layer&lt;/strong&gt; it will, by default, be rendered using grayscale antialiasing. Developers often notice that if they apply hacks to elements to get them into their own (non-root) layers (&lt;a href=&quot;http://aerotwist.com/blog/on-translate3d-and-layer-creation-hacks/&quot; rel=&quot;noopener&quot;&gt;such as using translateZ&lt;/a&gt;) that they see text being rendered differently. Often times developers apply “new layer” triggers on the fly through JavaScript or CSS causing the text rendering to switch from subpixel to grayscale; it can be confusing if you don’t know what triggered the rendering change. If your text resides in the root layer, however, it should be rendered with subpixel antialiasing, and consequently it will be much clearer to read.&lt;/p&gt;
&lt;p&gt;But, like all things web, it’s changing. Subpixel antialiasing is being enabled in Chrome for text in non-root layers, provided the layer satisfies three criteria. It’s worth saying that these criteria apply &lt;strong&gt;today&lt;/strong&gt;, but it’s likely they will change and you should expect to see &lt;strong&gt;more&lt;/strong&gt; cases covered over time. Today those criteria are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The layer has a fully opaque background color.&lt;/strong&gt; Notably using &lt;code&gt;border-radius&lt;/code&gt; or a non-default &lt;code&gt;background-clip&lt;/code&gt; value results in the layer being treated as non-opaque and text rendering reverts to grayscale antialiasing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The layer can only have either identity transform or integral translation applied to it.&lt;/strong&gt; By integral we mean rounded values. So for example &lt;code&gt;translate(20.2px, 30px)&lt;/code&gt; would result in grayscale antialiasing since the x component, &lt;code&gt;20.2px&lt;/code&gt;, is non-integral. The identity transform simply means that there is no additional rotation, translation, or scaling applied beyond its default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The layer has an opacity of 1.0&lt;/strong&gt;. Any change in opacity will change the antialiasing from subpixel to grayscale.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Figure 5 - Before and after: grayscale vs. subpixel. Note the     color edging on the text to the right&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8P2VqVpXXb2cz1yQEza8.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Figure 5 - Before and after: grayscale vs. subpixel. Note the
    color edging on the text to the right
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One final thing of note is that applying a CSS animation may cause a new layer to be created, whereas using &lt;code&gt;requestAnimationFrame&lt;/code&gt; does not. For some developers the text rendering differences that implies has precluded the use of CSS animations. So if you’ve been using JavaScript to animate elements because of text rendering differences, check to see if this update fixes things for you!&lt;/p&gt;
&lt;p&gt;So that’s Chrome covered. As far as other browsers go, Opera, as it moves to Chromium, should match Chrome’s behaviours closely. Internet Explorer seems to use subpixel antialiasing for virtually all text (if you’ve enabled ClearType, of course!), although seemingly not in Windows 8’s Metro mode. Safari, given WebKit’s proximity to Blink, behaves very similarly to Chrome, albeit without these newer improvements that allow for more subpixel antialiasing. Firefox largely behaves in the same way as Internet Explorer insofar as it uses subpixel antialiasing for virtually all text. Of course this isn’t an exhaustive list, and there are likely to be cases in &lt;strong&gt;all browsers&lt;/strong&gt; where grayscale antialiasing is used instead of subpixel, but it’s good to know that subpixel antialiasing is widely used across the main set of browsers.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/antialiasing-101/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So now you know a little about how antialiasing works, and why you might see text rendering differences in your sites and applications today, especially on lower DPI devices. If you’re interested in following along with Chrome’s implementation with respect to text rendering you should star the following bugs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://crbug.com/167131&quot; rel=&quot;noopener&quot;&gt;Automatic Font Grayscale Anti-Aliasing above 48px not Overridable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crbug.com/137692&quot; rel=&quot;noopener&quot;&gt;Horrible font rendering with Google Web Fonts on Chrome for Windows&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crbug.com/25541&quot; rel=&quot;noopener&quot;&gt;DirectWrite support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources-and-references&quot;&gt;Resources and references &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/antialiasing-101/#resources-and-references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Subpixel_rendering&quot; rel=&quot;noopener&quot;&gt;Subpixel Rendering on Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://alienryderflex.com/sub_pixel/&quot; rel=&quot;noopener&quot;&gt;Darel Rex Finley on Subpixel Text Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.grc.com/ctwhat.htm&quot; rel=&quot;noopener&quot;&gt;How Subpixel rendering works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.typekit.com/category/type-rendering/&quot; rel=&quot;noopener&quot;&gt;CSS properties that affect type rendering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Avoiding unnecessary paints</title>
    <link href="https://web.dev/speed-unnecessary-paints/"/>
    <updated>2013-05-08T00:00:00Z</updated>
    <id>https://web.dev/speed-unnecessary-paints/</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-unnecessary-paints/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Painting the elements for a site or application can get really expensive, and it can have a negative knock-on effect on our runtime performance. In this article we take a quick look at what can trigger painting in the browser, and how you can prevent unnecessary paints from taking place.&lt;/p&gt;
&lt;h2 id=&quot;painting-a-super-quick-tour&quot;&gt;Painting: A super-quick tour &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-unnecessary-paints/#painting-a-super-quick-tour&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the major tasks a browser has to perform is converting your DOM and CSS into pixels on the screen, and it does this through a fairly complex process. It starts by reading the markup and from this it creates a DOM tree. It does a similar thing with the CSS and from that it creates the CSSOM. The DOM and CSSOM are then combined and, eventually, we arrive at a structure from which we can start to paint some pixels.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you want a much deeper insight into how browsers work there&#39;s an &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/&quot;&gt;in-depth article here on HTML5 Rocks&lt;/a&gt; that you should check out! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The painting process itself is interesting. In Chrome that combined tree of DOM and CSS is rasterized by some software called &lt;a href=&quot;https://code.google.com/p/skia&quot; rel=&quot;noopener&quot;&gt;Skia&lt;/a&gt;. If you&#39;ve ever played with the &lt;code&gt;canvas&lt;/code&gt; element Skia&#39;s API would look awfully familiar to you; there are many &lt;code&gt;moveTo&lt;/code&gt;- and &lt;code&gt;lineTo&lt;/code&gt;-style functions as well as a bunch of more advanced ones. Essentially all the elements that need to be painted are distilled to a collection of Skia calls that can be executed, and the output of is a bunch of bitmaps. These bitmaps are uploaded to the GPU, and the GPU helps out by compositing them together to give us the final picture on screen.&lt;/p&gt;
&lt;figure&gt;
 &lt;img alt=&quot;Dom to pixels&quot; decoding=&quot;async&quot; height=&quot;352&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/6dluZRhiUc2i50OAdnzU.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The thing to take away is that Skia&#39;s workload is directly affected by the styles you apply to your elements. If you use algorithmically heavy styles then Skia is going to have more work to do. &lt;a href=&quot;http://www.google.com/+ColtMcAnlis&quot; rel=&quot;noopener&quot;&gt;Colt McAnlis&lt;/a&gt; has written an &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/css-paint-times/&quot; rel=&quot;noopener&quot;&gt;article on how CSS affects page render weight&lt;/a&gt;, so you should read that for more insight.&lt;/p&gt;
&lt;p&gt;With all that said, paint work takes time to perform, and if we don’t reduce it we will run over our frame budget of ~16ms. Users will notice that we missed frames and see it as jank, which ultimately hurts the user experience of our app. We really don’t want that, so let’s see what kinds of things cause paint work to be necessary, and what we can do about it.&lt;/p&gt;
&lt;h2 id=&quot;scrolling&quot;&gt;Scrolling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-unnecessary-paints/#scrolling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whenever you scroll up or down in the browser it needs to repaint content before it appears onscreen. All being well that will just be a small area, but even if that’s the case the elements that need to be drawn could have complex styles applied. So just because you have a small area to paint doesn&#39;t mean it&#39;s going to happen quickly.&lt;/p&gt;
&lt;p&gt;In order to see what areas are being repainted you can use the “Show Paint Rectangles” feature in Chrome’s DevTools (just hit the little cog in the lower right corner). Then, with DevTools open, simply interact with your page and you’ll see flashing rectangles show where and when Chrome painted a part of your page.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Show Paint Rectangles in Chrome DevTools&quot; decoding=&quot;async&quot; height=&quot;287&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lPRqbWJnCzQre3ZatZG3.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Show Paint Rectangles in Chrome DevTools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Scrolling performance is critical to your site&#39;s success; users really notice when your site or application doesn&#39;t scroll well, and they don&#39;t like it. We therefore have a vested interest in keeping the paint work light during scrolls so users don&#39;t see jank.&lt;/p&gt;
&lt;p&gt;I&#39;ve previously written an &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/scrolling/&quot; rel=&quot;noopener&quot;&gt;article on scrolling performance&lt;/a&gt;, so take a look at that if you want to know more about the specifics of scrolling performance.&lt;/p&gt;
&lt;h2 id=&quot;interactions&quot;&gt;Interactions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-unnecessary-paints/#interactions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Interactions are another cause of paint work: hovers, clicks, touches, drags. Whenever the user performs one of those interactions, let&#39;s say a hover, then Chrome will have to repaint the affected element. And, much as with scrolling, if there&#39;s a large and complex paint required you&#39;re going to see the frame rate drop.&lt;/p&gt;
&lt;p&gt;Everyone wants nice, smooth, interaction animations, so again we will need to see if the styles that change in our animation are costing us too much time.&lt;/p&gt;
&lt;h2 id=&quot;an-unfortunate-combination&quot;&gt;An unfortunate combination &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-unnecessary-paints/#an-unfortunate-combination&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A demo with expensive paints&quot; decoding=&quot;async&quot; height=&quot;388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lEaGOVpmC9p83WMCBGfK.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A demo with expensive paints&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;What happens if I scroll and I happen to move the mouse at the same time? It&#39;s perfectly possible for me to &lt;strong&gt;inadvertently&lt;/strong&gt; &amp;quot;interact&amp;quot; with an element as I scroll past it, triggering an expensive paint. That, in turn, could push me through my frame budget of ~16.7ms (the time we need to stay under that to hit 60 frames per second). I&#39;ve &lt;a href=&quot;https://dl.dropboxusercontent.com/u/2272348/codez/expensivescroll/demo.html&quot; rel=&quot;noopener&quot;&gt;created a demo&lt;/a&gt; to show you exactly what I mean. Hopefully as you scroll and move your mouse you&#39;ll see the hover effects kicking in, but let&#39;s see what Chrome&#39;s DevTools makes of it:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome&amp;#x27;s DevTools showing expensive frames&quot; decoding=&quot;async&quot; height=&quot;466&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bib9tiR2rEkkpPe66Nhj.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Chrome&#39;s DevTools showing expensive frames&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the image above you can see that DevTools is registering the paint work when I hover on one of the blocks. I&#39;ve gone with some super heavy styles in my demo to make the point, and so I&#39;m pushing up to and occasionally through my frame budget. The last thing I want is to have to do this paint work unnecessarily, and especially so during a scroll when there&#39;s other work to be done!&lt;/p&gt;
&lt;p&gt;So how can we stop this from happening? As it happens the fix is pretty simple to implement. The trick here is to attach a &lt;code&gt;scroll&lt;/code&gt; handler that will disable hover effects and set a timer for enabling them again. That means we are guaranteeing that when you scroll we won&#39;t need to perform any expensive interaction paints. When you&#39;ve stopped for long enough we figure it&#39;s safe to switch them back on again.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; By changing this you’re affecting the user experience of your application, so trade wisely. Only you and your team will be able to make the call as to what is an acceptable level of delay before hover effects are enabled again. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Here&#39;s the code:&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;// Used to track the enabling of hover effects&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; enableTimer &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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/*&lt;br /&gt; * Listen for a scroll and use that to remove&lt;br /&gt; * the possibility of hover effects&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;scroll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&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;  &lt;span class=&quot;token function&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;enableTimer&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 function&quot;&gt;removeHoverClass&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;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// enable after 1 second, choose your own value here!&lt;/span&gt;&lt;br /&gt;  enableTimer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addHoverClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&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;&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 boolean&quot;&gt;false&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Removes the hover class from the body. Hover styles&lt;br /&gt; * are reliant on this class being present&lt;br /&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;removeHoverClass&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;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hover&#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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Adds the hover class to the body. Hover styles&lt;br /&gt; * are reliant on this class being present&lt;br /&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;addHoverClass&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;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hover&#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;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;As you can see we use a class on the body to track whether or not hover effects are &amp;quot;allowed&amp;quot;, and the underlying styles rely on this class to be present:&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;/* Expect the hover class to be on the body&lt;br /&gt; before doing any hover effects */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.hover .block:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&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;And that&#39;s all there is to it!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-unnecessary-paints/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Rendering performance is critical to users enjoying your application, and you should always aim to keep your paint workload under 16ms. To help you do that, you should integrate using DevTools &lt;strong&gt;throughout your development process&lt;/strong&gt; to identify and fix bottlenecks as they arise.&lt;/p&gt;
&lt;p&gt;Inadvertent interactions, particularly on paint-heavy elements, can be very costly and will kill rendering performance. As you&#39;ve seen, we can use a small piece of code to fix that.&lt;/p&gt;
&lt;p&gt;Take a look at your sites and applications, could they do with a little paint protection?&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>High DPI Canvas</title>
    <link href="https://web.dev/canvas-hidipi/"/>
    <updated>2012-08-25T00:00:00Z</updated>
    <id>https://web.dev/canvas-hidipi/</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/canvas-hidipi/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HiDPI screens are lovely, they make everything look smoother and cleaner. But they also present a new set of challenges to developers. In this article we are going to take a look into the unique challenges of drawing images in the canvas in the context of HiDPI screens.&lt;/p&gt;
&lt;h2 id=&quot;the-devicepixelratio-property&quot;&gt;The devicePixelRatio property &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/canvas-hidipi/#the-devicepixelratio-property&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s start at the beginning. Back before we had HiDPI screens a pixel was a pixel (if we ignore zooming and scaling for a bit) and that was it, you didn&#39;t really need to change anything around. If you set something to be 100px wide that was all there was to it. Then the first few HiDPI mobile handsets started popping up with the slightly enigmatic devicePixelRatio property on the window object and available for use in media queries. What this property allowed us to do was understand the ratio of how pixel values (which we call the logical pixel value) in - say - CSS would translate to the &lt;strong&gt;actual&lt;/strong&gt; number of pixels the device would use when it came to rendering. In the case of an iPhone 4S, which has a devicePixelRatio of 2, you will see that a 100px logical value equates to a 200px device value.&lt;/p&gt;
&lt;p&gt;That&#39;s interesting, but what does that mean for us developers? Well in the early days we all started to notice that our images were being upscaled by these devices. We were creating images at the logical pixel width of our elements and, when they were drawn out, they would be upscaled by the devicePixelRatio and they&#39;d be blurry.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;An image being upscaled and blurred due to the devicePixelRatio&quot; decoding=&quot;async&quot; height=&quot;180&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1TY8FIbQ4pLaiSsWlxbm.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
    &lt;figcaption&gt;Figure 1 - An image being upscaled and blurred due to the devicePixelRatio&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The de facto solution to this has been to create images scaled up by the devicePixelRatio and then use CSS to scale it down by the same amount, and the same is true for canvas!&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupCanvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;canvas&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;  &lt;span class=&quot;token comment&quot;&gt;// Get the device pixel ratio, falling back to 1.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dpr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devicePixelRatio &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 comment&quot;&gt;// Get the size of the canvas in CSS pixels.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBoundingClientRect&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;  &lt;span class=&quot;token comment&quot;&gt;// Give the canvas pixel dimensions of their CSS&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// size * the device pixel ratio.&lt;/span&gt;&lt;br /&gt;  canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; dpr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; dpr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;2d&#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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Scale all drawing operations by the dpr, so you&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// don&#39;t have to worry about the difference.&lt;/span&gt;&lt;br /&gt;  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dpr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dpr&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 keyword&quot;&gt;return&lt;/span&gt; ctx&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 comment&quot;&gt;// Now this line will be the same size on the page&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// but will look sharper on high-DPI devices!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupCanvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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;.my-canvas&#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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lineWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;beginPath&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;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&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;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token number&quot;&gt;200&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;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stroke&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Parallaxin&#39;</title>
    <link href="https://web.dev/speed-parallax/"/>
    <updated>2011-12-20T00:00:00Z</updated>
    <id>https://web.dev/speed-parallax/</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-parallax/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Parallax sites have been all the rage recently, just take a look at these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rowtothepole.com/&quot; rel=&quot;noopener&quot;&gt;Old Pulteney Row to the Pole&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.adidas.com/com/apps/snowboarding/&quot; rel=&quot;noopener&quot;&gt;Adidas Snowboarding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.bbc.co.uk/news/entertainment-arts-20026367&quot; rel=&quot;noopener&quot;&gt;BBC News - James Bond: Cars, catchphrases and kisses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re not familiar with them, they’re the sites where the visual structure of the page changes as you scroll. Normally elements within the page scale, rotate or move proportionally to the scroll position on the page.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A demo parallax page&quot; decoding=&quot;async&quot; height=&quot;394&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fCJqpmsQOPiUG5ax4KrH.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Our demo page complete with parallax effect&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Whether or not you like parallaxing sites is one thing, but what you can say pretty confidently is that they’re a black hole of performance. The reason for this is that browsers tend to be optimized for the case where new content appears at the top or bottom of the screen when you scroll (depending on your scroll direction) and, in general terms, the browsers work best when very little changes visually during a scroll. For a parallax site that’s rarely the case since many times large visual elements all over the page change, causing the browser to do a repaint of the whole page.&lt;/p&gt;
&lt;p&gt;It is reasonable to generalize a parallaxing site like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Background elements which, as you scroll up and down, change their position, rotation and scale.&lt;/li&gt;
&lt;li&gt;Page content, such as text or smaller images, which scrolls in the typical top-to-bottom fashion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We previously covered &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/scrolling/&quot; rel=&quot;noopener&quot;&gt;scrolling performance&lt;/a&gt; and the ways in which you can look to improve your app’s responsiveness, and this article builds on that foundation so it may be worth reading that if you’ve not done so already.&lt;/p&gt;
&lt;p&gt;So the question is if you’re building a parallax scrolling site are you locked into expensive repaints or are there alternative approaches you can take to maximise performance? Let’s take a look at our options.&lt;/p&gt;
&lt;h2 id=&quot;option-1-use-dom-elements-and-absolute-positions&quot;&gt;Option 1: Use DOM elements and absolute positions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-parallax/#option-1-use-dom-elements-and-absolute-positions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This appears to be the default approach that most people take. There are a bunch of elements within the page, and whenever a scroll event is fired a bunch of visual updates are done to transform them.&lt;/p&gt;
&lt;p&gt;If you start the DevTools Timeline in frame mode and scroll around you’ll notice that there are expensive full-screen paint operations, and if you scroll a lot you may see several scroll events inside a single frame, each of which is going to trigger layout work.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome DevTools without debounced scroll events.&quot; decoding=&quot;async&quot; height=&quot;478&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tVfxqBWBAaEKFiqvIqfW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;DevTools showing large paints and multiple event-triggered layouts in a single frame.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The important thing to bear in mind is that to hit 60fps (matching the typical monitor refresh rate of 60Hz) we have just over 16ms to get everything done. In this first version we’re performing our visual updates every time we get a scroll event, but as we’ve discussed in previous articles on &lt;a href=&quot;http://www.html5rocks.com/tutorials/speed/animations/&quot; rel=&quot;noopener&quot;&gt;leaner, meaner animations with requestAnimationFrame&lt;/a&gt; and &lt;a href=&quot;http://www.html5rocks.com/tutorials/speed/scrolling/&quot; rel=&quot;noopener&quot;&gt;scrolling performance&lt;/a&gt;, this doesn’t coincide with the browser’s update schedule, and so we either miss frames or do too much work inside each one. That could easily result in a janky and unnatural feel to your site, which leads to disappointed users and unhappy kittens.&lt;/p&gt;
&lt;p&gt;Let’s move the update code out from the scroll event to a &lt;code&gt;requestAnimationFrame&lt;/code&gt; callback and simply capture the scroll value in the scroll event’s callback.&lt;/p&gt;
&lt;p&gt;If you repeat the scrolling test you’ll possibly notice a slight improvement, although not much. The reason is that the layout operation that we trigger by scrolling isn’t all that expensive, but in other use-cases it really could be. Now at least we are only performing &lt;strong&gt;one layout&lt;/strong&gt; operation in each frame.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome DevTools with debounced scroll events.&quot; decoding=&quot;async&quot; height=&quot;489&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZJVP13CyLgILY3GLIwk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;DevTools showing large paints and multiple event-triggered layouts in a single frame.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We can now handle one or one hundred scroll events per frame but crucially we only store the most recent value for use whenever the &lt;code&gt;requestAnimationFrame&lt;/code&gt; callback runs and performs our visual updates. The point is you’ve moved from attempting to force visual updates every time you receive a scroll event to requesting that the browser give you an appropriate window in which to do them. Aren’t you sweet?&lt;/p&gt;
&lt;p&gt;The main problem with this approach, &lt;code&gt;requestAnimationFrame&lt;/code&gt; or not, is that we essentially have one layer for the whole page, and by moving these visual elements around we require large (and expensive) repaints. Typically speaking the painting is a blocking operation (although that is &lt;a href=&quot;http://www.chromium.org/developers/design-documents/impl-side-painting&quot; rel=&quot;noopener&quot;&gt;changing&lt;/a&gt;), meaning that the browser can’t do any other work and we often run way over our frame’s budget of 16ms and things remain janky.&lt;/p&gt;
&lt;h2 id=&quot;option-2-use-dom-elements-and-3d-transforms&quot;&gt;Option 2: Use DOM elements and 3D transforms &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-parallax/#option-2-use-dom-elements-and-3d-transforms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instead of using absolute positions another approach we can take it is to apply 3D transforms to the elements. In this situation we see that the elements with the 3D transforms applied are given a new layer per element and, in WebKit browsers, it often also causes a switch over to the hardware compositor. In Option 1, by contrast, we had one large layer for the page that needed to be repainted when anything changed and all the painting and compositing was handled by the CPU.&lt;/p&gt;
&lt;p&gt;That means with &lt;strong&gt;this&lt;/strong&gt; option, things are different: we potentially have one layer for any element to which we apply a 3D transform. If all we do from this point is more transformations on the elements we won’t need to repaint the layer, and the GPU can deal with moving the elements around and compositing the final page together.&lt;/p&gt;
&lt;p&gt;Many times people just use the &lt;code&gt;-webkit-transform: translateZ(0);&lt;/code&gt; hack and see magical performance improvements, and while this works today there are problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It’s not cross-browser compatible.&lt;/li&gt;
&lt;li&gt;It forces the browser’s hand by creating a new layer for every transformed element. Lots of layers can bring other performance bottlenecks, so use sparingly!&lt;/li&gt;
&lt;li&gt;It’s been &lt;a href=&quot;http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html#//apple_ref/doc/uid/TP40012166-CH1-SW19&quot; rel=&quot;noopener&quot;&gt;disabled for some WebKit ports&lt;/a&gt; (fourth bullet from the bottom!).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you go down the 3D translation route be cautious, it’s a temporary solution to your problem! Ideally speaking we would see similar rendering characteristics from 2D transforms as we do with 3D. Browsers are progressing at a phenomenal rate, so hopefully before that&#39;s what we&#39;ll see.&lt;/p&gt;
&lt;p&gt;Finally, you should aim to avoid paints wherever you can and simply move existing elements around the page. By way of example, it is a typical approach in parallax sites to use fixed height divs and change their background position to provide the effect. Unfortunately that means that the element needs to be repainted on every pass, which can cost you in terms of performance. Instead you should, if you can, create the element (wrap it inside an div with &lt;code&gt;overflow: hidden&lt;/code&gt; if necessary) and simply translate it instead.&lt;/p&gt;
&lt;h2 id=&quot;option-3-use-a-fixed-position-canvas-or-webgl&quot;&gt;Option 3: Use a fixed position canvas or WebGL &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-parallax/#option-3-use-a-fixed-position-canvas-or-webgl&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The final option we’re going to consider is to use a fixed position canvas at the back of the page into which we will draw our transformed images. At first glance that might not seem like the most performant solution, but there are actually a few benefits to this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We no longer require as much compositor work due to only having one element, the canvas.&lt;/li&gt;
&lt;li&gt;We’re effectively dealing with a single &lt;strong&gt;hardware accelerated&lt;/strong&gt; bitmap.&lt;/li&gt;
&lt;li&gt;The Canvas2D API is a great fit for the kind of transformations we’re looking to perform, meaning development and maintenance is more manageable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using a canvas element gives us a new layer, but it’s just &lt;strong&gt;one&lt;/strong&gt; layer, whereas in Option 2 we were actually given a new layer for &lt;strong&gt;every&lt;/strong&gt; element with a 3D transform applied, so we have an increased workload compositing all those layers together. This is also the most compatible solution today in light of the differing cross-browser implementations of transforms.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Updates and draws in the underlying visual elements to the canvas.&lt;br /&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;updateElements&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;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; relativeY &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastScrollY &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; h&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Fill the canvas up&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fillStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#1e2124&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fillRect&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height&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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Draw the background&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bg&lt;span class=&quot;token punctuation&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;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Draw each of the blobs in turn&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;484&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;254&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 number&quot;&gt;4400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;84&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;954&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 number&quot;&gt;5400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;584&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1054&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 number&quot;&gt;3900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;44&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1400&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 number&quot;&gt;6900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob5&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 number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1730&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 number&quot;&gt;5900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;325&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2860&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 number&quot;&gt;7900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob7&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;725&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2550&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 number&quot;&gt;4900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;570&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2300&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 number&quot;&gt;3700&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3700&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 number&quot;&gt;9000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relativeY&lt;span class=&quot;token punctuation&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;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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Allow another rAF call to be scheduled&lt;/span&gt;&lt;br /&gt;  ticking &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Calculates a relative disposition given the page&#39;s scroll&lt;br /&gt; * range normalized from 0 to 1&lt;br /&gt; * @param {number} base The starting value.&lt;br /&gt; * @param {number} range The amount of pixels it can move.&lt;br /&gt; * @param {number} relY The normalized scroll value.&lt;br /&gt; * @param {number} offset A base normalized value from which to start the scroll behavior.&lt;br /&gt; * @returns {number} The updated position value.&lt;br /&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;pos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;base&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relY&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offset&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;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relY &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; range&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 comment&quot;&gt;/**&lt;br /&gt; * Clamps a number to a range.&lt;br /&gt; * @param {number} min The minimum value.&lt;br /&gt; * @param {number} max The maximum value.&lt;br /&gt; * @param {number} value The value to limit.&lt;br /&gt; * @returns {number} The clamped value.&lt;br /&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;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;min&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; max&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&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;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;min&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This approach really works where you’re dealing with large images (or other elements that can be easily written into a canvas), and certainly dealing with large blocks of text would be more challenging, but depending on your site it may prove to be the most appropriate solution. If you &lt;strong&gt;do&lt;/strong&gt; have to deal with text in the canvas you would have to use the &lt;code&gt;fillText&lt;/code&gt; API method, but it’s at the cost of accessibility (you just rasterized the text into a bitmap!) and you will now have to deal with line wrapping and a whole heap of other issues. If you can avoid it, you really should, and you&#39;d likely be better served using the transforms approach above.&lt;/p&gt;
&lt;p&gt;Seeing as we’re taking this as far as possible, there’s no reason to presume that the parallax work should be done inside a canvas element. If the browser supports it we could use WebGL. The key here is that WebGL has the most direct route of all APIs to the graphics card and, as such, is your most likely candidate for achieving 60fps, especially if the site’s effects are complex.&lt;/p&gt;
&lt;p&gt;Your immediate reaction might be that WebGL is overkill, or that it isn’t ubiquitous in terms of support, but if you use something like &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;Three.js&lt;/a&gt; then you can always fall back to using a canvas element and your code is abstracted in a consistent and friendly manner. All we need to do is use &lt;a href=&quot;http://modernizr.com/&quot; rel=&quot;noopener&quot;&gt;Modernizr&lt;/a&gt; to check for the appropriate API support:&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;// check for WebGL support, otherwise switch to canvas&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webgl&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;  renderer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebGLRenderer&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canvas&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;  renderer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CanvasRenderer&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As a final thought on this approach, if you’re not a big fan of adding extra elements to the page you can always &lt;a href=&quot;http://updates.html5rocks.com/2012/12/Canvas-driven-background-images&quot; rel=&quot;noopener&quot;&gt;use a canvas as a background element&lt;/a&gt; in both Firefox and WebKit-based browsers. That’s not ubiquitous, obviously, so as usual you should treat it with caution.&lt;/p&gt;
&lt;h2 id=&quot;the-choice-is-yours&quot;&gt;The choice is yours &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-parallax/#the-choice-is-yours&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The main reason developers default to absolutely positioned elements rather than any of the other options may simply be the ubiquity of support. This is, to some degree, illusory, since the older browsers that are being targeted are likely to provide an extremely poor rendering experience. Even in today’s modern browsers using absolutely positioned elements doesn’t necessarily result in good performance!&lt;/p&gt;
&lt;p&gt;Transforms, certainly the 3D kind, offer you the ability to work directly with DOM elements and achieve a solid frame rate. The key to success here is to avoid painting wherever you can and simply try and move elements around. Do bear in mind that the way that WebKit browsers create layers doesn&#39;t necessarily correlate to other browser engines, so be sure to test it before committing yourself to that solution.&lt;/p&gt;
&lt;p&gt;If you&#39;re aiming just for the top tier of browsers, and are able to render the site using canvases, that might the best option for you. Certainly if you were to use &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;Three.js&lt;/a&gt; you should be able to swap and change between renderers very easily depending on the support you require.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-parallax/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve assessed a few approaches to dealing with parallax sites, from absolutely positioned elements to using a fixed position canvas. The implementation you take will, of course, depends on what you’re trying to achieve and the specific design you’re working with, but it’s always good to know you have options.&lt;/p&gt;
&lt;p&gt;And as always, whichever approach you try: &lt;strong&gt;don’t guess it, test it&lt;/strong&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Avoiding Unnecessary Paints - Animated GIF Edition</title>
    <link href="https://web.dev/speed-animated-gifs/"/>
    <updated>2011-10-21T00:00:00Z</updated>
    <id>https://web.dev/speed-animated-gifs/</id>
    <content type="html" mode="escaped">&lt;p&gt;Avoiding paints is critical to achieving a silky smooth frame rate, especially on mobile. Sometimes, however, paints crop up in the most unusual of places. This article looks at why animated GIFs can cause unnecessary paints to occur, and the remarkably simple fix you can apply.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Last time we looked at ways to &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/unnecessary-paints/&quot;&gt;avoid hover effects during scrolls&lt;/a&gt;. If you&#39;ve not seen that, check it out now! &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;layers-of-loveliness&quot;&gt;Layers of loveliness &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#layers-of-loveliness&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As you probably know, modern browsers may paint groups of DOM elements into separate &amp;quot;images&amp;quot;, called layers. Sometimes there&#39;s one layer for the entire page, sometimes there are hundreds or  in rare cases - thousands!&lt;/p&gt;
&lt;p&gt;When DOM elements are grouped together into a layer and one of the elements changes visually, we end up having to paint not just the changed element, but &lt;strong&gt;all the other elements in the layer that overlap the changed element as well&lt;/strong&gt;. Painting one thing on top of another results in the overwritten pixels being effectively &amp;quot;lost&amp;quot; forever; if you want the original pixels back you need to repaint them.&lt;/p&gt;
&lt;p&gt;Sometimes, therefore, we want to isolate one element from the others so that when it gets painted we won&#39;t need to repaint the other elements that &lt;strong&gt;haven&#39;t&lt;/strong&gt; changed. For example, when you combine a fixed page header with scrollable content, you have to repaint the header each time the content scrolls, as well as the newly visible content. By placing the header in a separate layer, the browser can optimize scrolling. When you scroll, the browser can move the layers around - probably with the help of the GPU - and avoid repainting either layer.&lt;/p&gt;
&lt;p&gt;Each additional layer increases memory consumption and adds performance overhead, so the goal is to group the page into as few layers as possible while maintaining good performance.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you want a more detailed breakdown of how layers are created and used definitely check out &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/speed/layers/&quot;&gt;Tom Wiltzius&#39;s intro to the subject&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-does-this-all-have-to-do-with-animated-gifs&quot;&gt;What does this all have to do with animated GIFs? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#what-does-this-all-have-to-do-with-animated-gifs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well let&#39;s have a look at this picture:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A web app broken down into four layers.&quot; decoding=&quot;async&quot; height=&quot;532&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Er8KGGjUYDusN9vNDZfH.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Figure 1: A web app broken down into four layers.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is a potential layer setup for a simple app. There are four layers here: three of them (layers 2 to 4) are interface elements; the back layer is a loader, which happens to be an animated GIF. In the normal flow you show the loader (layer 1) while your app loads, then as everything finishes you would show the other layers. But here&#39;s the key: &lt;strong&gt;you need to hide the animated GIF.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;but-why-do-i-need-to-hide-it&quot;&gt;But why do I need to hide it?! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#but-why-do-i-need-to-hide-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Good question. In a perfect world the browser would simply check the GIF’s visibility for you and avoid painting automatically. Unfortunately, checking whether the animated GIF is obscured or visible on the screen is typically &lt;strong&gt;more&lt;/strong&gt; expensive than simply painting it, so it gets painted.&lt;/p&gt;
&lt;p&gt;In the best case the GIF is in its own layer and the browser only has to paint and upload it to the GPU. But in the worst case all your elements might be grouped into a single layer and the browser has to repaint &lt;strong&gt;every single element&lt;/strong&gt;. And, when it’s done, it still needs to upload everything to the GPU. All of this is work occurs for every GIF frame, despite the fact that the user can’t even see the GIF!&lt;/p&gt;
&lt;p&gt;On desktops you can probably get away with this kind of painting behavior because the CPUs and GPUs are more powerful, and there&#39;s plenty of bandwidth for transferring data between the two. On mobile, however, painting is extremely expensive so you must take great care.&lt;/p&gt;
&lt;h2 id=&quot;which-browsers-does-this-affect&quot;&gt;Which browsers does this affect? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#which-browsers-does-this-affect&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As is often the way, behaviors differ between browsers. Today Chrome, Safari and Opera all repaint, even if the GIF is obscured. Firefox, on the other hand, figures out that the GIF is obscured and doesn’t need to be repainted. Internet Explorer remains something of a black box, and even in IE11 - since the F12 tools are still being developed - there is no indication as to whether or not any repainting is taking place.&lt;/p&gt;
&lt;h2 id=&quot;how-can-i-tell-if-i-have-this-problem&quot;&gt;How can I tell if I have this problem? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#how-can-i-tell-if-i-have-this-problem&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The easiest way is to use &amp;quot;Show paint rectangles&amp;quot; in Chrome DevTools. Load DevTools and press the cog in the lower right corner (&lt;img alt=&quot;Cog icon&quot; decoding=&quot;async&quot; height=&quot;21&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 22px) 22px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=22 22w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=25 25w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=29 29w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=33 33w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=37 37w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=42 42w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Q1K5jZRASU3Q7aTfR4p9.png?auto=format&amp;w=44 44w&quot; width=&quot;22&quot; /&gt;) and choose &lt;strong&gt;Show paint rectangles&lt;/strong&gt; under the &lt;strong&gt;Rendering&lt;/strong&gt; section.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Enabling Show paint rectangles inside Chrome DevTools&quot; decoding=&quot;async&quot; height=&quot;59&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 366px) 366px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fceVMJ0tb0ZewvAcF5PP.png?auto=format&amp;w=732 732w&quot; width=&quot;366&quot; /&gt;
  &lt;figcaption&gt;Figure 2: Enabling Show paint rectangles inside Chrome DevTools.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now all you need to do is look for a red rectangle like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools’ Show Paint Rectangles hints at animated GIF problems with a red rectangle.&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vysbt6HCy7AFVxbZg72s.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;Figure 3: DevTools’ Show Paint Rectangles hints at animated GIF problems with a red rectangle.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The little red box on the screen shows that Chrome is repainting something. You know that there’s a loader GIF hidden behind the other elements, so when you see a red box like this you need to hide the visible elements and check if you have left the animated GIF spinning away. If you have then you need to pop some CSS or JavaScript in place to apply &lt;code&gt;display: none&lt;/code&gt; or &lt;code&gt;visibility: hidden&lt;/code&gt; to it or its parent element. Of course if it&#39;s just a background image then you should make sure to remove it.&lt;/p&gt;
&lt;p&gt;If you want to see an example of this behavior in a live site, check out &lt;a href=&quot;http://allegro.pl/listing/listing.php?string=phone&quot; rel=&quot;noopener&quot;&gt;Allegro&lt;/a&gt;, where each product’s image has a loader GIF that is obscured rather than explicitly hidden.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-animated-gifs/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Achieving 60fps means doing &lt;strong&gt;only&lt;/strong&gt; what&#39;s needed to render the page and no more. Removing excess paints is a critical step in achieving this goal. Animated GIFs that are left running can trigger unnecessary paints, something which you can find and debug easily with DevTools&#39; Show paint rectangles tool.&lt;/p&gt;
&lt;p&gt;Now, you didn&#39;t leave that animated kitten loader GIF running forever, did you?&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>Getting started with Three.js</title>
    <link href="https://web.dev/three-intro/"/>
    <updated>2011-06-02T00:00:00Z</updated>
    <id>https://web.dev/three-intro/</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/three-intro/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have used &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;Three.js&lt;/a&gt; for some of &lt;a href=&quot;http://aerotwist.com/lab/&quot; rel=&quot;noopener&quot;&gt;my experiments&lt;/a&gt;, and it does a really great job of
abstracting away the headaches of getting going with 3D in the browser.
With it you can create cameras, objects, lights, materials and more, and
you have a choice of renderer, which means you can decide if you want
your scene to be drawn using HTML 5&#39;s canvas, WebGL or SVG. And since it&#39;s
open source you could even get involved with the project. But right now
I&#39;ll focus on what I&#39;ve learned by playing with it as an engine, and talk
you through some of the basics.&lt;/p&gt;
&lt;p&gt;For all the awesomeness of Three.js, there can be times where you
might struggle. Typically you will need to spend quite a large amount of time with
the examples, reverse engineering and (in my case certainly) hunting down
specific functionality and occasionally &lt;a href=&quot;https://github.com/mrdoob/three.js/issues&quot; rel=&quot;noopener&quot;&gt;asking questions via GitHub&lt;/a&gt;. If you
have to as questions, by the way, I&#39;ve found &lt;a href=&quot;http://mrdoob.com/&quot; rel=&quot;noopener&quot;&gt;Mr. doob&lt;/a&gt; and &lt;a href=&quot;http://alteredqualia.com/&quot; rel=&quot;noopener&quot;&gt;AlteredQualia&lt;/a&gt; are
extremely helpful!&lt;/p&gt;
&lt;h2 id=&quot;1-the-basics&quot;&gt;1. The basics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#1-the-basics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I will assume that you have at
least a passing knowledge of 3D, and reasonable proficiency with
JavaScript. If you don&#39;t it may be worth learning a bit before you try
and play with this stuff, as it can get a little confusing.&lt;/p&gt;
&lt;p&gt;In our 3D world we will have some of the following, which I will
guide you through the process of creating:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A scene&lt;/li&gt;
&lt;li&gt;A renderer&lt;/li&gt;
&lt;li&gt;A camera&lt;/li&gt;
&lt;li&gt;An object or two (with materials)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can, of course, do some cool things, and my hope is that you will
go on to do that and start to experiment with 3D in your browser.&lt;/p&gt;
&lt;h2 id=&quot;2-support&quot;&gt;2. Support &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#2-support&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just a quick note on support in the browsers. Google&#39;s Chrome browser is, in my experience,
the best browser to work with in terms of which renderers are supported, and
the speed of the underlying JavaScript engine. Chrome supports
Canvas, WebGL and SVG and it&#39;s blazingly fast. Firefox is a close second, with the
advent of version 4. Its JavaScript engine does seem to be a touch slower than
Chrome&#39;s, but again its support for the render technologies is great. Opera and Safari
are in the process of adding WebGL support, but their current versions only support canvas.
Internet Explorer (version 9+) support canvas rendering only, and I&#39;ve not heard
anything of Microsoft planning to add WebGL capabilities.&lt;/p&gt;
&lt;h2 id=&quot;3-set-the-scene&quot;&gt;3. Set the Scene &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#3-set-the-scene&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ll assume you&#39;ve chosen a browser that supports all the rendering technologies,
and that you want to render with canvas or WebGL, since they&#39;re the more standard
choices. Canvas is more widely supported than WebGL, but it&#39;s worth noting that
WebGL runs on your graphics card&#39;s GPU, which means that your CPU can concentrate
on other non-rendering tasks like any physics or user interaction you&#39;re trying to
do.&lt;/p&gt;
&lt;p&gt;Irrespective of your chosen renderer you should bear in mind that the JavaScript
will need to optimised for performance. 3D isn&#39;t a lightweight task for a browser
(and it&#39;s awesome that it&#39;s even possible), so be careful to understand where any
bottlenecks are in your code, and remove them if you can!&lt;/p&gt;
&lt;p&gt;So with that said, and on the assumption &lt;a href=&quot;https://github.com/mrdoob/three.js/archives/master&quot; rel=&quot;noopener&quot;&gt;you have downloaded&lt;/a&gt; and included three.js
in your HTML file, how do you go about setting up a scene? Like this:&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;// set the scene size&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WIDTH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token constant&quot;&gt;HEIGHT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// set some camera attributes&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VIEW_ANGLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token constant&quot;&gt;ASPECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WIDTH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token constant&quot;&gt;NEAR&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token constant&quot;&gt;FAR&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// get the DOM element to attach to&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// - assume we&#39;ve got jQuery to hand&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; $container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#container&#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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// create a WebGL renderer, camera&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// and a scene&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; renderer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebGLRenderer&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;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; camera &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PerspectiveCamera&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token constant&quot;&gt;VIEW_ANGLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token constant&quot;&gt;ASPECT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token constant&quot;&gt;NEAR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token constant&quot;&gt;FAR&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;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; scene &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Scene&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the camera starts at 0,0,0 so pull it back&lt;/span&gt;&lt;br /&gt;camera&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// start the renderer&lt;/span&gt;&lt;br /&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;HEIGHT&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// attach the render-supplied DOM element&lt;/span&gt;&lt;br /&gt;$container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domElement&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;Not too tricky, really!&lt;/p&gt;
&lt;h2 id=&quot;4-making-a-mesh&quot;&gt;4. Making a Mesh &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#4-making-a-mesh&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So we have a scene, a camera and a renderer (I opted for a WebGL one in my sample
code) but we have nothing to actually draw. Three.js actually comes with support
for loading a few different standard file types, which is great if you are outputting
models from Blender, Maya, Cinema4D or anything else. To keep things simple (this
is about getting started after all!) I&#39;ll talk about primitives. Primitives are
geometric meshes, relatively basic ones like Spheres, Planes, Cubes and Cylinders.
Three.js lets you create these types of primitives easily:&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;// set up the sphere vars&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; radius &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; segments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// create a new mesh with sphere geometry -&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// we will cover the sphereMaterial next!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sphere &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Mesh&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SphereGeometry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;radius&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;segments&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;rings&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;br /&gt;sphereMaterial&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// add the sphere to the scene&lt;/span&gt;&lt;br /&gt;scene&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sphere&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;All good, but what about the material for the sphere? In the code we&#39;ve used a
variable &lt;strong&gt;sphereMaterial&lt;/strong&gt; but we&#39;ve not defined it yet. First we need
to talk about materials in a bit more detail.&lt;/p&gt;
&lt;h2 id=&quot;5-materials&quot;&gt;5. Materials &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#5-materials&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Without doubt this is one of the most useful parts of Three.js. It provides
for you a number of common (and very handy) materials to apply to your meshes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&#39;Basic&#39; - which just means that it renders &#39;unlit&#39;&lt;/li&gt;
&lt;li&gt;Lambert&lt;/li&gt;
&lt;li&gt;Phong&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are more, but again in the interests of simplicity I&#39;ll let you discover
those for yourself. In the case of WebGL particularly these materials can be a
life-saver. Why? Well because in WebGL you have to write shaders for everything
being rendered. Shaders are a huge topic in themselves, but in short they are
written in GLSL (OpenGL Shader Language), which tells the GPU
how something should look. This means you need to mimic the maths of lighting,
reflection and so on. It can get very complicated very quickly. Thanks to Three.js you don&#39;t have to do this
if you don&#39;t want to because it abstracts that
away for you. If you want to write shaders, however, you can do that too with
a MeshShaderMaterial, so it&#39;s a flexible setup.&lt;/p&gt;
&lt;p&gt;For now, however, let&#39;s apply a lambert material to the sphere:&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;// create the sphere&#39;s material&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sphereMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MeshLambertMaterial&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 comment&quot;&gt;// a gorgeous red.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xCC0000&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It&#39;s worth pointing out as well that there are other properties you can specify
when you create a material besides the colour, such as smoothing or environment maps.
You should &lt;a href=&quot;https://github.com/mrdoob/three.js/wiki/API-Reference&quot; rel=&quot;noopener&quot;&gt;check out the Wiki page&lt;/a&gt; for the various properties you can set on
the materials and, in fact, any object that the engine provides for you. Also &lt;a href=&quot;http://threejs.org/&quot; rel=&quot;noopener&quot;&gt;threejs.org&lt;/a&gt; recently sprung up, which offers a more attractive view of the API.&lt;/p&gt;
&lt;h2 id=&quot;6-lights&quot;&gt;6. Lights! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#6-lights&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you were to render the scene right now you&#39;d see a red circle. Even
though we have a Lambert material applied there&#39;s no light in the scene so
by default Three.js will revert to a full ambient light, which is the same
as flat colouring. Let&#39;s fix that with a simple point of light:&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;// create a point light&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; pointLight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointLight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFFFFFF&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// set its position&lt;/span&gt;&lt;br /&gt;pointLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;pointLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;pointLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;130&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// add to the scene&lt;/span&gt;&lt;br /&gt;scene&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pointLight&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;h2 id=&quot;7-render&quot;&gt;7. Render &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#7-render&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We now actually have everything set up to render, remarkably. But we actually
need to go ahead and do just that:&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;// draw!&lt;/span&gt;&lt;br /&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scene&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; camera&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;You&#39;re probably going to want to render more than once, though, so if you&#39;re
going to do a loop you should really use requestAnimationFrame; it&#39;s by
far the smartest way of handling animation in the browser. It&#39;s not fully
supported as yet, so I&#39;d totally recommend that you take a look at &lt;a href=&quot;http://paulirish.com/2011/requestanimationframe-for-smart-animating/&quot; rel=&quot;noopener&quot;&gt;Paul Irish&#39;s shim&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;8-common-object-properties&quot;&gt;8. Common Object Properties &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#8-common-object-properties&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you take time to look through the code for Three.js you&#39;ll see a lot
of objects &#39;inherit&#39; from Object3D. This is a base object which contains some very useful
properties, such as the &lt;strong&gt;position&lt;/strong&gt;, &lt;strong&gt;rotation&lt;/strong&gt; and &lt;strong&gt;scale&lt;/strong&gt; information. In particular
our Sphere is a Mesh which inherits from Object3D, to which it adds its own properties:
&lt;strong&gt;geometry&lt;/strong&gt; and &lt;strong&gt;materials&lt;/strong&gt;. Why do I mention these? Well it&#39;s unlikely you&#39;re going
to want to just have a sphere on your screen that does nothing, and these
properties are worth investigating as they allow you to manipulate the underlying
details of the meshes and materials on the fly.&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;// sphere geometry&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// which contains the vertices and faces&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertices &lt;span class=&quot;token comment&quot;&gt;// an array&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;faces &lt;span class=&quot;token comment&quot;&gt;// also an array&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// its position&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token comment&quot;&gt;// has x, y and z properties&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rotation &lt;span class=&quot;token comment&quot;&gt;// same&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scale &lt;span class=&quot;token comment&quot;&gt;// ... same&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;9-dirty-little-secrets&quot;&gt;9. Dirty Little Secrets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#9-dirty-little-secrets&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I just wanted to quickly point out a quick gotcha for Three.js, which is that
if you modify, for example, the vertices of a mesh, you will notice in your render
loop that nothing changes. Why? Well because Three.js (as far as I can tell) caches
the data for a mesh as something of
an optimisation. What you actually need to do is to flag to Three.js that something
has changed so it can recalculate whatever it needs to. You do this with the following:&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;// changes to the vertices&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__dirtyVertices &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// changes to the normals&lt;/span&gt;&lt;br /&gt;sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__dirtyNormals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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;Again there are more, but those two I&#39;ve found are the most useful. You should obviously
only flag the things that have changed to avoid unnecessary calculations.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/three-intro/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well I hope you&#39;ve found this brief introduction to Three.js helpful. There&#39;s
nothing quite like actually getting your hands dirty and trying something, and
I can&#39;t recommend it highly enough. 3D running natively in the browser
is a lot of fun, and using an engine like Three.js takes away a lot of the
headaches for you and lets you get to making some seriously cool stuff.
To help you out a bit I&#39;ve &lt;a href=&quot;http://aerotwist.com/lab/getting-started-with-three-js/sample.zip&quot; rel=&quot;noopener&quot;&gt;wrapped up the source code&lt;/a&gt; in this lab article, so you can
use that as a reference.
If you&#39;ve enjoyed this let me know via &lt;a href=&quot;http://twitter.com/aerotwist&quot; rel=&quot;noopener&quot;&gt;Twitter&lt;/a&gt;, it&#39;s always good
to say hello!&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
  
  <entry>
    <title>An introduction to shaders</title>
    <link href="https://web.dev/webgl-shaders/"/>
    <updated>2011-06-02T00:00:00Z</updated>
    <id>https://web.dev/webgl-shaders/</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/webgl-shaders/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve previously given you &lt;a href=&quot;https://www.html5rocks.com/tutorials/three/intro/&quot; rel=&quot;noopener&quot;&gt;an introduction to Three.js&lt;/a&gt;. If you&#39;ve
not read that you might want to as it&#39;s the foundation on which I will
be building during this article.&lt;/p&gt;
&lt;p&gt;What I want to do is discuss shaders. WebGL is brilliant, and as I&#39;ve
said before &lt;a href=&quot;https://github.com/mrdoob/three.js/&quot; rel=&quot;noopener&quot;&gt;Three.js&lt;/a&gt; (and other libraries) do a fantastic job of abstracting
away the difficulties for you. But there will be times you want to achieve
a specific effect, or you will want to dig a little deeper into how that
amazing stuff appeared on your screen, and shaders will almost certainly be
a part of that equation. Also if you&#39;re like me you may well want to go from
the basic stuff in the last tutorial to something a little more tricky. I&#39;ll
work on the basis that you&#39;re using Three.js, since it does a lot of the donkey
work for us in terms of getting the shader going.
I&#39;ll say up front as well that at the start I will be explaining
the context for shaders, and that the latter part of this tutorial is where
we will get into slightly more advanced territory. The reason for this is that
shaders are unusual at first sight and take a bit of explaining.&lt;/p&gt;
&lt;h2 id=&quot;1-our-two-shaders&quot;&gt;1. Our Two Shaders &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#1-our-two-shaders&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebGL does not offer the use of the Fixed
Pipeline, which is a shorthand way of saying that it doesn&#39;t give you any means
of rendering your stuff out of the box. What it &lt;strong&gt;does&lt;/strong&gt; offer, however, is
the Programmable Pipeline, which is more powerful but also more
difficult to understand and use. In short the Programmable Pipeline means as the
programmer you take responsibility for getting the vertices and so forth rendered
to the screen. Shaders are a part of this pipeline, and there are two types
of them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Vertex shaders&lt;/li&gt;
&lt;li&gt;Fragment shaders&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both of which, I&#39;m sure you&#39;ll agree, mean absolutely nothing by themselves. What you
should know about them is that they both run entirely on your graphics card&#39;s GPU. This means
that we want to offload all that we can to them, leaving our CPU to do other work.
A modern GPU is heavily optimised for the functions that shaders require so it&#39;s great
to be able to use it.&lt;/p&gt;
&lt;h2 id=&quot;2-vertex-shaders&quot;&gt;2. Vertex Shaders &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#2-vertex-shaders&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a standard primitive shape, like a sphere. It&#39;s made up of vertices, right?
A vertex shader is given every single one of these vertices in turn and can mess around with them.
It&#39;s up to the vertex shader
what it actually does with each one, but it has one responsibility: it must at some point set
something called &lt;strong&gt;gl_Position&lt;/strong&gt;, a 4D float vector, which is the final position of the
vertex on screen. In and of itself that&#39;s quite an interesting process, because we&#39;re
actually talking about getting a 3D position (a vertex with x,y,z) onto, or projected,
to a 2D screen. Thankfully for us
if we&#39;re using something like Three.js we will have a shorthand way of setting the
&lt;strong&gt;gl_Position&lt;/strong&gt; without things getting too heavy.&lt;/p&gt;
&lt;h2 id=&quot;3-fragment-shaders&quot;&gt;3. Fragment Shaders &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#3-fragment-shaders&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So we have our object with its vertices, and we&#39;ve projected them to the 2D screen,
but what about the colours we use? What about texturing and lighting? That&#39;s exactly
what the fragment shader is there for.
Very much like the vertex shader, the fragment shader also only has one must-do
job: it must set or discard the &lt;strong&gt;gl_FragColor&lt;/strong&gt; variable, another 4D float vector, which the final colour of our fragment.
But what is a fragment? Think of three vertices which make a triangle. Each pixel within that triangle needs to be
drawn out. A fragment is the data provided by those three vertices for the purpose of drawing each pixel in that triangle.
Because of this the fragments receive interpolated values from their constituent vertices. If one vertex is coloured red, and
its neighbour is blue we would see the colour values interpolate from red, through purple, to blue.&lt;/p&gt;
&lt;h2 id=&quot;4-shader-variables&quot;&gt;4. Shader Variables &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#4-shader-variables&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When talking about variables there are three declarations you can make: &lt;strong&gt;Uniforms&lt;/strong&gt;,
&lt;strong&gt;Attributes&lt;/strong&gt; and &lt;strong&gt;Varyings&lt;/strong&gt;. When I first heard of those three I was very confused since they don&#39;t match anything else
I&#39;d ever worked with. But here&#39;s how you can think of them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Uniforms&lt;/strong&gt; are sent to &lt;strong&gt;both&lt;/strong&gt;
vertex shaders and fragment shaders and contain values that stay the same across the
entire frame being rendered. A good example of this might be a light&#39;s position.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attributes&lt;/strong&gt; are values that are applied to individual vertices. Attributes
are &lt;strong&gt;only&lt;/strong&gt; available to the vertex shader. This could be something like each vertex
having a distinct colour. Attributes have a one-to-one relationship with vertices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Varying&lt;/strong&gt;s are variables declared in
the vertex shader that we want to share with the fragment shader. To do this we make
sure we declare a varying variable of the same type and name in both the vertex shader
and the fragment shader. A classic use of this would be a vertex&#39;s normal since this can
be used in the lighting calculations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Later on we&#39;ll use all three types so you can
get a feel for how they are applied for real.&lt;/p&gt;
&lt;p&gt;Now we&#39;ve talked about vertex shaders and fragment shaders and the types
of variables they deal with, it&#39;s now worth looking at the simplest shaders
we can create.&lt;/p&gt;
&lt;h2 id=&quot;5-bonjourno-world&quot;&gt;5. Bonjourno World &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#5-bonjourno-world&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here, then, is the Hello World of vertex shaders:&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;/**&lt;br /&gt;* Multiply each vertex by the model-view matrix&lt;br /&gt;* and the projection matrix (both provided by&lt;br /&gt;* Three.js) to get a final vertex position&lt;br /&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;gl_Position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; projectionMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                modelViewMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;   &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;and here&#39;s the same for the fragment shader:&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;/**&lt;br /&gt;* Set the colour to a lovely pink.&lt;br /&gt;* Note that the color is a 4D Float&lt;br /&gt;* Vector, R,G,B and A and each part&lt;br /&gt;* runs from 0.0 to 1.0&lt;br /&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;gl_FragColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Not too complicated though, right?&lt;/p&gt;
&lt;p&gt;In the vertex shader we are sent a couple of uniforms by Three.js. These two uniforms are
4D matrices, called the Model-View Matrix and the Projection Matrix. You
don&#39;t desperately need to know exactly how these work, although it&#39;s always
best to understand how things do what they do if you can. The short version is
that they are how the 3D position of the vertex is actually projected to
the final 2D position on the screen.&lt;/p&gt;
&lt;p&gt;I&#39;ve actually left them out of the snippet above because
Three.js adds them to the top of your shader code itself so you don&#39;t need
to worry about doing it. Truth be told it actually adds a lot more than
that, such as light data, vertex colours and vertex normals. If you were doing
this without Three.js you would have to create and set all those uniforms
and attributes yourself. True story.&lt;/p&gt;
&lt;h2 id=&quot;6-using-a-meshshadermaterial&quot;&gt;6. Using a MeshShaderMaterial &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#6-using-a-meshshadermaterial&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OK, so we have a shader set up, but how do we use it with Three.js? It
turns out that it&#39;s terribly easy. It&#39;s rather like this:&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;/**&lt;br /&gt;* Assume we have jQuery to hand and pull out&lt;br /&gt;* from the DOM the two snippets of text for&lt;br /&gt;* each of our shaders&lt;br /&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MeshShaderMaterial&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;&lt;span class=&quot;token literal-property property&quot;&gt;vertexShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;vertexshader&#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&quot;&gt;text&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;&lt;span class=&quot;token literal-property property&quot;&gt;fragmentShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fragmentshader&#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&quot;&gt;text&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;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;From there Three.js will compile and run your shaders attached to the
mesh to which you give that material. It doesn&#39;t get much easier than that
really. Well it probably does, but we&#39;re talking about 3D running in your
browser so I figure you expect a certain amount of complexity.&lt;/p&gt;
&lt;p&gt;We can actually add two more properties to our MeshShaderMaterial: uniforms
and attributes. They can both take vectors, integers or floats but as I mentioned
before uniforms are the same for the whole frame, i.e. for all vertices, so they
tend to be single values. Attributes, however, are per-vertex variables, so they
are expected to be an array. There should be a one-to-one relationship
between the number of values in the attributes array and the number of vertices
in the mesh.&lt;/p&gt;
&lt;h2 id=&quot;7-next-steps&quot;&gt;7. Next Steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#7-next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now we are going to spend a bit of time adding in an animation loop,
vertex attributes and a uniform. We&#39;ll also add in a varying variable
so that the vertex shader can send some data to the fragment shader.
The end result is that our sphere
that was pink is going to appear to be lit from above and to the side and
is going to pulsate. It&#39;s kind of trippy, but hopefully it will lead
you to a good understanding of the three variable types as well as how they
relate to each other and the underlying geometry.&lt;/p&gt;
&lt;h2 id=&quot;8-a-fake-light&quot;&gt;8. A Fake Light &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#8-a-fake-light&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s update the colouring so it&#39;s not a flat coloured object. We could take a look at
how Three.js handles lighting, but as I&#39;m sure you can appreciate
it&#39;s more complex than we need right now, so we&#39;re going to fake it. You should
totally look through &lt;a href=&quot;https://github.com/mrdoob/three.js/blob/master/src/extras/ShaderUtils.js&quot; rel=&quot;noopener&quot;&gt;the fantastic shaders&lt;/a&gt; that are a part of Three.js, and also &lt;a href=&quot;http://ro.me/tech&quot; rel=&quot;noopener&quot;&gt;the ones&lt;/a&gt; from the recent amazing WebGL project by Chris Milk and Google, &lt;a href=&quot;http://ro.me/&quot; rel=&quot;noopener&quot;&gt;Rome&lt;/a&gt;.
Back to our shaders. We&#39;ll update our Vertex Shader to provide each vertex
normal to the Fragment Shader. We do this with a varying:&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;// create a shared variable for the&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// VS and FS containing the normal&lt;/span&gt;&lt;br /&gt;varying vec3 vNormal&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;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// set the vNormal value with&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the attribute value passed&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// in by Three.js&lt;/span&gt;&lt;br /&gt;vNormal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;gl_Position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; projectionMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                modelViewMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;and in the Fragment Shader we&#39;re going to set up the same variable name
and then use the dot product of the vertex normal with a vector that
represents a light shining from above and to the right of the sphere.
The net result of this gives us an effect
similar to a directional light in a 3D package.&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;// same name and type as VS&lt;/span&gt;&lt;br /&gt;varying vec3 vNormal&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;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// calc the dot product and clamp&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 0 -&gt; 1 rather than -1 -&gt; 1&lt;/span&gt;&lt;br /&gt;vec3 light &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&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;    &lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ensure it&#39;s normalized&lt;/span&gt;&lt;br /&gt;light &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;light&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// calculate the dot product of&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the light to the vertex normal&lt;/span&gt;&lt;br /&gt;float dProd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vNormal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; light&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// feed into our frag colour&lt;/span&gt;&lt;br /&gt;gl_FragColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dProd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dProd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dProd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&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;&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;So the reason the dot product works is that given two vectors it comes
out with a number that tells you how &#39;similar&#39; the two vectors are. With
normalised vectors, if they point in exactly the same direction, you get
a value of 1. If they point in opposite directions you get a -1. What we
do is take that number and apply it to our lighting. So a vertex in the
top right will have a value near or equal to 1, i.e. fully lit, whereas a vertex on
the side would have a value near 0 and round the back would be -1. We
clamp the value to 0 for anything negative, but when you plug the numbers
in you end up with the basic lighting we&#39;re seeing.&lt;/p&gt;
&lt;p&gt;What&#39;s next? Well it would be nice to maybe
try messing with some vertex positions.&lt;/p&gt;
&lt;h2 id=&quot;9-attributes&quot;&gt;9. Attributes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#9-attributes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What I&#39;d like us to do now is attach a random number to each vertex
via an attribute. We&#39;ll use this number to push the vertex out along
its normal. The net result will be some kind of weird spike ball that
will change every time you refresh the page. It won&#39;t be animated just
yet (that happens next) but a few page refreshes will show you it&#39;s randomised.&lt;/p&gt;
&lt;p&gt;Let&#39;s start by adding in the attribute to the vertex shader:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;attribute float displacement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;varying vec3 vNormal&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;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;&lt;br /&gt;vNormal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// push the displacement into the three&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// slots of a 3D vector so it can be&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// used in operations with other 3D&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// vectors like positions and normals&lt;/span&gt;&lt;br /&gt;vec3 newPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; position &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;br /&gt;                    normal &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;br /&gt;                    &lt;span class=&quot;token function&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;displacement&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;br /&gt;gl_Position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; projectionMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                modelViewMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newPosition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;How does it look?&lt;/p&gt;
&lt;p&gt;Not much different really! This is because the attribute
hasn&#39;t been set up in the MeshShaderMaterial so effectively the
shader uses a zero value instead. It&#39;s kind of like a placeholder
right now. In a second we&#39;ll add the attribute to the
MeshShaderMaterial in the JavaScript and Three.js will tie the two
together for us automatically.&lt;/p&gt;
&lt;p&gt;Also of note is the fact that I had to
assign the updated position to a &lt;strong&gt;new&lt;/strong&gt; vec3 variable because the
original attribute, like all attributes, is read only.&lt;/p&gt;
&lt;h2 id=&quot;10-updating-the-meshshadermaterial&quot;&gt;10. Updating the MeshShaderMaterial &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#10-updating-the-meshshadermaterial&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s hop straight into updating our MeshShaderMaterial
with the attribute needed to power our displacement. A reminder:
attributes are per-vertex values so we need one value per vertex
in our sphere. Like this:&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 keyword&quot;&gt;var&lt;/span&gt; attributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;displacement&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;f&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a float&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&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 punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// an empty array&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// create the material and now&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// include the attributes property&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MeshShaderMaterial&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;&lt;span class=&quot;token literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     attributes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;vertexShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#vertexshader&#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&quot;&gt;text&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;&lt;span class=&quot;token literal-property property&quot;&gt;fragmentShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#fragmentshader&#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&quot;&gt;text&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;&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// now populate the array of attributes&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; vertices &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sphere&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertices&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; attributes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;displacement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; v &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; v &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; vertices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; v&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&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 operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now we&#39;re seeing a mangled sphere, but the cool
thing is that all the displacement is happening on the GPU.&lt;/p&gt;
&lt;h2 id=&quot;11-animating-that-sucker&quot;&gt;11. Animating That Sucker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#11-animating-that-sucker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We should totally make this animate. How do we do it? Well there
are two things we need to get in place:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A uniform to animate how much displacement should be applied
in each frame. We can use sine or cosine for that since they run from -1 to 1&lt;/li&gt;
&lt;li&gt;An animation loop in the JS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&#39;re going to add the uniform to both
the MeshShaderMaterial and the Vertex Shader. First the Vertex Shader:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;uniform float amplitude&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;attribute float displacement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;varying vec3 vNormal&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;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&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;&lt;br /&gt;vNormal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// multiply our displacement by the&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// amplitude. The amp will get animated&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// so we&#39;ll have animated displacement&lt;/span&gt;&lt;br /&gt;vec3 newPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; position &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;br /&gt;                    normal &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;br /&gt;                    &lt;span class=&quot;token function&quot;&gt;vec3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;displacement &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                        amplitude&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;br /&gt;gl_Position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; projectionMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                modelViewMatrix &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function&quot;&gt;vec4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newPosition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next we update the MeshShaderMaterial:&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;// add a uniform for the amplitude&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; uniforms &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;amplitude&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;f&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a float&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// create the final material&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; shaderMaterial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;THREE&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MeshShaderMaterial&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;&lt;span class=&quot;token literal-property property&quot;&gt;uniforms&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;       uniforms&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     attributes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;vertexShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#vertexshader&#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&quot;&gt;text&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;&lt;span class=&quot;token literal-property property&quot;&gt;fragmentShader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#fragmentshader&#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&quot;&gt;text&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;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Our shaders are done for now. But right we would appear to have taken a step backwards.
This is largely because
our amplitude value is at 0 and since we multiply that with the
displacement we&#39;re seeing nothing change. We also haven&#39;t set up the
animation loop so we never see that 0 change to anything else.&lt;/p&gt;
&lt;p&gt;In our JavaScript we now need to wrap up the render call into a function and
then use requestAnimationFrame to call it. In there we also need
to update the uniform&#39;s value.&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 keyword&quot;&gt;var&lt;/span&gt; frame &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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// update the amplitude based on&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the frame value&lt;/span&gt;&lt;br /&gt;uniforms&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amplitude&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;frame&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;frame &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scene&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; camera&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// set up the next call&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;requestAnimFrame&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;requestAnimFrame&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;12-conclusion&quot;&gt;12. Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webgl-shaders/#12-conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;And that&#39;s it! You can now see it&#39;s animating  in a strange (and slightly trippy) pulsating manner.&lt;/p&gt;
&lt;p&gt;There&#39;s so much more we can cover
on shaders as a topic, but I hope you&#39;ve found this introduction
helpful. You should now be able to understand shaders when you see them
as well as having the confidence to create some amazing shaders of your own!&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author>
  </entry>
</feed>
