<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Surma on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Surma</name>
  </author>
  <link href="https://web.dev/authors/surma/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/MPQ3Co9Ej7Uka4cgPePh.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Surma.</subtitle>
  
  
  <entry>
    <title>Deep-copying in JavaScript using structuredClone</title>
    <link href="https://web.dev/structured-clone/"/>
    <updated>2021-12-16T00:00:00Z</updated>
    <id>https://web.dev/structured-clone/</id>
    <content type="html" mode="escaped">&lt;p&gt;For the longest time, you had to resort to workarounds and libraries to create a deep copy of a JavaScript value. The Platform now ships with &lt;code&gt;structuredClone()&lt;/code&gt;, a built-in function for deep-copying.&lt;/p&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 98, 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;
      98
    &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 94, 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;
      94
    &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 98, 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;
      98
    &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 15.4, 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;
      15.4
    &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/API/structuredClone#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;shallow-copies&quot;&gt;Shallow copies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#shallow-copies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Copying a value in JavaScript is almost always &lt;em&gt;shallow&lt;/em&gt;, as opposed to &lt;em&gt;deep&lt;/em&gt;.  That means that changes to deeply nested values will be visible in the copy as well as the original.&lt;/p&gt;
&lt;p&gt;One way to create a shallow copy in JavaScript using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax&quot; rel=&quot;noopener&quot;&gt;object spread operator&lt;/a&gt; &lt;code&gt;...&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 keyword&quot;&gt;const&lt;/span&gt; myOriginal &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;someProp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;with a string value&quot;&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;anotherProp&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;withAnotherProp&lt;/span&gt;&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 literal-property property&quot;&gt;andAnotherProp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&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 keyword&quot;&gt;const&lt;/span&gt; myShallowCopy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;myOriginal&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;Adding or changing a property directly on the shallow copy will only affect the copy, not the original:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;myShallowCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aNewProp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a new value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myOriginal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aNewProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ^ logs `undefined`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;However, adding or changing a deeply nested property affects &lt;em&gt;both&lt;/em&gt; the copy and the original:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;myShallowCopy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;anotherProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aNewProp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a new value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myOriginal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;anotherProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aNewProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ^ logs `a new value`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The expression &lt;code&gt;{...myOriginal}&lt;/code&gt; iterates over the (enumerable) properties of &lt;code&gt;myOriginal&lt;/code&gt; using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax&quot; rel=&quot;noopener&quot;&gt;Spread Operator&lt;/a&gt;. It uses the property name and value, and assigns them one by one to a freshly created, empty object. As such, the resulting object is identical in shape, but with its own copy of the list of properties and values. The values are copied, too, but so-called primitive values are handled differently by the JavaScript value than non-primitive values. To quote &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Primitive&quot; rel=&quot;noopener&quot;&gt;MDN&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In JavaScript, a primitive (primitive value, primitive data type) is data that is not an object and has no methods. There are seven primitive data types: string, number, bigint, boolean, undefined, symbol, and null.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;MDN — Primitive&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Non-primitive values are handled as &lt;em&gt;references&lt;/em&gt;, meaning that the act of copying the value is really just copying a reference to the same underlying object, resulting in the shallow copy behavior.&lt;/p&gt;
&lt;h2 id=&quot;deep-copies&quot;&gt;Deep copies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#deep-copies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The opposite of a shallow copy is a deep copy. A deep copy algorithm also copies an object’s properties one by one, but invokes itself recursively when it finds a reference to another object, creating a copy of that object as well. This can be very important to make sure that two pieces of code don’t accidentally share an object and unknowingly manipulate each others’ state.&lt;/p&gt;
&lt;p&gt;There used to be no easy or nice way to create a deep-copy of a value in JavaScript. Many people relied on third-party libraries like &lt;a href=&quot;https://lodash.com/docs/#cloneDeep&quot; rel=&quot;noopener&quot;&gt;Lodash’s &lt;code&gt;cloneDeep()&lt;/code&gt;&lt;/a&gt;  function. Arguably the most common solution to this problem was a JSON-based hack:&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;const&lt;/span&gt; myDeepCopy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myOriginal&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;In fact, this was such a popular workaround, that &lt;a href=&quot;https://v8.dev/blog/cost-of-javascript-2019#json&quot; rel=&quot;noopener&quot;&gt;V8 aggressively optimized&lt;/a&gt; &lt;code&gt;JSON.parse()&lt;/code&gt; and specifically the pattern above to make it as fast as possible. And while it is fast, it comes with a couple of shortcomings and tripwires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recursive data structures&lt;/strong&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; will throw when you give it a recursive data structure. This can happen quite easily when working with linked lists or trees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in types&lt;/strong&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; will throw if the value contains other JS built-ins like &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt; or &lt;code&gt;ArrayBuffer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functions&lt;/strong&gt;: &lt;code&gt;JSON.stringify()&lt;/code&gt; will quietly discard functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;structured-cloning&quot;&gt;Structured cloning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#structured-cloning&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The platform already needed the ability to create deep copies of JavaScript values in a couple of places: Storing a JS value in IndexedDB requires some form of serialization so it can be stored on disk and later deserialized to restore the JS value. Similarly, sending messages to a WebWorker via &lt;code&gt;postMessage()&lt;/code&gt; requires transferring a JS value from one JS realm to another. The algorithm that is used for this is called “Structured Clone”, and until recently, wasn’t easily accessible to developers.&lt;/p&gt;
&lt;p&gt;That has now changed! The HTML spec was amended to expose a function called &lt;code&gt;structuredClone()&lt;/code&gt; that runs exactly that algorithm as a means for developers to easily create deep copies of JavaScript 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 keyword&quot;&gt;const&lt;/span&gt; myDeepCopy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;structuredClone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myOriginal&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;That’s it! That’s the entire API. If you want to dive deeper into the details, take a look at the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/structuredClone&quot; rel=&quot;noopener&quot;&gt;MDN article&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;features-and-limitations&quot;&gt;Features and limitations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#features-and-limitations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Structured cloning addresses many (although not all) shortcomings of the &lt;code&gt;JSON.stringify()&lt;/code&gt; technique. Structured cloning can handle cyclical data structures, support many built-in data types and is generally more robust and often faster.&lt;/p&gt;
&lt;p&gt;However, it still has some limitations that may catch you off-guard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prototypes&lt;/strong&gt;: If you use &lt;code&gt;structuredClone()&lt;/code&gt; with a class instance, you’ll get a plain object as the return
value, as structured cloning discards the object’s prototype chain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Functions&lt;/strong&gt;: If your object contains functions, they will be &lt;em&gt;quietly&lt;/em&gt; discarded.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-cloneables&lt;/strong&gt;: Some values are not structured cloneable, most notably &lt;code&gt;Error&lt;/code&gt; and DOM nodes. It
will cause &lt;code&gt;structuredClone()&lt;/code&gt; to throw.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If any of these limitations are a deal-breaker for your use-case, libraries like Lodash still provide custom implementations of other deep-cloning algorithms that may or may not fit your use-case.&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While I haven’t done a new micro-benchmark comparison, &lt;a href=&quot;https://surma.dev/things/deep-copy/index.html&quot; rel=&quot;noopener&quot;&gt;I did a comparison in early 2018&lt;/a&gt;, before &lt;code&gt;structuredClone()&lt;/code&gt; was exposed. Back then, &lt;code&gt;JSON.parse()&lt;/code&gt; was the fastest option for very small objects. I expect that to remain the same. Techniques that relied on structured cloning were (significantly) faster for bigger objects. Considering that the new &lt;code&gt;structuredClone()&lt;/code&gt; comes without the overhead of abusing other APIs and is more robust  than &lt;code&gt;JSON.parse()&lt;/code&gt;, I recommend you make it your default approach for creating deep copies.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/structured-clone/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you need to create a deep-copy of a value in JS—maybe that be because you use immutable data structures or you want to make sure a function can manipulate an object without affecting the original—you no longer need to reach for workarounds or libraries. The JS ecosystem now has &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/structuredClone&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;structuredClone()&lt;/code&gt;&lt;/a&gt;. Huzzah.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Introducing libSquoosh</title>
    <link href="https://web.dev/introducing-libsquoosh/"/>
    <updated>2021-06-08T00:00:00Z</updated>
    <id>https://web.dev/introducing-libsquoosh/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;strong&gt;We are happy to introduce &lt;a href=&quot;https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh&quot; rel=&quot;noopener&quot;&gt;libSquoosh&lt;/a&gt;, an experimental Node library on top of which the Squoosh CLI is built, giving you all the capabilities for the Squoosh CLI with a JavaScript-idiomatic interface.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Squoosh.app is a PWA that compresses images for you in the browser. It supports many old and new image formats and processes them client-side in the browser through WebAssembly. This means your pictures stay safely on your own computer rather than being sent to a server somewhere, and that Squoosh works even when offline.&lt;/p&gt;
&lt;p&gt;At Chrome DevSummit 2020 &lt;a href=&quot;https://web.dev/squoosh-v2/&quot;&gt;we announced Squoosh v2&lt;/a&gt;, together with the Squoosh CLI to bring all the codecs of Squoosh to the command-line using Node and WebAssembly. This allows you to compress entire folders with one command and make use of the &lt;a href=&quot;https://github.com/GoogleChromeLabs/squoosh/tree/dev/cli#auto-optimizer&quot; rel=&quot;noopener&quot;&gt;CLI&#39;s&lt;/a&gt; to let it choose the codec parameters for you.&lt;/p&gt;
&lt;p&gt;The CLI enables a lot of automation and so it&#39;s only natural that developers began asking for a more idiomatic interface than programmatically invoking the Squoosh CLI via the shell. &lt;a href=&quot;https://github.com/atjn&quot; rel=&quot;noopener&quot;&gt;Anton (@atjn on GitHub)&lt;/a&gt; stepped up to the task and separated the Squoosh CLI code into two parts: The command line interface code and the underlying core functionality.&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ImagePool &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@squoosh/lib&quot;&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;// libSquoosh uses a worker-pool under the hood&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// to parallelize all image processing.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imagePool &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;ImagePool&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;// Accepts both file paths and Buffers/TypedArrays.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imagePool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ingestImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./squoosh.jpeg&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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Optional.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// await image.preprocess({&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   resize: {&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//     enabled: true,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//     width: 128,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   },&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// });&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&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;// All codecs are initialized with default values&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// that can be individually overwritten.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;mozjpeg&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;quality&lt;/span&gt;&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;  &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;avif&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;cqLevel&lt;/span&gt;&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;  &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;jxl&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 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 keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; extension&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; binary &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 keyword&quot;&gt;await&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;encodedWith&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mozjpeg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&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 string&quot;&gt;output.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;extension&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&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; binary&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;// ... same for other encoders ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; imagePool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&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;&lt;p&gt;Our goal is to make image compression more accessible to tooling authors. We hope to see integration into Webpack, Rollup and other build tools to make sure your images are appropriately optimized for the web.&lt;/p&gt;
&lt;p&gt;I&#39;d like to express a huge &amp;quot;thank you&amp;quot; to Anton for the time he has committed to Squoosh!&lt;/p&gt;
&lt;p&gt;It&#39;s still early for the Squoosh CLI and libSquoosh and we have many more ideas and plans that we&#39;d like to implement. In the meanwhile, try libSquoosh! However, be mindful that this is an early, experimental release and that there is a good chance you will run into some bugs. If you find some or have questions, please open an &lt;a href=&quot;https://github.com/GoogleChromeLabs/squoosh/issues/new/choose&quot; rel=&quot;noopener&quot;&gt;issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in contributing to Squoosh—for example contributing to the extremely sparse documentation around libSquoosh or help with any of the other parts of the app—we are starting a mentorship program to help you get started. If you want to know more, check out our &lt;a href=&quot;https://github.com/GoogleChromeLabs/squoosh/issues/1020&quot; rel=&quot;noopener&quot;&gt;tracking issue&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Pixel-perfect rendering with devicePixelContentBox</title>
    <link href="https://web.dev/device-pixel-content-box/"/>
    <updated>2020-07-07T00:00:00Z</updated>
    <id>https://web.dev/device-pixel-content-box/</id>
    <content type="html" mode="escaped">&lt;p&gt;Since Chrome 84, &lt;a href=&quot;https://web.dev/resize-observer/&quot;&gt;ResizeObserver&lt;/a&gt; supports a new box measurement called &lt;code&gt;devicePixelContentBox&lt;/code&gt;, that measures the element&#39;s dimension in &lt;em&gt;physical&lt;/em&gt; pixels. This enables rendering pixel-perfect graphics, especially in the context of high-density screens.&lt;/p&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 84, 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;
      84
    &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 93, 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;
      93
    &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 84, 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;
      84
    &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, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id=&quot;background-css-pixels,-canvas-pixels,-and-physical-pixels&quot;&gt;Background: CSS pixels, canvas pixels, and physical pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#background-css-pixels,-canvas-pixels,-and-physical-pixels&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While we often work with abstract units of length like &lt;code&gt;em&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt; or &lt;code&gt;vh&lt;/code&gt;, it all boils down to pixels. Whenever we specify the size or position of an element in CSS, the browser&#39;s layout engine will eventually convert that value to pixels (&lt;code&gt;px&lt;/code&gt;). These are &amp;quot;CSS Pixels&amp;quot;, which have a lot of history and only have a loose relationship with the pixels you have on your screen.&lt;/p&gt;
&lt;p&gt;For a long time, it was fairly reasonable to estimate anyone&#39;s screen pixel density with 96DPI (&amp;quot;dots per inch&amp;quot;), meaning any given monitor would have roughly 38 pixels per cm. Over time, monitors grew and/or shrunk or started to have more pixels on the same surface area. Combine that with the fact that lots of content on the web define their dimensions, including font sizes, in &lt;code&gt;px&lt;/code&gt;, and we end up with illegible text on these high-density (&amp;quot;HiDPI&amp;quot;) screens. As a counter-measure, browsers hide the monitor&#39;s actual pixel density and instead pretend that the user has a 96 DPI display. The &lt;code&gt;px&lt;/code&gt; unit in CSS represents the size of one pixel on this &lt;em&gt;virtual&lt;/em&gt; 96 DPI display, hence the name &amp;quot;CSS Pixel&amp;quot;. This unit is only used for measurement and positioning. Before any actual rendering happens, a conversion to physical pixels happens.&lt;/p&gt;
&lt;p&gt;How do we go from this virtual display to the user&#39;s real display? Enter &lt;code&gt;devicePixelRatio&lt;/code&gt;. This global value tells you how many &lt;em&gt;physical&lt;/em&gt; pixels you need to form a single CSS pixel. If &lt;code&gt;devicePixelRatio&lt;/code&gt; (dPR) is &lt;code&gt;1&lt;/code&gt;, you are working on a monitor with roughly 96DPI. If you have a retina screen, your dPR is probably &lt;code&gt;2&lt;/code&gt;. On phones it is not uncommon to encounter higher (and weirder) dPR values like &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt; or even &lt;code&gt;2.65&lt;/code&gt;. It is essential to note that this value is &lt;em&gt;exact&lt;/em&gt;, but doesn&#39;t let you derive the monitor&#39;s &lt;em&gt;actual&lt;/em&gt; DPI value. A dPR of &lt;code&gt;2&lt;/code&gt; means that 1 CSS pixel will map to &lt;em&gt;exactly&lt;/em&gt; 2 physical pixels.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  Example
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; My monitor has a dPR of &lt;code&gt;1&lt;/code&gt; according to Chrome…&lt;/p&gt;
&lt;/summary&gt;
It has 3440 pixels in width and the display area is 79cm wide.
That leads to a resolution of 110 DPI. Close to 96, but not quite.
That is also the reason why a &lt;code&gt;&amp;lt;div style=&quot;width: 1cm; height: 1cm&quot;&amp;gt;&lt;/code&gt;
will not exactly measure 1cm in size on most displays.
&lt;/details&gt;
&lt;p&gt;Finally, dPR can also be affected by your browser&#39;s zoom feature. If you zoom in, the browser increases the reported dPR, causing everything to render bigger. If you check &lt;code&gt;devicePixelRatio&lt;/code&gt; in a DevTools Console while zooming, you can see fractional values appear.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;314&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ko6ehWmACBrUcPrl4AGO.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;DevTools showing a variety of fractional &lt;code&gt;devicePixelRatio&lt;/code&gt; due to zooming.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let&#39;s add the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element to the mix. You can specify how many pixels you want the canvas to have using the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes. So &lt;code&gt;&amp;lt;canvas width=40 height=30&amp;gt;&lt;/code&gt; would be a canvas with 40 by 30 pixels. However, this does not mean that it will be &lt;em&gt;displayed&lt;/em&gt; at 40 by 30 pixels. By default, the canvas will use the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attribute to define its intrinsic size, but you can arbitrarily resize the canvas using all the CSS properties you know and love. With everything we have learned so far, it might occur to you that this will not be ideal in every scenario. One pixel on the canvas might end up covering multiple physical pixels, or just a fraction of a physical pixel. This can lead to unpleasing visual artifacts.&lt;/p&gt;
&lt;p&gt;To summarize: Canvas elements have a given size to define the area that you can draw on. The number of canvas pixels is completely independent from the canvas&#39; display size, specified in CSS pixels. The number of CSS pixels is not the same as the number of physical pixels.&lt;/p&gt;
&lt;h3 id=&quot;pixel-perfection&quot;&gt;Pixel perfection &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#pixel-perfection&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some scenarios, it is desirable to have an exact mapping from canvas pixels to physical pixels. If this mapping is achieved, it&#39;s called &amp;quot;pixel-perfect&amp;quot;. Pixel-perfect rendering is crucial for legible rendering of text, especially when using &lt;a href=&quot;https://en.wikipedia.org/wiki/Subpixel_rendering&quot; rel=&quot;noopener&quot;&gt;subpixel rendering&lt;/a&gt; or when displaying graphics with tightly aligned lines of alternating brightness.&lt;/p&gt;
&lt;p&gt;To achieve something as close to a pixel-perfect canvas as possible on the web, this has been more or less the go-to approach:&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* … styles that affect the canvas&#39; size … */&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;canvas&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myCanvas&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;canvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cvs &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;#myCanvas&#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;// Get the canvas&#39; size in CSS pixels&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rectangle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cvs&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;// Convert it to real pixels. Ish.&lt;/span&gt;&lt;br /&gt;  cvs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rectangle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; devicePixelRatio&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  cvs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rectangle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; devicePixelRatio&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Start drawing…&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The astute reader might be wondering what happens when dPR is not an integer value. That is a good question and exactly where the crux of this entire problem lies. In addition, if you specify an element&#39;s position or size using percentages, &lt;code&gt;vh&lt;/code&gt;, or other indirect values, it is possible that they will resolve to fractional CSS pixel values. An element with &lt;code&gt;margin-left: 33%&lt;/code&gt; can end up with a rectangle like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;409&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gWP1lVOw8ITEJhziaKnU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;DevTools showing fractional pixel values as a result of a &lt;code&gt;getBoundingClientRect()&lt;/code&gt; call.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;CSS pixels are purely virtual, so having fractions of a pixel is okay in theory, but how does the browser figure out the mapping to physical pixels? Because fractional &lt;em&gt;physical&lt;/em&gt; pixels are not a thing.&lt;/p&gt;
&lt;h2 id=&quot;pixel-snapping&quot;&gt;Pixel snapping &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#pixel-snapping&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The part of the unit conversion process that takes care of aligning elements with physical pixels is called &amp;quot;pixel snapping&amp;quot;, and it does what it says on the tin: It snaps fractional pixel values to integer, physical pixel values. How exactly this happens is different from browser to browser. If we have an element with a width of &lt;code&gt;791.984px&lt;/code&gt; on a display where dPR is 1, one browser might render the element at &lt;code&gt;792px&lt;/code&gt; physical pixels, while another browser might render it at &lt;code&gt;791px&lt;/code&gt;. That&#39;s just a single pixel off, but a single pixel can be detrimental to renderings that need to be pixel-perfect. This can lead to blurriness or even more visible artifacts like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Moir%C3%A9_pattern&quot; rel=&quot;noopener&quot;&gt;Moiré effect&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;802&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/11FeCvp9aKLnDk05vYwY.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;The top image is a raster of differently colored pixels. The bottom image is the same as above, but the width and height have been reduced by one pixel using bilinear scaling. The emerging pattern is called the Moiré effect.&lt;br /&gt;(You might have to open this image in a new tab to see it without any scaling applied to it.)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;devicepixelcontentbox&quot;&gt;&lt;code&gt;devicePixelContentBox&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#devicepixelcontentbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;devicePixelContentBox&lt;/code&gt; gives you an element&#39;s content box in device pixel (i.e. physical pixel) units. It&#39;s part of &lt;code&gt;ResizeObserver&lt;/code&gt;. While &lt;a href=&quot;https://caniuse.com/#feat=resizeobserver&quot; rel=&quot;noopener&quot;&gt;ResizeObserver is now supported in all major browsers&lt;/a&gt; since Safari 13.1, the &lt;code&gt;devicePixelContentBox&lt;/code&gt; property is only in Chrome 84+ for now.&lt;/p&gt;
&lt;p&gt;As mentioned in &lt;a href=&quot;https://web.dev/resize-observer/&quot;&gt;&lt;code&gt;ResizeObserver&lt;/code&gt;: it&#39;s like &lt;code&gt;document.onresize&lt;/code&gt; for elements&lt;/a&gt;, the callback function of a &lt;code&gt;ResizeObserver&lt;/code&gt; will be called before paint and after layout. That means that the &lt;code&gt;entries&lt;/code&gt; parameter to the callback will contain the sizes of all observed elements just before they are being painted. In the context of our canvas problem outlined above, we can use this opportunity to adjust the number of pixels on our canvas, ensuring that we end up with an exact one-to-one mapping between canvas pixels and physical pixels.&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;const&lt;/span&gt; observer &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;ResizeObserver&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 parameter&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; entry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&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 parameter&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &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 punctuation&quot;&gt;;&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; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devicePixelContentBoxSize&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;inlineSize&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; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devicePixelContentBoxSize&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;blockSize&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;/* … render to canvas … */&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;observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&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; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;box&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 string&quot;&gt;&#39;device-pixel-content-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;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;code&gt;box&lt;/code&gt; property in the options object for &lt;code&gt;observer.observe()&lt;/code&gt; lets you define which sizes you wish to &lt;em&gt;observe&lt;/em&gt;. So while each &lt;code&gt;ResizeObserverEntry&lt;/code&gt; will always provide &lt;code&gt;borderBoxSize&lt;/code&gt;, &lt;code&gt;contentBoxSize&lt;/code&gt; and &lt;code&gt;devicePixelContentBoxSize&lt;/code&gt; (provided the browser supports it), the callback will only be invoked if any of the &lt;em&gt;observed&lt;/em&gt; box metrics change.&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; All of the box metrics are arrays to allow &lt;code&gt;ResizeObserver&lt;/code&gt; to handle fragmentation in the future. At the time of writing, the array is always of length 1. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;With this new property, we can even animate our canvas&#39; size and position (effectively guaranteeing fractional pixel values), and not see any Moiré effects on the rendering. If you would like to see the Moiré effect on the approach using &lt;code&gt;getBoundingClientRect()&lt;/code&gt;, and how the new &lt;code&gt;ResizeObserver&lt;/code&gt; property allows you to avoid it, take a look at the &lt;a href=&quot;https://device-pixel-content-box.glitch.me/&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; in Chrome 84 or later!&lt;/p&gt;
&lt;h3 id=&quot;feature-detection&quot;&gt;Feature detection &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#feature-detection&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To check if a user&#39;s browser has support for &lt;code&gt;devicePixelContentBox&lt;/code&gt;, we can observe any element, and check if the property is present on the &lt;code&gt;ResizeObserverEntry&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hasDevicePixelContentBox&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;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&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 parameter&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; ro &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;ResizeObserver&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 parameter&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;every&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 parameter&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;devicePixelContentBoxSize&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; entry&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;      ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disconnect&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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&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;body&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 literal-property property&quot;&gt;box&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 string&quot;&gt;&#39;device-pixel-content-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;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;span class=&quot;token function&quot;&gt;catch&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;span class=&quot;token operator&quot;&gt;=&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 keyword&quot;&gt;if&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hasDevicePixelContentBox&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;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;// The browser does NOT support devicePixelContentBox&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;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-pixel-content-box/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pixels are a surprisingly complex topic on the web and up until now there was no way for you to know the exact number of physical pixels an element occupies on the user&#39;s screen. The new &lt;code&gt;devicePixelContentBox&lt;/code&gt; property on a &lt;code&gt;ResizeObserverEntry&lt;/code&gt; gives you that piece of information and allows you to do pixel-perfect renderings with &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;. &lt;code&gt;devicePixelContentBox&lt;/code&gt; is supported in Chrome 84+.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Use web workers to run JavaScript off the browser&#39;s main thread</title>
    <link href="https://web.dev/off-main-thread/"/>
    <updated>2019-12-05T00:00:00Z</updated>
    <id>https://web.dev/off-main-thread/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the past 20 years,
the web has evolved dramatically from static documents with a few styles and images
to complex, dynamic applications.
However, one thing has remained largely unchanged:
we have just one thread per browser tab (with some exceptions)
to do the work of rendering our sites and running our JavaScript.&lt;/p&gt;
&lt;p&gt;As a result, the main thread has become incredibly overworked.
And as web apps grow in complexity,
the main thread becomes a significant bottleneck for performance.
To make matters worse,
the amount of time it takes to run code on the main thread for a given user
is &lt;strong&gt;almost completely unpredictable&lt;/strong&gt;
because device capabilities have a massive effect on performance.
That unpredictability will only grow as users access the web
from an increasingly diverse set of devices,
from hyper-constrained feature phones to high-powered,
high-refresh-rate flagship machines.&lt;/p&gt;
&lt;p&gt;If we want sophisticated web apps to reliably meet performance guidelines
like the &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;—which
is based on empirical data about human perception and psychology—we
need ways to execute our code &lt;strong&gt;off the main thread (OMT)&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 want to hear more about the case for an OMT architecture, watch my CDS 2019 talk below. &lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;7Rrv9qFMWNM&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-web-workers&quot;&gt;Why web workers? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#why-web-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript is, by default, a single-threaded language that runs &lt;a href=&quot;https://web.dev/optimize-long-tasks/#what-is-a-task&quot;&gt;tasks&lt;/a&gt; on &lt;a href=&quot;https://web.dev/optimize-long-tasks/#what-is-the-main-thread&quot;&gt;the main thread&lt;/a&gt;. However, web workers provide a sort of escape hatch from the main thread by allowing developers to spin up separate threads to handle work off of the main thread. While the scope of web workers is limited and doesn&#39;t offer direct access to the DOM, they can be hugely beneficial if there is considerable work that needs to be done that would otherwise overwhelm the main thread.&lt;/p&gt;
&lt;p&gt;Where &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; are concerned, running work off the main thread can be beneficial. In particular, offloading work from the main thread to web workers can reduce contention for the main thread, which can improve important responsiveness metrics such as &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt;. When the main thread has less work to process, it can respond more quickly to user interactions.&lt;/p&gt;
&lt;p&gt;Less main thread work—especially during startup—also carries a potential benefit for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; by reducing long tasks. Rendering an LCP element requires main thread time—either for rendering text or images, which are frequent and common LCP elements—and by reducing main thread work overall, you can ensure that your page&#39;s LCP element is less likely to be blocked by expensive work that a web worker could handle instead.&lt;/p&gt;
&lt;h2 id=&quot;threading-with-web-workers&quot;&gt;Threading with web workers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#threading-with-web-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Other platforms typically support parallel work
by allowing you to give a thread a function,
which runs in parallel with the rest of your program.
You can access the same variables from both threads,
and access to these shared resources can be synchronized
with mutexes and semaphores to prevent race conditions.&lt;/p&gt;
&lt;p&gt;In JavaScript, we can get roughly similar functionality from web workers,
which have been around since 2007
and supported across all major browsers since 2012.
Web workers run in parallel with the main thread,
but unlike OS threading they can&#39;t share variables.&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; Don&#39;t confuse web workers with &lt;a href=&quot;https://web.dev/service-workers-cache-storage&quot;&gt;service workers&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Worklet&quot;&gt;worklets&lt;/a&gt;. While the names are similar, the functionality and uses are different. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To create a web worker, pass a file to the worker constructor,
which starts running that file in a separate thread:&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;const&lt;/span&gt; worker &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;./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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Communicate with the web worker by sending messages via the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/postMessage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;postMessage&lt;/code&gt; API&lt;/a&gt;.
Pass the message value as a parameter in the &lt;code&gt;postMessage&lt;/code&gt; call
and then add a message event listener to the worker:&lt;/p&gt;
&lt;!--lint disable no-duplicate-headings-in-section--&gt;
&lt;h3 id=&quot;mainjs&quot;&gt;&lt;code&gt;main.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#mainjs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &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;./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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&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 number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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;/mark&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;workerjs&quot;&gt;&lt;code&gt;worker.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#workerjs&quot;&gt;#&lt;/a&gt;&lt;/h3&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 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;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&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;// Do stuff with the message&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;To send a message back to the main thread,
use the same &lt;code&gt;postMessage&lt;/code&gt; API in the web worker
and set up an event listener on the main thread:&lt;/p&gt;
&lt;h3 id=&quot;mainjs-2&quot;&gt;&lt;code&gt;main.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#mainjs-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &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;./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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&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 number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;worker&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;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;/mark&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;workerjs-2&quot;&gt;&lt;code&gt;worker.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#workerjs-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&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;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Do stuff with the message&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Admittedly, this approach is somewhat limited.
Historically, web workers have mainly been used
for moving a single piece of heavy work off the main thread.
Trying to handle multiple operations with a single web worker gets unwieldy quickly:
you have to encode not only the parameters but also the operation in the message,
and you have to do bookkeeping to match responses to requests.
That complexity is likely why web workers haven&#39;t been adopted more widely.&lt;/p&gt;
&lt;p&gt;But if we could remove some of the difficulty of communicating
between the main thread and web workers,
this model could be a great fit for many use cases.
And, luckily, there&#39;s a library that does just that!&lt;/p&gt;
&lt;h2 id=&quot;comlink-making-web-workers-less-work&quot;&gt;Comlink: making web workers less work &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#comlink-making-web-workers-less-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://npm.im/comlink&quot; rel=&quot;noopener&quot;&gt;Comlink&lt;/a&gt; is a library
whose goal is to let you use web workers
without having to think about the details of &lt;code&gt;postMessage&lt;/code&gt;.
Comlink lets you to share variables
between web workers and the main thread
almost like other programming languages that support threading.&lt;/p&gt;
&lt;p&gt;You set up Comlink by importing it in a web worker
and defining a set of functions to expose to the main thread.
You then import Comlink on the main thread, wrap the worker,
and get access to the exposed functions:&lt;/p&gt;
&lt;h3 id=&quot;workerjs-3&quot;&gt;&lt;code&gt;worker.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#workerjs-3&quot;&gt;#&lt;/a&gt;&lt;/h3&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;expose&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;comlink&quot;&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;const&lt;/span&gt; api &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 function&quot;&gt;someMethod&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;span class=&quot;token comment&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;expose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&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;h3 id=&quot;mainjs-3&quot;&gt;&lt;code&gt;main.js&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#mainjs-3&quot;&gt;#&lt;/a&gt;&lt;/h3&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;wrap&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;comlink&quot;&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;const&lt;/span&gt; worker &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;./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;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;wrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;worker&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;code&gt;api&lt;/code&gt; variable on main thread behaves the same as the one in the web worker,
except that every function returns a promise for a value rather than the value itself.&lt;/p&gt;
&lt;h2 id=&quot;what-code-should-you-move-to-a-web-worker&quot;&gt;What code should you move to a web worker? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#what-code-should-you-move-to-a-web-worker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web workers don&#39;t have access to the DOM and many APIs
like &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/USB&quot; rel=&quot;noopener&quot;&gt;WebUSB&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebRTC_API&quot; rel=&quot;noopener&quot;&gt;WebRTC&lt;/a&gt;, or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Audio_API&quot; rel=&quot;noopener&quot;&gt;Web Audio&lt;/a&gt;,
so you can&#39;t put pieces of your app that rely on such access in a worker.
Still, every small piece of code moved to a worker buys more headroom
on the main thread for stuff that &lt;em&gt;has&lt;/em&gt; to be there—like updating the user interface.&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; Restricting UI access  to the main thread is actually typical in other languages. In fact, both iOS and Android call the main thread the &lt;em&gt;UI thread&lt;/em&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;One problem for web developers is that most web apps rely on a UI framework
like Vue or React to orchestrate everything in the app;
everything is a component of the framework and so is inherently tied to the DOM.
That would seem to make it difficult to migrate to an OMT architecture.&lt;/p&gt;
&lt;p&gt;However, if we shift to a model in which UI concerns are separated from other concerns,
like state management, web workers can be quite useful even with framework-based apps.
That&#39;s exactly the approach taken with PROXX.&lt;/p&gt;
&lt;h2 id=&quot;proxx-an-omt-case-study&quot;&gt;PROXX: an OMT case study &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#proxx-an-omt-case-study&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Google Chrome team developed &lt;a href=&quot;https://web.dev/load-faster-like-proxx/&quot;&gt;PROXX&lt;/a&gt;
as a Minesweeper clone that meets
&lt;a href=&quot;https://developers.google.com/web/progressive-web-apps&quot; rel=&quot;noopener&quot;&gt;Progressive Web App&lt;/a&gt; requirements,
including working offline and having an engaging user experience.
Unfortunately, early versions of the game performed poorly on constrained devices
like feature phones, which led the team to realize that the main thread was a bottleneck.&lt;/p&gt;
&lt;p&gt;The team decided to use web workers to separate the game&#39;s visual state from its logic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main thread handles rendering of animations and transitions.&lt;/li&gt;
&lt;li&gt;A web worker handles game logic, which is purely computational.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This approach is similar to the Redux &lt;a href=&quot;https://facebook.github.io/flux/&quot;&gt;Flux pattern&lt;/a&gt;, so many Flux apps may be able to migrate fairly easily to an OMT architecture. Take a look at &lt;a href=&quot;http://dassur.ma/things/react-redux-comlink/&quot;&gt;this blog post&lt;/a&gt; to read more about applying OMT to a Redux app. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;OMT had interesting effects on PROXX&#39;s feature phone performance.
In the non-OMT version,
the UI is frozen for six seconds after the user interacts with it.
There&#39;s no feedback, and the user has to wait for the full six seconds
before being able to do something else.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; style=&quot;max-width: 400px;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/off-main-thread/proxx-nonomt.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/off-main-thread/proxx-nonomt.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
 &lt;figcaption&gt;
    UI response time in the &lt;strong&gt;non-OMT&lt;/strong&gt; version of PROXX.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the OMT version, however, the game takes &lt;em&gt;twelve&lt;/em&gt; seconds to complete a UI update.
While that seems like a performance loss,
it actually leads to increased feedback to the user.
The slowdown occurs because the app is shipping more frames than the non-OMT version,
which isn&#39;t shipping any frames at all.
The user therefore knows that something is happening
and can continue playing as the UI updates,
making the game feel considerably better.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; style=&quot;max-width: 400px;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/off-main-thread/proxx-omt.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/off-main-thread/proxx-omt.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
 &lt;figcaption&gt;
    UI response time in the &lt;strong&gt;OMT&lt;/strong&gt; version of PROXX.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is a conscious tradeoff:
we give users of constrained devices an experience that &lt;em&gt;feels&lt;/em&gt; better
without penalizing users of high-end devices.&lt;/p&gt;
&lt;h2 id=&quot;implications-of-an-omt-architecture&quot;&gt;Implications of an OMT architecture &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#implications-of-an-omt-architecture&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As the PROXX example shows,
OMT makes your app reliably run on a wider range of devices,
but it doesn&#39;t make your app faster:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&#39;re just moving work from the main thread, not reducing the work.&lt;/li&gt;
&lt;li&gt;The extra communication overhead between the web worker
and the main thread can sometimes make things marginally slower.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;considering-the-tradeoffs&quot;&gt;Considering the tradeoffs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#considering-the-tradeoffs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since the main thread is free to process user interactions
like scrolling while JavaScript is running,
there are fewer dropped frames even though total wait time may be marginally longer.
Making the user wait a bit is preferable to dropping a frame
because the margin of error is smaller for dropped frames:
dropping a frame happens in milliseconds,
while you have &lt;em&gt;hundreds&lt;/em&gt; of milliseconds before a user perceives wait time.&lt;/p&gt;
&lt;p&gt;Because of the unpredictability of performance across devices,
the goal of OMT architecture is really about &lt;strong&gt;reducing risk&lt;/strong&gt;—making
your app more robust in the face of highly variable runtime conditions—not
about the performance benefits of parallelization.
The increase in resilience and the improvements
to UX are more than worth any small tradeoff in speed.&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; Developers are sometimes concerned about the cost of copying complex objects across the main thread and web workers. There&#39;s more detail in the talk, but, in general, you shouldn&#39;t break your performance budget if your object&#39;s stringified JSON representation is less than 10 KB. If you need to copy larger objects, consider using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer&quot;&gt;ArrayBuffer&lt;/a&gt; or &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt;. You can read more about this issue in &lt;a href=&quot;https://dassur.ma/things/is-postmessage-slow&quot;&gt;this blog post about &lt;code&gt;postMessage&lt;/code&gt; performance&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;a-note-about-tooling&quot;&gt;A note about tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#a-note-about-tooling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web workers aren&#39;t yet mainstream,
so most module tools—like &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;WebPack&lt;/a&gt;
and &lt;a href=&quot;https://github.com/rollup/rollup&quot; rel=&quot;noopener&quot;&gt;Rollup&lt;/a&gt;—don&#39;t support them out of the box.
(&lt;a href=&quot;https://parceljs.org/&quot; rel=&quot;noopener&quot;&gt;Parcel&lt;/a&gt; does though!)
Luckily, there are plugins to make web workers, well, &lt;em&gt;work&lt;/em&gt; with WebPack and Rollup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/worker-plugin&quot; rel=&quot;noopener&quot;&gt;worker-plugin&lt;/a&gt; for WebPack&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/surma/rollup-plugin-off-main-thread&quot; rel=&quot;noopener&quot;&gt;rollup-plugin-off-main-thread&lt;/a&gt; for Rollup&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing up &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/off-main-thread/#summing-up&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To make sure our apps are as reliable and accessible as possible, especially in an increasingly globalized marketplace, we need to support constrained devices—they&#39;re how most users are accessing the web globally. OMT offers a promising way to increase performance on such devices without adversely affecting users of high-end devices.&lt;/p&gt;
&lt;p&gt;Also, OMT has secondary benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It moves JavaScript execution costs to a separate thread.&lt;/li&gt;
&lt;li&gt;It moves &lt;em&gt;parsing&lt;/em&gt; costs, meaning UI might boot up faster.
That might reduce &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint&lt;/a&gt;
or even &lt;a href=&quot;https://web.dev/tti/&quot;&gt;Time to Interactive&lt;/a&gt;,
which can in turn increase your
&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; score.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Web workers don&#39;t have to be scary.
Tools like Comlink are taking the work out of workers
and making them a viable choice for a wide range of web applications.&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/@jimmyp9751&quot; rel=&quot;noopener&quot;&gt;James Peacock&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Techniques to make a web app load fast, even on a feature phone</title>
    <link href="https://web.dev/load-faster-like-proxx/"/>
    <updated>2019-09-23T00:00:00Z</updated>
    <id>https://web.dev/load-faster-like-proxx/</id>
    <content type="html" mode="escaped">&lt;p&gt;At Google I/O 2019 Mariko, Jake, and I shipped &lt;a href=&quot;https://proxx.app/&quot; rel=&quot;noopener&quot;&gt;PROXX&lt;/a&gt;, a modern Minesweeper-clone for the web. Something that sets PROXX apart is the focus on accessibility (you can play it with a screenreader!) and the ability to run as well on a feature phone as on a high-end desktop device. Feature phones are constrained in multiple ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Weak CPUs&lt;/li&gt;
&lt;li&gt;Weak or non-existent GPUs&lt;/li&gt;
&lt;li&gt;Small screens without touch input&lt;/li&gt;
&lt;li&gt;Very limited amounts of memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But they run a modern browser and are very affordable. For this reason, feature phones are making a resurgence in emerging markets. Their price point allows a whole new audience, who previously couldn&#39;t afford it, to come online and make use of the modern web. &lt;strong&gt;&lt;a href=&quot;https://www.counterpointresearch.com/more-than-a-billion-feature-phones-to-be-sold-over-next-three-years/&quot; rel=&quot;noopener&quot;&gt;For 2019 it is projected that around 400 million feature phones will be sold in India alone&lt;/a&gt;&lt;/strong&gt;, so users on feature phones might become a significant portion of your audience. In addition to that, connection speeds akin to 2G are the norm in emerging markets. How did we manage to make PROXX work well under feature phone conditions?&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FuAyD4tBgrjLsbXuFs5l.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/0Z2YqHWp5ToNzqlU40ng.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    PROXX gameplay.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Performance is important, and that includes both loading performance and runtime performance. It has been shown that &lt;strong&gt;good performance correlates with increased user retention, improved conversions and—most importantly—increased inclusivity.&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/malchata&quot; rel=&quot;noopener&quot;&gt;Jeremy Wagner&lt;/a&gt; has much more data and insight on &lt;a href=&quot;https://web.dev/why-speed-matters/&quot;&gt;why performance matters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is part 1 of a two-part series. &lt;strong&gt;Part 1 focuses on loading performance&lt;/strong&gt;, and part 2 will focus on runtime performance.&lt;/p&gt;
&lt;h2 id=&quot;capturing-the-status-quo&quot;&gt;Capturing the status quo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#capturing-the-status-quo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Testing your loading performance on a &lt;em&gt;real&lt;/em&gt; device is critical. If you don&#39;t have a real device at hand, I recommend &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; (WPT), specifically the &lt;a href=&quot;https://webpagetest.org/easy&quot; rel=&quot;noopener&quot;&gt;&amp;quot;simple&amp;quot; setup&lt;/a&gt;. &lt;strong&gt;WPT runs a battery of loading tests on a &lt;em&gt;real&lt;/em&gt; device with an emulated 3G connection.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;3G is a good speed to measure. While you might be used to 4G, LTE or soon even 5G, the reality of mobile internet looks quite different. Maybe you&#39;re on a train, at a conference, at a concert, or on a flight. What you&#39;ll be experiencing there is most likely closer to 3G, and sometimes even worse.&lt;/p&gt;
&lt;p&gt;That being said, we&#39;re going to focus on 2G in this article because PROXX is explicitly targeting feature phones and emerging markets in its target audience. Once WebPageTest has run its test, you get a waterfall (similar to what you see in DevTools) as well as a filmstrip at the top. The film strip shows what your user sees while your app is loading. On 2G, the loading experience of the unoptimized version of PROXX is pretty bad:&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CuprSULvVI7IKyS35eCA.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/BXNCRVkyZeVHPWJ9WGcI.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    The filmstrip video shows what the user sees when PROXX is loading on a real, low-end device over an emulated 2G connection.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When loaded over 3G, the user sees 4 seconds of white nothingness. &lt;strong&gt;Over 2G the user sees absolutely nothing for over 8 seconds.&lt;/strong&gt; If you read &lt;a href=&quot;https://web.dev/why-speed-matters/&quot;&gt;why performance matters&lt;/a&gt; you know that we have now lost a good portion of our potential users due to impatience. The user needs to download all of the 62 KB of JavaScript for anything to appear on screen. The silver lining in this scenario is that the second anything appears on screen it is also interactive. Or is it?&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CwGKJEpvyPw9UmvJf3su.webp?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/&quot; rel=&quot;noopener&quot;&gt;First Meaningful Paint&lt;/a&gt; in the unoptimized version of PROXX is &lt;em&gt;technically&lt;/em&gt; &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/interactive/&quot; rel=&quot;noopener&quot;&gt;interactive&lt;/a&gt; but useless to the user.&lt;/p&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After about 62 KB of gzip&#39;d JS has been downloaded and the DOM has been generated, the user gets to see our app. The app is &lt;em&gt;technically&lt;/em&gt; interactive. Looking at the visual, however, shows a different reality. The web fonts are still loading in the background and until they are ready the user can see no text. While this state qualifies as a &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/&quot; rel=&quot;noopener&quot;&gt;First Meaningful Paint (FMP)&lt;/a&gt;, it surely does not qualify as properly &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/interactive/&quot; rel=&quot;noopener&quot;&gt;interactive&lt;/a&gt;, as the user can&#39;t tell what any of the inputs are about. It takes another second on 3G and 3 seconds on 2G until the app is ready to go. &lt;strong&gt;All in all, the app takes 6 seconds on 3G and 11 seconds on 2G to become interactive.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;waterfall-analysis&quot;&gt;Waterfall analysis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#waterfall-analysis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we know &lt;em&gt;what&lt;/em&gt; the user sees, we need to figure out the &lt;em&gt;why&lt;/em&gt;. For this we can look at the waterfall and analyze why resources are loading too late. In our 2G trace for PROXX we can see two major red flags:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There are multiple, multi-colored thin lines.&lt;/li&gt;
&lt;li&gt;JavaScript files form a chain. For example, the second resource only starts loading once the first resource is finished, and the third resource only starts when the second resource is finished.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;345&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vcd5JU5MJNr0IHyMMtAU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The waterfall gives insight into which resources are loading when and how long they take.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;reducing-connection-count&quot;&gt;Reducing connection count &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#reducing-connection-count&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each thin line (&lt;code&gt;dns&lt;/code&gt;, &lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;ssl&lt;/code&gt;) stands for the creation of a new HTTP connection. Setting up a new connection is costly as it takes around 1s on 3G and roughly 2.5s on 2G. In our waterfall we see a new connection for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Request #1: Our &lt;code&gt;index.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Request #5: The font styles from &lt;code&gt;fonts.googleapis.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Request #8: Google Analytics&lt;/li&gt;
&lt;li&gt;Request #9: A font file from &lt;code&gt;fonts.gstatic.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Request #14: The web app manifest&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new connection for &lt;code&gt;index.html&lt;/code&gt; is unavoidable. The browser &lt;em&gt;has&lt;/em&gt; to create a connection to our server to get the contents. The new connection for Google Analytics could be avoided by inlining something like &lt;a href=&quot;https://minimalanalytics.com/&quot; rel=&quot;noopener&quot;&gt;Minimal Analytics&lt;/a&gt;, but Google Analytics is not blocking our app from rendering or becoming interactive, so we don&#39;t really care about how fast it loads. Ideally, Google Analytics should be loaded in idle time, when everything else has already loaded. That way it won&#39;t take up bandwidth or processing power during the initial load. The new connection for the web app manifest is &lt;a href=&quot;https://fetch.spec.whatwg.org/#connections&quot; rel=&quot;noopener&quot;&gt;prescribed by the fetch spec&lt;/a&gt;, as the manifest has to be loaded over a non-credentialed connection. Again, the web app manifest doesn&#39;t block our app from rendering or becoming interactive, so we don&#39;t need to care that much.&lt;/p&gt;
&lt;p&gt;The two fonts and their styles, however, are a problem as they block rendering and also interactivity. If we look at the CSS that is delivered by &lt;code&gt;fonts.googleapis.com&lt;/code&gt;, it&#39;s just two &lt;code&gt;@font-face&lt;/code&gt; rules, one for each font. The font &lt;em&gt;styles&lt;/em&gt; are so small in fact, that we decided to inline it into our HTML, removing one unnecessary connection. To avoid the cost of the connection setup for the font &lt;em&gt;files&lt;/em&gt;, we can copy them to our own server.&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; &lt;strong&gt;Note:&lt;/strong&gt; Copying CSS or font files to your own server is okay when using &lt;a href=&quot;https://fonts.google.com/&quot;&gt;Google Fonts&lt;/a&gt;. Other font providers might have different rules. Please check with your font provider&#39;s terms of service! &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;parallelizing-loads&quot;&gt;Parallelizing loads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#parallelizing-loads&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Looking at the waterfall, we can see that once the first JavaScript file is done loading, new files start loading immediately. This is typical for module dependencies. Our main module probably has static imports, so the JavaScript cannot run until those imports are loaded. The important thing to realize here is that these kinds of dependencies are known at build time. We can make use of &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tags to make sure all dependencies start loading the second we receive our HTML.&lt;/p&gt;
&lt;h3 id=&quot;results&quot;&gt;Results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#results&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s take a look at what our changes have achieved. It&#39;s important to not change any other variables in our test setup that could skew the results, so we will be using &lt;a href=&quot;https://webpagetest.org/easy&quot; rel=&quot;noopener&quot;&gt;WebPageTest&#39;s simple setup&lt;/a&gt; for the rest of this article and look at the filmstrip:&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zjJ410ZPSr99njy4KMoh.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/v76UWV9zidMILuFlLpaX.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    We use WebPageTest&#39;s filmstrip to see what our changes have achieved.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;These changes reduced our TTI from 11 to 8.5&lt;/strong&gt;, which is roughly the 2.5s of connection setup time we aimed to remove. Well done us.&lt;/p&gt;
&lt;h2 id=&quot;prerendering&quot;&gt;Prerendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#prerendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While we just reduced our &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/interactive/&quot; rel=&quot;noopener&quot;&gt;TTI&lt;/a&gt;, we haven&#39;t really affected the eternally long white screen the user has to endure for 8.5 seconds. Arguably &lt;strong&gt;the biggest improvements for &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/&quot; rel=&quot;noopener&quot;&gt;FMP&lt;/a&gt; can be achieved by sending styled markup in your &lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt;. Common techniques to achieve this are prerendering and server-side rendering, which are closely related and are explained in &lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot;&gt;Rendering on the Web&lt;/a&gt;. Both techniques run the web app in Node and serialize the resulting DOM to HTML. Server-side rendering does this per request on the, well, server side, while prerendering does this at build time and stores the output as your new &lt;code&gt;index.html&lt;/code&gt;. Since PROXX is a &lt;a href=&quot;https://jamstack.org/&quot; rel=&quot;noopener&quot;&gt;JAMStack&lt;/a&gt; app and has no server side, we decided to implement prerendering.&lt;/p&gt;
&lt;p&gt;There are many ways to implement a prerenderer. In PROXX we chose to use &lt;a href=&quot;https://pptr.dev/&quot; rel=&quot;noopener&quot;&gt;Puppeteer&lt;/a&gt;, which starts Chrome without any UI and allows you to remote control that instance with a Node API. We use this to inject our markup and our JavaScript and then read back the DOM as a string of HTML. Because we are using &lt;a href=&quot;https://github.com/css-modules/css-modules&quot; rel=&quot;noopener&quot;&gt;CSS Modules&lt;/a&gt;, we get CSS inlining of the styles that we need for free.&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;const&lt;/span&gt; browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; puppeteer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&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;const&lt;/span&gt; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newPage&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;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rawIndexHTML&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;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;codeToRun&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;const&lt;/span&gt; renderedHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;content&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;  browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&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;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; renderedHTML&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;With this in place, we can expect an improvement for our FMP. We still need to load and execute the same amount of JavaScript as before, so we shouldn&#39;t expect TTI to change much. If anything, our &lt;code&gt;index.html&lt;/code&gt; has gotten bigger and might push back our TTI a bit. There&#39;s only one way to find out: running WebPageTest.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lzm3LUZs6FPr7hxZsWO8.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/kkHfcTZnTgdSAuWlYFfj.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    The filmstrip shows a clear improvement for our FMP metric. TTI is mostly unaffected.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Our First Meaningful Paint has moved from 8.5 seconds to 4.9 seconds,&lt;/strong&gt; a massive improvement. Our TTI still happens at around 8.5 seconds so it has been largely unaffected by this change. What we did here is a &lt;em&gt;perceptual&lt;/em&gt; change. Some might even call it a sleight of hand. By rendering an intermediate visual of the game, we are changing the perceived loading performance for the better.&lt;/p&gt;
&lt;h2 id=&quot;inlining&quot;&gt;Inlining &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#inlining&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another metric that both DevTools and WebPageTest give us is &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time To First Byte (TTFB)&lt;/a&gt;. This is the time it takes from the first byte of the request being sent to the first byte of the response being received. This time is also often called a Round Trip Time (RTT), although technically there is a difference between these two numbers: RTT does not include the processing time of the request on the server side. &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#timing-preview&quot; rel=&quot;noopener&quot;&gt;DevTools&lt;/a&gt; and WebPageTest visualize TTFB with a light color within the request/response block.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;171&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/J86O71iJ9OPjlginvwrp.svg&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The light section of a request signifies the request is waiting to receive the first byte of the response.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Looking at our waterfall, we can see that the &lt;strong&gt;all of requests spend the &lt;em&gt;majority&lt;/em&gt; of their time waiting&lt;/strong&gt; for the first byte of the response to arrive.&lt;/p&gt;
&lt;p&gt;This problem was what HTTP/2 Push was originally conceived for. The app developer &lt;em&gt;knows&lt;/em&gt; that certain resources are needed and can &lt;em&gt;push&lt;/em&gt; them down the wire. By the time the client realizes that it needs to fetch additional resources, they are already in the browser&#39;s caches. &lt;strong&gt;HTTP/2 Push turned out to be too hard to get right and is considered discouraged.&lt;/strong&gt; This problem space will be revisited during the standardization of HTTP/3. For now, &lt;strong&gt;the easiest solution is to &lt;em&gt;inline&lt;/em&gt; all the critical resources&lt;/strong&gt; at the expense of caching efficiency.&lt;/p&gt;
&lt;p&gt;Our critical CSS is already inlined thanks to CSS Modules and our Puppeteer-based prerenderer. For JavaScript we need to inline our critical modules &lt;em&gt;and their dependencies&lt;/em&gt;. This task has varying difficulty, based on the bundler that you&#39;re using.&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; &lt;strong&gt;Note:&lt;/strong&gt; In this step we also subset our font files to contain only the glyphs that we need for our landing page. I am not going to go into detail on this step as it is not easily abstracted and sometimes not even practical. We still load the full font files lazily, but they are not needed for the initial render. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lzm3LUZs6FPr7hxZsWO8.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/kkHfcTZnTgdSAuWlYFfj.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    With the inlining of our JavaScript we have reduced our TTI from 8.5s to 7.2s.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This shaved 1 second off our TTI. We have now reached the point where our &lt;code&gt;index.html&lt;/code&gt; contains everything that is needed for the initial render and becoming interactive. The HTML can render while it is still downloading, creating our FMP. The moment the HTML is done parsing and executing, the app is interactive.&lt;/p&gt;
&lt;h2 id=&quot;aggressive-code-splitting&quot;&gt;Aggressive code splitting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#aggressive-code-splitting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Yes, our &lt;code&gt;index.html&lt;/code&gt; contains everything that is needed to become interactive. But on closer inspection it turns out it also contains everything else. Our &lt;code&gt;index.html&lt;/code&gt; is around 43 KB. Let&#39;s put that in relation to what the user can interact with at the start: We have a form to configure the game containing a couple of components, a start button and probably some code to persist and load user settings. That&#39;s pretty much it. 43 KB seems like a lot.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PDjREt9PrWz9oqayT3CE.webp?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The landing page of PROXX. Only critical components are used here.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To understand where our bundle size is coming from we can use a &lt;a href=&quot;https://npm.im/source-map-explorer&quot; rel=&quot;noopener&quot;&gt;source map explorer&lt;/a&gt; or a similar tool to break down what the bundle consists of. As predicted, our bundle contains the game logic, the rendering engine, the win screen, the lose screen and a bunch of utilities. Only a small subset of these modules are needed for the landing page. Moving everything that is not strictly required for interactivity into a lazily-loaded module will decrease TTI &lt;em&gt;significantly&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;700&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4j3GRcHylDnIuwhH8iKT.svg&quot; width=&quot;700&quot; /&gt;
  &lt;figcaption&gt;
    Analyzing the contents of PROXX&#39;s `index.html` shows a lot of unneeded resources. Critical resources are highlighted.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;What we need to do is &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code split&lt;/a&gt;. Code splitting breaks apart your monolithic bundle into smaller parts that can be lazy-loaded on-demand. Popular bundlers like &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;Webpack&lt;/a&gt;, &lt;a href=&quot;https://rollupjs.org/&quot; rel=&quot;noopener&quot;&gt;Rollup&lt;/a&gt;, and &lt;a href=&quot;https://parceljs.org/&quot; rel=&quot;noopener&quot;&gt;Parcel&lt;/a&gt; support code splitting by using dynamic &lt;code&gt;import()&lt;/code&gt;. The bundler will analyze your code and &lt;em&gt;inline&lt;/em&gt; all modules that are imported &lt;em&gt;statically&lt;/em&gt;. Everything that you import &lt;em&gt;dynamically&lt;/em&gt; will be put into its own file and will only be fetched from the network once the &lt;code&gt;import()&lt;/code&gt; call gets executed. Of course hitting the network has a cost and should only be done if you have the time to spare. &lt;strong&gt;The mantra here is to statically import the modules that are &lt;em&gt;critically&lt;/em&gt; needed at load time and dynamically load everything else.&lt;/strong&gt; But you shouldn&#39;t wait to the very last moment to lazy-load modules that are definitely going to be used. &lt;a href=&quot;https://twitter.com/philwalton&quot; rel=&quot;noopener&quot;&gt;Phil Walton&lt;/a&gt;&#39;s &lt;a href=&quot;https://philipwalton.com/articles/idle-until-urgent/&quot; rel=&quot;noopener&quot;&gt;Idle Until Urgent&lt;/a&gt; is a great pattern for a healthy middle ground between lazy loading and eager loading.&lt;/p&gt;
&lt;p&gt;In PROXX we created a &lt;code&gt;lazy.js&lt;/code&gt; file that statically imports everything that we &lt;em&gt;don&#39;t&lt;/em&gt; need. In our main file, we can then &lt;em&gt;dynamically&lt;/em&gt; import &lt;code&gt;lazy.js&lt;/code&gt;. However, some of our &lt;a href=&quot;https://preactjs.com/&quot; rel=&quot;noopener&quot;&gt;Preact&lt;/a&gt; components ended up in &lt;code&gt;lazy.js&lt;/code&gt;, which turned out to be a bit of a complication as Preact can&#39;t handle lazily-loaded components out of the box. For this reason we wrote a little &lt;code&gt;deferred&lt;/code&gt; component wrapper that allows us to render a placeholder until the actual component has loaded.&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;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deferred&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;componentPromise&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; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Deferred&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&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;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&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;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &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;LoadedComponent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&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;      componentPromise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&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 literal-property property&quot;&gt;LoadedComponent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; component &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 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 function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; loaded&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loading &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; LoadedComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LoadedComponent&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; &lt;span class=&quot;token function&quot;&gt;loaded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LoadedComponent&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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loading&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;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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With this in place, we can use a Promise of a component in our &lt;code&gt;render()&lt;/code&gt; functions. For example, the &lt;code&gt;&amp;lt;Nebula&amp;gt;&lt;/code&gt; component, which renders the animated background image, will be replaced by an empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; while the component is loading. Once the component is loaded and ready to use, the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; will be replaced with the actual component.&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;const&lt;/span&gt; NebulaDeferred &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deferred&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;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/components/nebula&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;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&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 keyword&quot;&gt;return&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;// ...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;NebulaDeferred&lt;br /&gt;    loading&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    loaded&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 parameter&quot;&gt;Nebula&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Nebula &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With all of this in place, we reduced our &lt;code&gt;index.html&lt;/code&gt; to a mere 20 KB, less than half of the original size. What effect does this have on FMP and TTI? WebPageTest will tell!&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/d2y6ZrlYkcfHTsmaP4m5.jpg?auto=format&quot; preload=&quot;metadata&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/byNSMGzFX0aSXRBmI1HM.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    The filmstrip confirms: Our TTI is now at 5.4s. A drastic improvement from our original 11s.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Our FMP and TTI are only 100ms apart, as it is only a matter of parsing and executing the inlined JavaScript. After just 5.4s on 2G, the app is completely interactive. All the other, less essential modules are loaded in the background.&lt;/p&gt;
&lt;h2 id=&quot;more-sleight-of-hand&quot;&gt;More Sleight of Hand &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#more-sleight-of-hand&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you look at our list of critical modules above, you&#39;ll see that the rendering engine is not part of the critical modules. Of course, the game cannot start until we have our rendering engine to render the game. We could disable the &amp;quot;Start&amp;quot; button until our rendering engine is ready to start the game, but in our experience the user usually takes long enough to configure their game settings that this isn&#39;t necessary. Most of the time the rendering engine and the other remaining modules are done loading by the time the user presses &amp;quot;Start&amp;quot;. In the rare case that the user is quicker than their network connection, we show a simple loading screen that waits for the remaining modules to finish.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/load-faster-like-proxx/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Measuring is important. To avoid spending time on problems that are not real, we recommend to always measure first before implementing optimizations. Additionally, measurements should be done on &lt;em&gt;real&lt;/em&gt; devices on a 3G connection or on &lt;a href=&quot;https://webpagetest.org/easy&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; if no real device is at hand.&lt;/p&gt;
&lt;p&gt;The filmstrip can give insight into how loading your app &lt;em&gt;feels&lt;/em&gt; for the user. The waterfall can tell you what resources are responsible for potentially long loading times. Here&#39;s a checklist of things you can do to improve loading performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deliver as many assets as possible over one connection.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/preload-critical-assets&quot;&gt;Preload&lt;/a&gt; or even inline resources that are required for the first render and interactivity.&lt;/li&gt;
&lt;li&gt;Prerender your app to improve perceived loading performance.&lt;/li&gt;
&lt;li&gt;Make use of aggressive &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code splitting&lt;/a&gt; to reduce the amount of code needed for interactivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned for part 2 where we discuss how to optimize runtime performance on hyper-constrained devices.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Emscripten and npm</title>
    <link href="https://web.dev/emscripten-npm/"/>
    <updated>2019-01-15T00:00:00Z</updated>
    <id>https://web.dev/emscripten-npm/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/emscripting-a-c-library/&quot;&gt;WebAssembly&lt;/a&gt; (wasm) is often
framed as either a performance primitive or a way to run your existing C++
codebase on the web. With &lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;squoosh.app&lt;/a&gt;, we wanted to show
that there is at least a third perspective for wasm: making use of the huge
ecosystems of other programming languages. With
&lt;a href=&quot;https://kripken.github.io/emscripten-site/&quot; rel=&quot;noopener&quot;&gt;Emscripten&lt;/a&gt;, you can use C/C++ code,
&lt;a href=&quot;https://rustwasm.github.io/book/&quot; rel=&quot;noopener&quot;&gt;Rust has wasm support&lt;/a&gt; built in, and the &lt;a href=&quot;https://github.com/golang/go/wiki/WebAssembly&quot; rel=&quot;noopener&quot;&gt;Go
team is working on it&lt;/a&gt;, too. I&#39;m
sure many other languages will follow.&lt;/p&gt;
&lt;p&gt;In these scenarios, wasm is not the centerpiece of your app, but rather a puzzle
piece: yet another module. Your app already has JavaScript, CSS, image assets, a
web-centric build system and maybe even a framework like React. How do you
integrate WebAssembly into this setup? In this article we are going to work this
out with C/C++ and Emscripten as an example.&lt;/p&gt;
&lt;h2 id=&quot;docker&quot;&gt;Docker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#docker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Note: While I will be using Docker, you don&#39;t need a deep understanding of
Docker to follow this article. If you have Docker installed on your machine, you
are good to go!&lt;/p&gt;
&lt;p&gt;I have found Docker to be invaluable when working with Emscripten. C/C++
libraries are often written to work with the operating system they are built on.
It is incredibly helpful to have a consistent environment. With Docker you get a
virtualized Linux system that is already set up to work with Emscripten and has
all the tools and dependencies installed. If something is missing, you can just
install it without having to worry about how it affects your own machine or your
other projects. If something goes wrong, throw the container away and start
over. If it works once, you can be sure that it will continue to work and
produce identical results.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://hub.docker.com/&quot; rel=&quot;noopener&quot;&gt;Docker Registry&lt;/a&gt; has an &lt;a href=&quot;https://hub.docker.com/r/trzeci/emscripten/&quot; rel=&quot;noopener&quot;&gt;Emscripten
image&lt;/a&gt; by
&lt;a href=&quot;https://github.com/trzecieu/&quot; rel=&quot;noopener&quot;&gt;trzeci&lt;/a&gt; that I have been using extensively.&lt;/p&gt;
&lt;h2 id=&quot;integration-with-npm&quot;&gt;Integration with npm &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#integration-with-npm&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the majority of cases, the entry point to a web project is npm&#39;s
&lt;code&gt;package.json&lt;/code&gt;. By convention, most projects can be built with &lt;code&gt;npm install &amp;amp;&amp;amp; npm run build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In general, the build artifacts produced by Emscripten (a &lt;code&gt;.js&lt;/code&gt; and a &lt;code&gt;.wasm&lt;/code&gt;
file) should be treated as just another JavaScript module and just another
asset. The JavaScript file can be handled by a bundler like webpack or rollup,
and the wasm file should be treated like any other bigger binary asset, like
images.&lt;/p&gt;
&lt;p&gt;As such, the Emscripten build artifacts need to be built before your &amp;quot;normal&amp;quot;
build process kicks in:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-worldchanging-project&quot;&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;&quot;scripts&quot;&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 property&quot;&gt;&quot;build:emscripten&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &quot;docker run --rm -v $(pwd)&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;/src trzeci/emscripten&lt;br /&gt;./build.sh&quot;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;build:app&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;the old build command&gt;&quot;&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;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build:emscripten &amp;amp;&amp;amp; npm run build:app&quot;&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;// ...&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;span class=&quot;token comment&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 new &lt;code&gt;build:emscripten&lt;/code&gt; task could invoke Emscripten directly, but as
mentioned before, I recommend using Docker to make sure the build environment is
consistent.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run ... trzeci/emscripten ./build.sh&lt;/code&gt; tells Docker to spin up a new
container using the &lt;code&gt;trzeci/emscripten&lt;/code&gt; image and run the &lt;code&gt;./build.sh&lt;/code&gt; command.
&lt;code&gt;build.sh&lt;/code&gt; is a shell script that you are going to write next! &lt;code&gt;--rm&lt;/code&gt; tells
Docker to delete the container after it&#39;s done running. This way, you don&#39;t build
up a collection of stale machine images over time. &lt;code&gt;-v $(pwd):/src&lt;/code&gt; means that
you want Docker to &amp;quot;mirror&amp;quot; the current directory (&lt;code&gt;$(pwd)&lt;/code&gt;) to &lt;code&gt;/src&lt;/code&gt; inside
the container. Any changes you make to files in the &lt;code&gt;/src&lt;/code&gt; directory inside the
container will be mirrored to your actual project. These mirrored directories
are called &amp;quot;bind mounts&amp;quot;.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look at &lt;code&gt;build.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; -e&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;OPTIMIZE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-Os&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;LDFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${OPTIMIZE}&lt;/span&gt;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${OPTIMIZE}&lt;/span&gt;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CXXFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${OPTIMIZE}&lt;/span&gt;&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Compiling wasm bindings&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&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;# Compile C/C++ code&lt;/span&gt;&lt;br /&gt;    emcc &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token variable&quot;&gt;${OPTIMIZE}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    --bind &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    -s &lt;span class=&quot;token assign-left variable&quot;&gt;STRICT&lt;/span&gt;&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;    -s &lt;span class=&quot;token assign-left variable&quot;&gt;ALLOW_MEMORY_GROWTH&lt;/span&gt;&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;    -s &lt;span class=&quot;token assign-left variable&quot;&gt;MALLOC&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;emmalloc &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    -s &lt;span class=&quot;token assign-left variable&quot;&gt;MODULARIZE&lt;/span&gt;&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;    -s &lt;span class=&quot;token assign-left variable&quot;&gt;EXPORT_ES6&lt;/span&gt;&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;    -o ./my-module.js &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    src/my-module.cpp&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# Create output folder&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; -p dist&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# Move artifacts&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; my-module.&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;js,wasm&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; dist&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Compiling wasm bindings done&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There&#39;s a lot to dissect here!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;set -e&lt;/code&gt; puts the shell into &amp;quot;fail fast&amp;quot; mode. If any commands in the script
return an error, the entire script gets aborted immediately. This can be
incredibly helpful as the last output of the script will always be a success
message or the error that caused the build to fail.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;export&lt;/code&gt; statements, you define the values of a couple of environment
variables. They allow you to pass additional command-line parameters to the C
compiler (&lt;code&gt;CFLAGS&lt;/code&gt;), the C++ compiler (&lt;code&gt;CXXFLAGS&lt;/code&gt;), and the linker (&lt;code&gt;LDFLAGS&lt;/code&gt;).
They all receive the optimizer settings via &lt;code&gt;OPTIMIZE&lt;/code&gt; to make sure that
everything gets optimized the same way. There are a couple of possible values
for the &lt;code&gt;OPTIMIZE&lt;/code&gt; variable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-O0&lt;/code&gt;: Don&#39;t do any optimization. No dead code is eliminated, and Emscripten
does not minify the JavaScript code it emits, either. Good for debugging.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-O3&lt;/code&gt;: Optimize aggressively for performance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-Os&lt;/code&gt;: Optimize aggressively for performance and size as a secondary
criterion.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-Oz&lt;/code&gt;: Optimize aggressively for size, sacrificing performance if necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the web, I mostly recommend &lt;code&gt;-Os&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;emcc&lt;/code&gt; command has a myriad of options of its own. Note that emcc is
supposed to be a  &amp;quot;drop-in replacement for compilers like GCC or clang&amp;quot;. So all
flags that you might know from GCC will most likely be implemented by emcc as
well. The &lt;code&gt;-s&lt;/code&gt; flag is special in that it allows us to configure Emscripten
specifically. All available options can be found in Emscripten&#39;s
&lt;a href=&quot;https://github.com/kripken/emscripten/blob/incoming/src/settings.js&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;settings.js&lt;/code&gt;&lt;/a&gt;,
but that file can be quite overwhelming. Here&#39;s a list of the Emscripten flags
that I think are most important for web developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--bind&lt;/code&gt; enables
&lt;a href=&quot;https://web.dev/embind/&quot;&gt;embind&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s STRICT=1&lt;/code&gt; drops support for all deprecated build options. This ensures
that your code builds in a forward compatible manner.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s ALLOW_MEMORY_GROWTH=1&lt;/code&gt; allows memory to be automatically grown if
necessary. At the time of writing, Emscripten will allocate 16MB of memory
initially. As your code allocates chunks of memory, this option decides if
these operations will make the entire wasm module fail when memory is
exhausted, or if the glue code is allowed to expand the total memory to
accommodate the allocation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s MALLOC=...&lt;/code&gt; chooses which &lt;code&gt;malloc()&lt;/code&gt; implementation to use. &lt;code&gt;emmalloc&lt;/code&gt; is
a small and fast &lt;code&gt;malloc()&lt;/code&gt; implementation specifically for Emscripten. The
alternative is &lt;code&gt;dlmalloc&lt;/code&gt;, a fully-fledged &lt;code&gt;malloc()&lt;/code&gt; implementation. You only
need to switch to &lt;code&gt;dlmalloc&lt;/code&gt; if you are allocating a lot of small objects
frequently or if you want to use threading.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s EXPORT_ES6=1&lt;/code&gt; will turn the JavaScript code into an ES6 module with a
default export that works with any bundler. Also requires &lt;code&gt;-s MODULARIZE=1&lt;/code&gt; to
be set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following flags are not always necessary or are only helpful for debugging
purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-s FILESYSTEM=0&lt;/code&gt; is a flag that relates to Emscripten and it&#39;s ability to
emulate a filesystem for you when your C/C++ code uses filesystem operations.
It does some analysis on the code it compiles to decide whether to include the
filesystem emulation in the glue code or not. Sometimes, however, this
analysis can get it wrong and you pay a rather hefty 70kB in additional glue
code for a filesystem emulation that you might not need. With &lt;code&gt;-s FILESYSTEM=0&lt;/code&gt; you can force Emscripten to not include this code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-g4&lt;/code&gt; will make Emscripten include debugging information in the &lt;code&gt;.wasm&lt;/code&gt; and
also emit a source maps file for the wasm module. You can read more on
debugging with Emscripten in their &lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/porting/Debugging.html&quot; rel=&quot;noopener&quot;&gt;debugging
section&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And there you go! To test this setup, let&#39;s whip up a tiny &lt;code&gt;my-module.cpp&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;    &lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;emscripten/bind.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; emscripten&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;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;say_hello&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;printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello from your wasm module\n&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;      &lt;span class=&quot;token keyword&quot;&gt;return&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;EMSCRIPTEN_BINDINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;my_module&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;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sayHello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;say_hello&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 an &lt;code&gt;index.html&lt;/code&gt;:&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 doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Emscripten + npm example&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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    Open the console to see the output from the wasm module.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;module&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 class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; wasmModule &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./my-module.js&quot;&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;const&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;wasmModule&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;onRuntimeInitialized&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;        instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sayHello&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;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;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;(Here is a
&lt;a href=&quot;https://gist.github.com/surma/9899231095ada390b2b178a72ff57aa3&quot; rel=&quot;noopener&quot;&gt;gist&lt;/a&gt;
containing all files.)&lt;/p&gt;
&lt;p&gt;To build everything, run&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;br /&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build&lt;br /&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run serve&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Navigating to localhost:8080 should show you the following output in the
DevTools console:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools showing a message printed via C++ and Emscripten.&quot; decoding=&quot;async&quot; height=&quot;254&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/F93NAa7jgyEBkpHGm6Og.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;adding-cc-code-as-a-dependency&quot;&gt;Adding C/C++ code as a dependency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#adding-cc-code-as-a-dependency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to build a C/C++ library for your web app, you need its code to be
part of your project. You can add the code to your project&#39;s repository manually
or you can use npm to manage these kind of dependencies as well. Let&#39;s say I
want to use &lt;a href=&quot;https://github.com/webmproject/libvpx&quot; rel=&quot;noopener&quot;&gt;libvpx&lt;/a&gt; in my webapp. libvpx
is a C++ library to encode images with VP8, the codec used in &lt;code&gt;.webm&lt;/code&gt; files.
However, libvpx is not on npm and doesn&#39;t have a &lt;code&gt;package.json&lt;/code&gt;, so I can&#39;t
install it using npm directly.&lt;/p&gt;
&lt;p&gt;To get out of this conundrum, there is
&lt;a href=&quot;https://www.npmjs.com/package/napa&quot; rel=&quot;noopener&quot;&gt;napa&lt;/a&gt;. napa allows you to install any git
repository URL as a dependency into your &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;Note: If you dislike using napa, please take a look at the appendix for a more
Docker-centric solution that doesn&#39;t require napa.&lt;/p&gt;
&lt;p&gt;Install napa as a dependency:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save napa&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;and make sure to run &lt;code&gt;napa&lt;/code&gt; as an install script:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&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 property&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;napa&quot;&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;// ...&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;span class=&quot;token property&quot;&gt;&quot;napa&quot;&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 property&quot;&gt;&quot;libvpx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;git+https://github.com/webmproject/libvpx&quot;&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;// ...&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;When you run &lt;code&gt;npm install&lt;/code&gt;, napa takes care of cloning the libvpx GitHub
repository into your &lt;code&gt;node_modules&lt;/code&gt; under the name &lt;code&gt;libvpx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can now extend your build script to build libvpx. libvpx uses &lt;code&gt;configure&lt;/code&gt;
and &lt;code&gt;make&lt;/code&gt; to be built. Luckily, Emscripten can help ensure that &lt;code&gt;configure&lt;/code&gt; and
&lt;code&gt;make&lt;/code&gt; use Emscripten&#39;s compiler. For this purpose there are the wrapper
commands &lt;code&gt;emconfigure&lt;/code&gt; and &lt;code&gt;emmake&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&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; above is unchanged &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;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling libvpx&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;rf build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    mkdir build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx&lt;br /&gt;    cd build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx&lt;br /&gt;    emconfigure &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;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libvpx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;configure \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;target&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;generic&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;gnu&lt;br /&gt;    emmake make&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling libvpx done&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling wasm bindings&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&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; below is unchanged &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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Some projects provide a &lt;code&gt;--host&lt;/code&gt; flag (or similar; libvpx uses non-standard &lt;code&gt;--target&lt;/code&gt;) to build for a specific processor architecture. This will often pull in assembler code that takes advantage of features specific to that architecture and can&#39;t be compiled to WebAssembly. If a flag like that is present (check with &lt;code&gt;./configure --help&lt;/code&gt;), make sure to set it to a generic target. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;A C/C++ library is split into two parts: the headers (traditionally &lt;code&gt;.h&lt;/code&gt; or
&lt;code&gt;.hpp&lt;/code&gt; files) that define the data structures, classes, constants etc. that a
library exposes and the actual library (traditionally &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.a&lt;/code&gt; files). To
use the &lt;code&gt;VPX_CODEC_ABI_VERSION&lt;/code&gt; constant of the library in your code, you have
to include the library&#39;s header files using a &lt;code&gt;#include&lt;/code&gt; statement:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vpxenc.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;emscripten/bind.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;say_hello&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;printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello from your wasm module with libvpx %d\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; VPX_CODEC_ABI_VERSION&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; &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The problem is that the compiler doesn&#39;t know &lt;em&gt;where&lt;/em&gt; to look for &lt;code&gt;vpxenc.h&lt;/code&gt;.
This is what the &lt;code&gt;-I&lt;/code&gt; flag is for. It tells the compiler which directories to
check for header files. Additionally, you also need to give the compiler the
actual library file:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&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; above is unchanged &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;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling wasm bindings&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&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 macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;Compile C&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; code&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    emcc \&lt;br /&gt;    $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;OPTIMIZE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;bind \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s STRICT&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s ALLOW_MEMORY_GROWTH&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s ASSERTIONS&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 operator&quot;&gt;-&lt;/span&gt;s MALLOC&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;emmalloc \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s MODULARIZE&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s EXPORT_ES6&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;o &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;I &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libvpx \&lt;br /&gt;    src&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpp \&lt;br /&gt;    build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libvpx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;a&lt;br /&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; below is unchanged &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;If you run &lt;code&gt;npm run build&lt;/code&gt; now, you will see that the process builds a new &lt;code&gt;.js&lt;/code&gt;
and a new &lt;code&gt;.wasm&lt;/code&gt; file and that the demo page will indeed output the constant:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools showing a the ABI version of libvpx printed via emscripten.&quot; decoding=&quot;async&quot; height=&quot;205&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/yGjXdGrEinLlydSXUnu0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;You will also notice that the build process takes a long time. The reason for
long build times can vary. In the case of libvpx, it takes a long time because
it compiles an encoder and a decoder for both VP8 and VP9 every time you run
your build command, even though the source files haven&#39;t changed. Even a small
change to your &lt;code&gt;my-module.cpp&lt;/code&gt; will take a long time to build. It would be very
beneficial to keep the build artifacts of libvpx around once they have been
built the first time.&lt;/p&gt;
&lt;p&gt;One way to achieve this is using environment variables.&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 dislike this solution, please take a look at the appendix for a more Docker-centric solution. &lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&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; above is unchanged &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;eval $@&lt;br /&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling libvpx&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;test &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;n &lt;span class=&quot;token string&quot;&gt;&quot;$SKIP_LIBVPX&quot;&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;    rm &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;rf build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    mkdir build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx&lt;br /&gt;    cd build&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vpx&lt;br /&gt;    emconfigure &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;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;libvpx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;configure \&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;target&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;generic&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;gnu&lt;br /&gt;    emmake make&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;Compiling libvpx done&quot;&lt;/span&gt;&lt;br /&gt;echo &lt;span class=&quot;token string&quot;&gt;&quot;=============================================&quot;&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; below is unchanged &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;(Here&#39;s a &lt;a href=&quot;https://gist.github.com/surma/8884a1e66b006c48e1ecc57bd1f36011&quot; rel=&quot;noopener&quot;&gt;gist&lt;/a&gt;
containing all the files.)&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;eval&lt;/code&gt; command allows us to set environment variables by passing parameters
to the build script. The &lt;code&gt;test&lt;/code&gt; command will skip building libvpx if
&lt;code&gt;$SKIP_LIBVPX&lt;/code&gt; is set (to any value).&lt;/p&gt;
&lt;p&gt;Now you can compile your module but skip rebuilding libvpx:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build:emscripten -- &lt;span class=&quot;token assign-left variable&quot;&gt;SKIP_LIBVPX&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;customizing-the-build-environment&quot;&gt;Customizing the build environment &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#customizing-the-build-environment&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes libraries depend on additional tools to build. If these dependencies
are missing in the build environment provided by the Docker image, you need to
add them yourself. As an example, let&#39;s say you also want to build the
documentation of libvpx using &lt;a href=&quot;http://www.doxygen.nl/&quot; rel=&quot;noopener&quot;&gt;doxygen&lt;/a&gt;. Doxygen is not
available inside your Docker container, but you can install it using &lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you were to do that in your &lt;code&gt;build.sh&lt;/code&gt;, you would re-download and re-install
doxygen everytime you want to build your library. Not only would that be
wasteful, but it would also stop you from working on your project while offline.&lt;/p&gt;
&lt;p&gt;Here it makes sense to build your own Docker image. Docker images are built by
writing a &lt;code&gt;Dockerfile&lt;/code&gt; that describes the build steps. Dockerfiles are quite
powerful and have &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot; rel=&quot;noopener&quot;&gt;a lot of
commands&lt;/a&gt;, but most of the
time you can get away with just using &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt;. In this case:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-docker&quot;&gt;&lt;code class=&quot;language-docker&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trzeci/emscripten&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    apt-get install -qqy doxygen&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With &lt;code&gt;FROM&lt;/code&gt;, you can declare which Docker image you want to use as a starting
point. I chose &lt;code&gt;trzeci/emscripten&lt;/code&gt; as a basis — the image you have been using
all along. With &lt;code&gt;RUN&lt;/code&gt;, you instruct Docker to run shell commands inside the
container. Whatever changes these commands make to the container is now part of
the Docker image. To make sure that your Docker image has been built and is
available before you run &lt;code&gt;build.sh&lt;/code&gt;, you have to adjust your &lt;code&gt;package.json&lt;/code&gt; a
bit:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&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 property&quot;&gt;&quot;build:dockerimage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;docker image inspect -f &#39;.&#39; mydockerimage || docker build -t mydockerimage .&quot;&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;&quot;build:emscripten&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;docker run --rm -v $(pwd):/src mydockerimage ./build.sh&quot;&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;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build:dockerimage &amp;amp;&amp;amp; npm run build:emscripten &amp;amp;&amp;amp; npm run build:app&quot;&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;// ...&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;span class=&quot;token comment&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;(Here&#39;s a &lt;a href=&quot;https://gist.github.com/surma/71d42a1c0c553d7a63ad8d4cddd61972&quot; rel=&quot;noopener&quot;&gt;gist&lt;/a&gt;
containing all the files.)&lt;/p&gt;
&lt;p&gt;This will build your Docker image, but only if it has not been built yet. Then
everything runs as before, but now the build environment has the &lt;code&gt;doxygen&lt;/code&gt;
command available, which will cause the documentation of libvpx to be built as
well.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is not surprising that C/C++ code and npm are not a natural fit, but you can
make it work quite comfortably with some additional tooling and the isolation
that Docker provides. This setup will not work for every project, but it&#39;s a
decent starting point that you can adjust for your needs. If you have
improvements, please share.&lt;/p&gt;
&lt;h2 id=&quot;appendix-making-use-of-docker-image-layers&quot;&gt;Appendix: Making use of Docker image layers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripten-npm/#appendix-making-use-of-docker-image-layers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An alternative solution is to encapsulate more of these problems with Docker and
Docker&#39;s smart approach to caching. Docker executes Dockerfiles step-by-step and
assigns the result of each step an image of it&#39;s own. These intermediate images
are often called &amp;quot;layers&amp;quot;. If a command in a Dockerfile hasn&#39;t changed, Docker
won&#39;t actually re-run that step when you are re-building the Dockerfile. Instead
it reuses the layer from the last time the image was built.&lt;/p&gt;
&lt;p&gt;Previously, you had to go through some effort to not rebuild libvpx every time
you build your app. Instead you can move the building instructions for libvpx
from your &lt;code&gt;build.sh&lt;/code&gt; into the &lt;code&gt;Dockerfile&lt;/code&gt; to make use of Docker&#39;s caching
mechanism:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-docker&quot;&gt;&lt;code class=&quot;language-docker&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; trzeci/emscripten&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    apt-get install -qqy doxygen git &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    mkdir -p /opt/libvpx/build &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    git clone https://github.com/webmproject/libvpx /opt/libvpx/src&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; cd /opt/libvpx/build &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    emconfigure ../src/configure --target=generic-gnu &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    emmake make&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;(Here&#39;s a &lt;a href=&quot;https://gist.github.com/surma/c1b69df0aa168eee6ef08812123e0bbd&quot; rel=&quot;noopener&quot;&gt;gist&lt;/a&gt;
containing all the files.)&lt;/p&gt;
&lt;p&gt;Note that you need to manually install git and clone libvpx as you don&#39;t have
bind mounts when running &lt;code&gt;docker build&lt;/code&gt;. As a side-effect, there is no need for
napa anymore.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Emscripten’s embind</title>
    <link href="https://web.dev/embind/"/>
    <updated>2018-08-20T00:00:00Z</updated>
    <id>https://web.dev/embind/</id>
    <content type="html" mode="escaped">&lt;p&gt;In my last &lt;a href=&quot;https://web.dev/emscripting-a-c-library/&quot;&gt;wasm article&lt;/a&gt;, I talked
about how to compile a C library to wasm so that you can use it on the web. One thing
that stood out to me (and to many readers) is the crude and slightly awkward way
you have to manually declare which functions of your wasm module you are using.
To refresh your mind, this is the code snippet I am talking about:&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;const&lt;/span&gt; api &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;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;version&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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;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;create_buffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;create_buffer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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 string&quot;&gt;&#39;number&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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;    &lt;span class=&quot;token literal-property property&quot;&gt;destroy_buffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;destroy_buffer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#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 string&quot;&gt;&#39;number&#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;&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;Here we declare the names of the functions that we marked with
&lt;code&gt;EMSCRIPTEN_KEEPALIVE&lt;/code&gt;, what their return types are, and what the types of their
arguments are. Afterwards, we can use the methods on the &lt;code&gt;api&lt;/code&gt; object to invoke
these functions. However, using wasm this way doesn&#39;t support strings and
requires you to manually move chunks of memory around which makes many library
APIs very tedious to use. Isn&#39;t there a better way? Why yes there is, otherwise
what would this article be about?&lt;/p&gt;
&lt;h2 id=&quot;c-name-mangling&quot;&gt;C++ name mangling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#c-name-mangling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While the developer experience would be reason enough to build a tool that helps
with these bindings, there&#39;s actually a more pressing reason: When you compile C
or C++ code, each file is compiled separately. Then, a linker takes care of
munging all these so-called object files together and turning them into a wasm
file. With C, the names of the functions are still available in the object file
for the linker to use. All you need to be able to call a C function is the name,
which we are providing as a string to &lt;code&gt;cwrap()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;C++ on the other hand supports function overloading, meaning you can implement
the same function multiple times as long as the signature is different (e.g.
differently typed parameters). At the compiler level, a nice name like &lt;code&gt;add&lt;/code&gt;
would get &lt;em&gt;mangled&lt;/em&gt; into something that encodes the signature in the function
name for the linker. As a result, we wouldn&#39;t be able to look up our function
with its name anymore.&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; You can prevent the compiler from mangling your functions&#39; names by annotating it with &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;enter-embind&quot;&gt;Enter embind &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#enter-embind&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html&quot; rel=&quot;noopener&quot;&gt;embind&lt;/a&gt;
is part of the Emscripten toolchain and provides you with a bunch of C++ macros
that allow you to annotate C++ code. You can declare which functions, enums,
classes or value types you are planning to use from JavaScript. Let&#39;s start
simple with some plain functions:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;emscripten/bind.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; emscripten&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;double&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 keyword&quot;&gt;double&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; b&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; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&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;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string &lt;span class=&quot;token function&quot;&gt;exclaim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string message&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; message &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;!&quot;&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 function&quot;&gt;EMSCRIPTEN_BINDINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;my_module&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;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;add&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;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;exclaim&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;exclaim&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;Compared to my previous article, we are not including &lt;code&gt;emscripten.h&lt;/code&gt; anymore, as
we don&#39;t have to annotate our functions with &lt;code&gt;EMSCRIPTEN_KEEPALIVE&lt;/code&gt; anymore.
Instead, we have an &lt;code&gt;EMSCRIPTEN_BINDINGS&lt;/code&gt; section in which we list the names under
which we want to expose our functions to JavaScript.&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; The parameter for the &lt;code&gt;EMSCRIPTEN_BINDINGS&lt;/code&gt; macro is mostly used to avoid name conflicts in bigger projects. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To compile this file, we can use the same setup (or, if you want, the same
Docker image) as in the &lt;a href=&quot;https://web.dev/emscripting-a-c-library&quot;&gt;previous
article&lt;/a&gt;. To use embind,
we add the &lt;code&gt;--bind&lt;/code&gt; flag:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ emcc --bind -O3 add.cpp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now all that&#39;s left is whipping up an HTML file that loads our freshly
created wasm module:&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;/a.out.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;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onRuntimeInitialized&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Module&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 number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.3&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;    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;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exclaim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello world&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;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;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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 are curious, &lt;a href=&quot;https://gist.github.com/surma/d04cd0fd896610575126d30de36d7eb6&quot;&gt;I wrote up the same&lt;/a&gt; C++ module &lt;em&gt;without&lt;/em&gt; embind to give you a feel for how much work it is doing for you. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;As you can see, we aren&#39;t using &lt;code&gt;cwrap()&lt;/code&gt; anymore. This just works straight out
of the box. But more importantly, we don&#39;t have to worry about manually copying
chunks of memory to make strings work! embind gives you that for free, along
with type checks:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools errors when you invoke a function with the wrong number of arguments or the arguments have the wrong type&quot; decoding=&quot;async&quot; height=&quot;570&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/j2YpEGF6Itd8ITPTqfvT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;This is pretty great as we can catch some errors early instead of dealing with
the occasionally quite unwieldy wasm errors.&lt;/p&gt;
&lt;h3 id=&quot;objects&quot;&gt;Objects &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#objects&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many JavaScript constructors and functions use options objects. It&#39;s a nice
pattern in JavaScript, but extremely tedious to realize in wasm manually. embind
can help here, too!&lt;/p&gt;
&lt;p&gt;For example, I came up with this &lt;em&gt;incredibly&lt;/em&gt; useful C++ function that processes my
strings, and I urgently want to use it on the web. Here is how I did that:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;emscripten/bind.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;algorithm&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; emscripten&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;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProcessMessageOpts&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;bool&lt;/span&gt; reverse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; exclaim&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; repeat&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;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string &lt;span class=&quot;token function&quot;&gt;processMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ProcessMessageOpts opts&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;  std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string copy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&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;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;opts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reverse&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;  std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;copy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&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; copy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&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;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 keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;opts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exclaim&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;  copy &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;!&quot;&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;  std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;string acc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; std&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&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;  &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;int&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; opts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repeat&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;  acc &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; copy&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 keyword&quot;&gt;return&lt;/span&gt; acc&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 function&quot;&gt;EMSCRIPTEN_BINDINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;my_module&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 generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;value_object&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ProcessMessageOpts&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ProcessMessageOpts&quot;&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 function&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;reverse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ProcessMessageOpts&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;reverse&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 function&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;exclaim&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ProcessMessageOpts&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;exclaim&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 function&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;repeat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ProcessMessageOpts&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;repeat&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 function&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;processMessage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;processMessage&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;I am defining a struct for the options of my &lt;code&gt;processMessage()&lt;/code&gt; function. In the
&lt;code&gt;EMSCRIPTEN_BINDINGS&lt;/code&gt; block, I can use &lt;code&gt;value_object&lt;/code&gt; to make JavaScript see
this C++ value as an object. I could also use &lt;code&gt;value_array&lt;/code&gt; if I preferred to
use this C++ value as an array. I also bind the &lt;code&gt;processMessage()&lt;/code&gt; function, and
the rest is embind &lt;em&gt;magic&lt;/em&gt;. I can now call the &lt;code&gt;processMessage()&lt;/code&gt; function from
JavaScript without any boilerplate code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;processMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;hello world&quot;&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 literal-property property&quot;&gt;reverse&lt;/span&gt;&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 literal-property property&quot;&gt;exclaim&lt;/span&gt;&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 literal-property property&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Prints &quot;hello world!hello world!hello world!&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;classes&quot;&gt;Classes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#classes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For completeness sake, I should also show you how embind allows you to expose
entire classes, which brings a lot of synergy with ES6 classes. You can probably
start to see a pattern by now:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;emscripten/bind.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;algorithm&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; emscripten&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Counter&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;public&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; counter&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;Counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;init&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 keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increase&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;    counter&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;squareCounter&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;return&lt;/span&gt; counter &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; counter&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;EMSCRIPTEN_BINDINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;my_module&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 generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;class_&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Counter&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Counter&quot;&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 generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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 function&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;increase&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;Counter&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;increase&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 function&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;squareCounter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;Counter&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;squareCounter&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 function&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;counter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;Counter&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;counter&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;On the JavaScript side, this almost feels like a native class:&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;script src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/a.out.js&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onRuntimeInitialized&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; c &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;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;22&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;    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;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&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;// prints 22&lt;/span&gt;&lt;br /&gt;    c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increase&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;    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;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&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;// prints 23&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;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;squareCounter&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// prints 529&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;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;what-about-c&quot;&gt;What about C? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#what-about-c&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;embind was written for C++ and can only be used in C++ files, but that doesn&#39;t
mean that you can&#39;t link against C files! To mix C and C++, you only need to
separate your input files into two groups: One for C and one for C++ files and
augment the CLI flags for &lt;code&gt;emcc&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;$ emcc --bind -O3 --std&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;c++11 a_c_file.c another_c_file.c -x c++ your_cpp_file.cpp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embind/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;embind gives you great improvements in the developer experience when working
with wasm and C/C++. This article does not cover all the options embind offers.
If you are interested, I recommend continuing with &lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html&quot; rel=&quot;noopener&quot;&gt;embind&#39;s
documentation&lt;/a&gt;.
Keep in mind that using embind can make both your wasm module and your
JavaScript glue code bigger by up to 11k when gzip&#39;d — most notably on small
modules. If you only have a very small wasm surface, embind might cost more than
it&#39;s worth in a production environment! Nonetheless, you should definitely give
it a try.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>Emscripting a C library to Wasm</title>
    <link href="https://web.dev/emscripting-a-c-library/"/>
    <updated>2018-03-05T00:00:00Z</updated>
    <id>https://web.dev/emscripting-a-c-library/</id>
    <content type="html" mode="escaped">&lt;p&gt;Sometimes you want to use a library that is only available as C or C++ code.
Traditionally, this is where you give up. Well, not anymore, because now we have
&lt;a href=&quot;http://emscripten.org/&quot; rel=&quot;noopener&quot;&gt;Emscripten&lt;/a&gt; and &lt;a href=&quot;http://webassembly.org/&quot; rel=&quot;noopener&quot;&gt;WebAssembly&lt;/a&gt;
(or Wasm)!&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; In this article, I will describe my journey of compiling &lt;a href=&quot;https://github.com/webmproject/libwebp&quot;&gt;libwebp&lt;/a&gt; to Wasm. To make use of this article as well as Wasm in general, you will need knowledge of C, especially pointers, memory management and compiler options. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;the-toolchain&quot;&gt;The toolchain &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#the-toolchain&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I set myself the goal of working out how to compile some existing C code to
Wasm. There&#39;s been some noise around &lt;a href=&quot;http://llvm.org/&quot; rel=&quot;noopener&quot;&gt;LLVM&lt;/a&gt;&#39;s Wasm backend, so
I started digging into that. While
&lt;a href=&quot;https://twitter.com/dassurma/status/752621241886990337?lang=en&quot; rel=&quot;noopener&quot;&gt;you can get simple programs to compile&lt;/a&gt;
this way, the second you want to use C&#39;s standard library or even compile
multiple files, you will probably run into problems. This led me to the major
lesson I learned:&lt;/p&gt;
&lt;p&gt;While Emscripten &lt;em&gt;used&lt;/em&gt; to be a C-to-asm.js compiler, it has since matured to
target Wasm and is
&lt;a href=&quot;https://github.com/kripken/emscripten/issues/6168&quot; rel=&quot;noopener&quot;&gt;in the process&lt;/a&gt; of switching
to the official LLVM backend internally. Emscripten also provides a
Wasm-compatible implementation of C&#39;s standard library. &lt;strong&gt;Use Emscripten&lt;/strong&gt;. It
&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/porting/emscripten-runtime-environment.html&quot; rel=&quot;noopener&quot;&gt;carries a lot of hidden work&lt;/a&gt;,
emulates a file system, provides memory management, wraps OpenGL with WebGL — a
lot of things that you really don&#39;t need to experience developing for yourself.&lt;/p&gt;
&lt;p&gt;While that might sound like you have to worry about bloat — I certainly worried
— the Emscripten compiler removes everything that&#39;s not needed. In my
experiments, the resulting Wasm modules are appropriately sized for the logic
that they contain and the Emscripten and WebAssembly teams are working on making
them even smaller in the future.&lt;/p&gt;
&lt;p&gt;You can get Emscripten by following the instructions on their
&lt;a href=&quot;http://emscripten.org/&quot; rel=&quot;noopener&quot;&gt;website&lt;/a&gt; or using Homebrew. If you are a fan of
dockerized commands like me and don&#39;t want to install things on your system just
to have a play with WebAssembly, there is a well-maintained
&lt;a href=&quot;https://hub.docker.com/r/trzeci/emscripten/&quot; rel=&quot;noopener&quot;&gt;Docker image&lt;/a&gt; that you can use
instead:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-docker&quot;&gt;&lt;code class=&quot;language-docker&quot;&gt;    $ docker pull trzeci/emscripten&lt;br /&gt;    $ docker run --rm -v $(pwd):/src trzeci/emscripten emcc &amp;lt;emcc options here&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;compiling-something-simple&quot;&gt;Compiling something simple &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#compiling-something-simple&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s take the almost canonical example of writing a function in C that
calculates the n&lt;sup&gt;th&lt;/sup&gt; fibonacci number:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-wasm&quot;&gt;&lt;code class=&quot;language-wasm&quot;&gt;    #include &amp;lt;emscripten.h&gt;&lt;br /&gt;&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    int fib&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;int n&lt;span class=&quot;token punctuation&quot;&gt;)&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;n &amp;lt;= &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;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;;&lt;br /&gt;      }&lt;br /&gt;      int i, t, a = &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;, b = &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;;&lt;br /&gt;      for &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i = &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;; i &amp;lt; n; i++&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {&lt;br /&gt;        t = a + b;&lt;br /&gt;        a = b;&lt;br /&gt;        b = t;&lt;br /&gt;      }&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; b;&lt;br /&gt;    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you know C, the function itself shouldn&#39;t be too surprising. Even if you
don&#39;t know C but know JavaScript, you will hopefully be able to understand
what&#39;s going on here.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;emscripten.h&lt;/code&gt; is a header file provided by Emscripten. We only need it so we
have access to the &lt;code&gt;EMSCRIPTEN_KEEPALIVE&lt;/code&gt; macro, but it
&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html&quot; rel=&quot;noopener&quot;&gt;provides much more functionality&lt;/a&gt;.
This macro tells the compiler to not remove a function even if it appears
unused. If we omitted that macro, the compiler would optimize the function away
— nobody is using it after all.&lt;/p&gt;
&lt;p&gt;Let&#39;s save all that in a file called &lt;code&gt;fib.c&lt;/code&gt;. To turn it into a &lt;code&gt;.wasm&lt;/code&gt; file we
need to turn to Emscripten&#39;s compiler command &lt;code&gt;emcc&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-wasm&quot;&gt;&lt;code class=&quot;language-wasm&quot;&gt;    $ emcc -O3 -s WASM=&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; -s EXTRA_EXPORTED_RUNTIME_METHODS=&#39;[&lt;span class=&quot;token string&quot;&gt;&quot;cwrap&quot;&lt;/span&gt;]&#39; fib.c&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Let&#39;s dissect this command. &lt;code&gt;emcc&lt;/code&gt; is Emscripten&#39;s compiler. &lt;code&gt;fib.c&lt;/code&gt; is our C
file. So far, so good. &lt;code&gt;-s WASM=1&lt;/code&gt; tells Emscripten to give us a Wasm file
instead of an &lt;a href=&quot;http://asmjs.org/&quot; rel=&quot;noopener&quot;&gt;asm.js&lt;/a&gt; file.
&lt;code&gt;-s EXTRA_EXPORTED_RUNTIME_METHODS=&#39;[&amp;quot;cwrap&amp;quot;]&#39;&lt;/code&gt; tells the compiler to leave the
&lt;code&gt;cwrap()&lt;/code&gt; function available in the JavaScript file — more on this function
later. &lt;code&gt;-O3&lt;/code&gt; tells the compiler to optimize aggressively. You can choose lower
numbers to decrease build time, but that will also make the resulting bundles
bigger as the compiler might not remove unused code.&lt;/p&gt;
&lt;p&gt;After running the command, you should end up with a JavaScript file called
&lt;code&gt;a.out.js&lt;/code&gt; and a WebAssembly file called &lt;code&gt;a.out.wasm&lt;/code&gt;. The Wasm file (or
&amp;quot;module&amp;quot;) contains our compiled C code and should be fairly small. The
JavaScript file takes care of loading and initializing our Wasm module and
providing a nicer API. If needed, it will also take care of setting up the
stack, the heap, and other functionality usually expected to be provided by the
operating system when writing C code. As such, the JavaScript file is a bit
bigger, weighing in at 19KB (~5KB gzip&#39;d).&lt;/p&gt;
&lt;h2 id=&quot;running-something-simple&quot;&gt;Running something simple &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#running-something-simple&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The easiest way to load and run your module is to use the generated JavaScript
file. Once you load that file, you will have a
&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/api_reference/module.html&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Module&lt;/code&gt; global&lt;/a&gt;
at your disposal. Use
&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#cwrap&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;cwrap&lt;/code&gt;&lt;/a&gt;
to create a JavaScript native function that takes care of converting parameters
to something C-friendly and invoking the wrapped function. &lt;code&gt;cwrap&lt;/code&gt; takes the
function name, return type and argument types as arguments, in that order:&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;script src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a.out.js&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;      Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onRuntimeInitialized&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; fib &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fib&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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 string&quot;&gt;&#39;number&#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;        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you
&lt;a href=&quot;https://googlechrome.github.io/samples/webassembly/index.html&quot; rel=&quot;noopener&quot;&gt;run this code&lt;/a&gt;,
you should see the &amp;quot;144&amp;quot; in the console, which is the 12th Fibonacci number.&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; Emscripten offers a couple of options to handle loading multiple modules. More about that in their &lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#how-can-i-tell-when-the-page-is-fully-loaded-and-it-is-safe-to-call-compiled-functions&quot;&gt;documentation&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;the-holy-grail-compiling-a-c-library&quot;&gt;The holy grail: Compiling a C library &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#the-holy-grail-compiling-a-c-library&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Up until now, the C code we have written was written with Wasm in mind. A core
use-case for WebAssembly, however, is to take the existing ecosystem of C
libraries and allow developers to use them on the web. These libraries often
rely on C&#39;s standard library, an operating system, a file system and other
things. Emscripten provides most of these features, although there are some
&lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/porting/guidelines/api_limitations.html&quot; rel=&quot;noopener&quot;&gt;limitations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s go back to my original goal: compiling an encoder for WebP to Wasm. The
source for the WebP codec is written in C and available on
&lt;a href=&quot;https://github.com/webmproject/libwebp&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt; as well as some extensive
&lt;a href=&quot;https://developers.google.com/speed/webp/docs/api&quot; rel=&quot;noopener&quot;&gt;API documentation&lt;/a&gt;. That&#39;s a pretty good starting point.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;    $ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/webmproject/libwebp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To start off simple, let&#39;s try to expose &lt;code&gt;WebPGetEncoderVersion()&lt;/code&gt; from
&lt;code&gt;encode.h&lt;/code&gt; to JavaScript by writing a C file called &lt;code&gt;webp.c&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;    &lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;emscripten.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/webp/encode.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;version&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;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WebPGetEncoderVersion&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;This is a good simple program to test if we can get the source code of libwebp
to compile, as we don&#39;t require any parameters or complex data structures to
invoke this function.&lt;/p&gt;
&lt;p&gt;To compile this program, we need to tell the compiler where it can find
libwebp&#39;s header files using the &lt;code&gt;-I&lt;/code&gt; flag and also pass it all the C files of
libwebp that it needs. I&#39;m going to be honest: I just gave it &lt;strong&gt;all&lt;/strong&gt; the C
files I could find and relied on the compiler to strip out everything that was
unnecessary. It seemed to work brilliantly!&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;    $ emcc -O3 -s &lt;span class=&quot;token assign-left variable&quot;&gt;WASM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; -s &lt;span class=&quot;token assign-left variable&quot;&gt;EXTRA_EXPORTED_RUNTIME_METHODS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[&quot;cwrap&quot;]&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;        -I libwebp &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;        webp.c &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;        libwebp/src/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;dec,dsp,demux,enc,mux,utils&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/*.c&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This strategy will not work with every C project out there. Many projects rely on autoconf/automake to generate system-specific code before compilation. Emscripten provides &lt;code&gt;emconfigure&lt;/code&gt; and &lt;code&gt;emmake&lt;/code&gt; to wrap these commands and inject the appropriate parameters. You can find more in the &lt;a href=&quot;https://kripken.github.io/emscripten-site/docs/compiling/Building-Projects.html&quot;&gt;Emscripten documentation&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now we only need some HTML and JavaScript to load our shiny new module:&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;/a.out.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;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onRuntimeInitialized&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; api &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;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;version&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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;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;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;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;version&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;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;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And we will see the correction version number in the
&lt;a href=&quot;https://googlechrome.github.io/samples/webassembly/version.html&quot; rel=&quot;noopener&quot;&gt;output&lt;/a&gt;:&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the DevTools console showing the correct version number.&quot; decoding=&quot;async&quot; height=&quot;389&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CeKJztdJOom78OH2UrXx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; libwebp returns the current version a.b.c as a hexadecimal number 0xabc. So v0.6.1 is encoded as 0x000601 = 1537. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;get-an-image-from-javascript-into-wasm&quot;&gt;Get an image from JavaScript into Wasm &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#get-an-image-from-javascript-into-wasm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Getting the encoder&#39;s version number is great and all, but encoding an actual
image would be more impressive, right? Let&#39;s do that, then.&lt;/p&gt;
&lt;p&gt;The first question we have to answer is: How do we get the image into Wasm land?
Looking at the
&lt;a href=&quot;https://developers.google.com/speed/webp/docs/api#simple_encoding_api&quot; rel=&quot;noopener&quot;&gt;encoding API of libwebp&lt;/a&gt;, it expects
an array of bytes in RGB, RGBA, BGR or BGRA. Luckily, the Canvas API has
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/getImageData&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getImageData()&lt;/code&gt;&lt;/a&gt;,
that gives us an
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray&quot; rel=&quot;noopener&quot;&gt;Uint8ClampedArray&lt;/a&gt;
containing the image data in RGBA:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;src&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;// Load image&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imgBlob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src&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;then&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 parameter&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; resp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;blob&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; img &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createImageBitmap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imgBlob&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 canvas same size as image&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; canvas &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;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;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;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; img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&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; img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Draw image onto canvas&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&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;  ctx&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;img&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 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;span class=&quot;token function&quot;&gt;getImageData&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; img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; img&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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now it&#39;s &amp;quot;only&amp;quot; a matter of copying the data from JavaScript land into Wasm
land. For that, we need to expose two additional functions. One that allocates
memory for the image inside Wasm land and one that frees it up again:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-wasm&quot;&gt;&lt;code class=&quot;language-wasm&quot;&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    uint8_t* create_buffer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;int width, int height&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; malloc&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width * height * &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; * sizeof&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uint8_t&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;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    void destroy_buffer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uint8_t* p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {&lt;br /&gt;      free&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&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;&lt;code&gt;create_buffer&lt;/code&gt; allocates a buffer for the RGBA image — hence 4 bytes per pixel.
The pointer returned by &lt;code&gt;malloc()&lt;/code&gt; is the address of the first memory cell of
that buffer. When the pointer is returned to JavaScript land, it is treated as
just a number. After exposing the function to JavaScript using &lt;code&gt;cwrap&lt;/code&gt;, we can
use that number to find the start of our buffer and copy the image data.&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;const&lt;/span&gt; api &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;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;version&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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;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;create_buffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;create_buffer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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 string&quot;&gt;&#39;number&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;number&#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;  &lt;span class=&quot;token literal-property property&quot;&gt;destroy_buffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;destroy_buffer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#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 string&quot;&gt;&#39;number&#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;&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;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;const&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/image.jpg&#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;const&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create_buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; image&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;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HEAP8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&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;// ... call encoder ...&lt;/span&gt;&lt;br /&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;destroy_buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&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;h3 id=&quot;grand-finale-encode-the-image&quot;&gt;Grand Finale: Encode the image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#grand-finale-encode-the-image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The image is now available in Wasm land. It is time to call the WebP encoder to
do its job! Looking at the
&lt;a href=&quot;https://developers.google.com/speed/webp/docs/api#simple_encoding_api&quot; rel=&quot;noopener&quot;&gt;WebP documentation&lt;/a&gt;, &lt;code&gt;WebPEncodeRGBA&lt;/code&gt;
seems like a perfect fit. The function takes a pointer to the input image and
its dimensions, as well as a quality option between 0 and 100. It also allocates
an output buffer for us, that we need to free using &lt;code&gt;WebPFree()&lt;/code&gt; once we are
done with the WebP image.&lt;/p&gt;
&lt;p&gt;The result of the encoding operation is an output buffer and its length. Because
functions in C can&#39;t have arrays as return types (unless we allocate memory
dynamically), I resorted to a static global array. I know, not clean C (in fact,
it relies on the fact that Wasm pointers are 32bit wide), but to keep things
simple I think this is a fair shortcut.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-wasm&quot;&gt;&lt;code class=&quot;language-wasm&quot;&gt;    int &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;[&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;];&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    void encode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uint8_t* img_in, int width, int height, float quality&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {&lt;br /&gt;      uint8_t* img_out;&lt;br /&gt;      size_t size;&lt;br /&gt;&lt;br /&gt;      size = WebPEncodeRGBA&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;img_in, width, height, width * &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;, quality, &amp;amp;img_out&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;result&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;int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;img_out;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;[&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;] = size;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    void free_result&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uint8_t* &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; {&lt;br /&gt;      WebPFree&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    int get_result_pointer&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; &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;[&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    EMSCRIPTEN_KEEPALIVE&lt;br /&gt;    int get_result_size&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; &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt;[&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;];&lt;br /&gt;    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now with all of that in place, we can call the encoding function, grab the
pointer and image size, put it in a JavaScript-land buffer of our own, and
release all the Wasm-land buffers we have allocated in the process.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-wasm&quot;&gt;&lt;code class=&quot;language-wasm&quot;&gt;    api.encode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p, image.width, image.height, &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;;&lt;br /&gt;    const resultPointer = api.get_result_pointer&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;    const resultSize = api.get_result_size&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;    const resultView = new Uint8Array&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Module.HEAP8.buffer, resultPointer, resultSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;;&lt;br /&gt;    const &lt;span class=&quot;token keyword&quot;&gt;result&lt;/span&gt; = new Uint8Array&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resultView&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;;&lt;br /&gt;    api.free_result&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resultPointer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;new Uint8Array(someBuffer)&lt;/code&gt; will create a new view onto the same memory chunk, while &lt;code&gt;new Uint8Array(someTypedArray)&lt;/code&gt; will copy the data. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Depending on the size of your image, you might run into an error where Wasm
can&#39;t grow the memory enough to accommodate both the input and the output image:&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the DevTools console showing an error.&quot; decoding=&quot;async&quot; height=&quot;103&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/tUQKJR5CE9D6wav70siO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Luckily, the solution to this problem is in the error message! We just need to
add &lt;code&gt;-s ALLOW_MEMORY_GROWTH=1&lt;/code&gt; to our compilation command.&lt;/p&gt;
&lt;p&gt;And there you have it! We compiled a WebP encoder and transcoded a JPEG image to
WebP. To prove that it worked, we can turn our result buffer into a blob and use
it on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element:&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;const&lt;/span&gt; blob &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;Blob&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;result&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;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;image/webp&#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;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blobURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blob&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;const&lt;/span&gt; img &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;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;img&#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;img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; blobURL&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;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;img&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;&lt;a href=&quot;https://googlechrome.github.io/samples/webassembly/image.html&quot; rel=&quot;noopener&quot;&gt;Behold, the glory of a new WebP image&lt;/a&gt;!&lt;/p&gt;
&lt;img alt=&quot;DevTools’ network panel and the generated image.&quot; decoding=&quot;async&quot; height=&quot;422&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eqKkzJ4MzO5T14py1wNb.jpeg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s not a walk in the park to get a C library to work in the browser, but once
you understand the overall process and how the data flow works, it becomes
easier and the results can be mind-blowing.&lt;/p&gt;
&lt;p&gt;WebAssembly opens many new possibilities on the web for processing, number
crunching and gaming. Keep in mind that Wasm is not a silver bullet that should
be applied to everything, but when you hit one of those bottlenecks, Wasm can be
an incredibly helpful tool.&lt;/p&gt;
&lt;h2 id=&quot;bonus-content-running-something-simple-the-hard-way&quot;&gt;Bonus content: Running something simple the hard way &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/emscripting-a-c-library/#bonus-content-running-something-simple-the-hard-way&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to try and avoid the generated JavaScript file, you might be able
to. Let&#39;s go back to the Fibonacci example. To load and run it ourselves, we can
do the following:&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 doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&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 keyword&quot;&gt;const&lt;/span&gt; imports &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;env&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;memory&lt;/span&gt;&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;WebAssembly&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Memory&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 literal-property property&quot;&gt;initial&lt;/span&gt;&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;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 constant&quot;&gt;STACKTOP&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;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;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;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; instance &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 keyword&quot;&gt;await&lt;/span&gt; WebAssembly&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;instantiateStreaming&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;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/a.out.wasm&#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;      imports&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;    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;instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_fib&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&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 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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Make sure that your &lt;code&gt;.wasm&lt;/code&gt; files have &lt;code&gt;Content-Type: application/wasm&lt;/code&gt;. Otherwise they will be rejected by WebAssembly. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;WebAssembly modules that have been created by Emscripten have no memory to work
with unless you provide them with memory. The way you provide a Wasm module with
&lt;em&gt;anything&lt;/em&gt; is by using the &lt;code&gt;imports&lt;/code&gt; object — the second parameter of the
&lt;code&gt;instantiateStreaming&lt;/code&gt; function. The Wasm module can access everything inside
the imports object, but nothing else outside of it. By convention, modules
compiled by Emscripting expect a couple of things from the loading JavaScript
environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firstly, there is &lt;code&gt;env.memory&lt;/code&gt;. The Wasm module is unaware of the outside
world so to speak, so it needs to get some memory to work with. Enter
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;WebAssembly.Memory&lt;/code&gt;&lt;/a&gt;.
It represents a (optionally growable) piece of linear memory. The sizing
parameters are in &amp;quot;in units of WebAssembly pages&amp;quot;, meaning the code above
allocates 1 page of memory, with each page having a size of 64
&lt;a href=&quot;https://en.wikipedia.org/wiki/Kibibyte&quot; rel=&quot;noopener&quot;&gt;KiB&lt;/a&gt;. Without providing a &lt;code&gt;maximum&lt;/code&gt;
option, the memory is theoretically unbounded in growth (Chrome currently has
a hard limit of 2GB). Most WebAssembly modules shouldn&#39;t need to set a
maximum.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;env.STACKTOP&lt;/code&gt; defines where the stack is supposed to start growing. The stack
is needed to make function calls and to allocate memory for local variables.
Since we don&#39;t do any dynamic memory management shenanigans in our little
Fibonacci program, we can just use the entire memory as a stack, hence
&lt;code&gt;STACKTOP = 0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-checkbox</title>
    <link href="https://web.dev/components-howto-checkbox/"/>
    <updated>2017-10-02T00:00:00Z</updated>
    <id>https://web.dev/components-howto-checkbox/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-checkbox&amp;gt;&lt;/code&gt; represents a boolean option in a form. The most common type
of checkbox is a dual-type which allows the user to toggle between two
choices -- checked and unchecked.&lt;/p&gt;
&lt;p&gt;The element attempts to self apply the attributes &lt;code&gt;role=&amp;quot;checkbox&amp;quot;&lt;/code&gt; and
&lt;code&gt;tabindex=&amp;quot;0&amp;quot;&lt;/code&gt; when it is first created. The &lt;code&gt;role&lt;/code&gt; attribute helps assistive
technology like a screen reader tell the user what kind of control this is.
The &lt;code&gt;tabindex&lt;/code&gt; attribute opts the element into the tab order, making it keyboard
focusable and operable. To learn more about these two topics, check out
&lt;a href=&quot;https://web.dev/semantics-aria/#what-can-aria-do&quot;&gt;What can ARIA do?&lt;/a&gt; and &lt;a href=&quot;https://web.dev/using-tabindex/&quot;&gt;Using tabindex&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When the checkbox is checked, it adds a &lt;code&gt;checked&lt;/code&gt; boolean attribute, and sets
a corresponding &lt;code&gt;checked&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt;. In addition, the element sets an
&lt;code&gt;aria-checked&lt;/code&gt; attribute to either &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt;, depending on its
state. Clicking on the checkbox with a mouse, or space bar, toggles these
checked states.&lt;/p&gt;
&lt;p&gt;The checkbox also supports a &lt;code&gt;disabled&lt;/code&gt; state. If either the &lt;code&gt;disabled&lt;/code&gt; property
is set to true or the &lt;code&gt;disabled&lt;/code&gt; attribute is applied, the checkbox sets
&lt;code&gt;aria-disabled=&amp;quot;true&amp;quot;&lt;/code&gt;, removes the &lt;code&gt;tabindex&lt;/code&gt; attribute, and returns focus
to the document if the checkbox is the current &lt;code&gt;activeElement&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The checkbox is paired with a &lt;code&gt;howto-label&lt;/code&gt; element to ensure it has an
&lt;a href=&quot;https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree#semantics_in_native_html&quot; rel=&quot;noopener&quot;&gt;accessible name&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&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;Warning sign&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;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Just because you &lt;em&gt;can&lt;/em&gt; build a custom element checkbox, doesn&#39;t necessarily mean that you &lt;em&gt;should&lt;/em&gt;. As this example shows, you will need to add your own keyboard, labeling, and ARIA support. It&#39;s also important to note that the native &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element will NOT submit values from a custom element. You will need to wire that up yourself using AJAX or a hidden &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; field. For these reasons it can often be preferable to use the built-in &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt; instead. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#checkbox&quot; rel=&quot;noopener&quot;&gt;Checkbox Pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/semantics-aria/#what-can-aria-do&quot;&gt;What can ARIA Do?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/using-tabindex/&quot;&gt;Using tabindex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-checkbox/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;howto-checkbox&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;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;howto-label&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;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-size&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;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-checkbox&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;join-checkbox&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;howto-checkbox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&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;join-checkbox&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;Join Newsletter&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;howto-label&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;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&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 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Define key codes to help with handling keyboard events.&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;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&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 constant&quot;&gt;SPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Cloning contents from a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element is more performant than using innerHTML because it avoids additional HTML parse costs.&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;const&lt;/span&gt; template &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;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;template&#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;  template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &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 string&quot;&gt;&lt;br /&gt;    &amp;lt;style&gt;&lt;br /&gt;      :host {&lt;br /&gt;        display: inline-block;&lt;br /&gt;        background: url(&#39;../images/unchecked-checkbox.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;        width: 24px;&lt;br /&gt;        height: 24px;&lt;br /&gt;      }&lt;br /&gt;      :host([hidden]) {&lt;br /&gt;        display: none;&lt;br /&gt;      }&lt;br /&gt;      :host([checked]) {&lt;br /&gt;        background: url(&#39;../images/checked-checkbox.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;      :host([disabled]) {&lt;br /&gt;        background:&lt;br /&gt;          url(&#39;../images/unchecked-checkbox-disabled.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;      :host([checked][disabled]) {&lt;br /&gt;        background:&lt;br /&gt;          url(&#39;../images/checked-checkbox-disabled.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/style&gt;&lt;br /&gt;  &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;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowToCheckbox&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observedAttributes&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;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;disabled&#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;The element&#39;s constructor is run anytime a new instance is created. Instances are created either by parsing HTML, calling document.createElement(&#39;howto-checkbox&#39;), or calling new HowToCheckbox(); The constructor is a good place to create shadow DOM, though you should avoid touching any attributes or light DOM children as they may not be available yet.&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 function&quot;&gt;constructor&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;super&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attachShadow&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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;open&#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;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cloneNode&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;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;code&gt;connectedCallback()&lt;/code&gt; fires when the element is inserted into the DOM. It&#39;s a good place to set the initial &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;tabindex&lt;/code&gt;, internal state, and install event listeners.&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 function&quot;&gt;connectedCallback&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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;checkbox&#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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A user may set a property on an instance of an element, before its prototype has been connected to this class. The &lt;code&gt;_upgradeProperty()&lt;/code&gt; method will check for any instance properties and run them through the proper class setters. See the &lt;a href=&quot;https://web.dev/custom-elements-best-practices/#make-properties-lazy&quot;&gt;lazy properties&lt;/a&gt; section for more details.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;disabled&#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 keyword&quot;&gt;this&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;keyup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onKeyUp&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;this&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; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&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 function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prop&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prop&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;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&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;delete&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&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;code&gt;disconnectedCallback()&lt;/code&gt; fires when the element is removed from the DOM. It&#39;s a good place to do clean up work like releasing references and removing event listeners.&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 function&quot;&gt;disconnectedCallback&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keyup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onKeyUp&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&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; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&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;Properties and their corresponding attributes should mirror one another. The property setter for checked handles truthy/falsy values and reflects those to the state of the attribute. See the &lt;a href=&quot;https://web.dev/custom-elements-best-practices/#avoid-reentrancy-issues&quot;&gt;avoid reentrancy&lt;/a&gt; section for more details.&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;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&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;const&lt;/span&gt; isChecked &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&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;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;isChecked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#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;else&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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 keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checked&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;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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 keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&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;const&lt;/span&gt; isDisabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&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;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;isDisabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;disabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#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;else&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;disabled&#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 keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disabled&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;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;disabled&#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;&lt;code&gt;attributeChangedCallback()&lt;/code&gt; is called when any of the attributes in the observedAttributes array are changed. It&#39;s a good place to handle side effects, like setting ARIA attributes.&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 function&quot;&gt;attributeChangedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; oldValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue&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;const&lt;/span&gt; hasValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&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;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;checked&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-checked&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasValue&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;break&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;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;disabled&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-disabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasValue&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;code&gt;tabindex&lt;/code&gt; attribute does not provide a way to fully remove focusability from an element. Elements with &lt;code&gt;tabindex=-1&lt;/code&gt; can still be focused with a mouse or by calling &lt;code&gt;focus()&lt;/code&gt;. To make sure an element is disabled and not focusable, remove the &lt;code&gt;tabindex&lt;/code&gt; attribute.&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasValue&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;If the focus is currently on this element, unfocus it by calling the &lt;code&gt;HTMLElement.blur()&lt;/code&gt; method&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;blur&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;_onKeyUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;Don’t handle modifier shortcuts typically used by assistive technology.&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;altKey&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;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&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;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_toggleChecked&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;break&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;Any other key press is ignored and passed back to the browser.&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;default&lt;/span&gt;&lt;span class=&quot;token operator&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;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 function&quot;&gt;_onClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_toggleChecked&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;&lt;code&gt;_toggleChecked()&lt;/code&gt; calls the checked setter and flips its state. Because &lt;code&gt;_toggleChecked()&lt;/code&gt; is only caused by a user action, it will also dispatch a change event. This event bubbles in order to mimic the native behavior of &lt;code&gt;&amp;lt;input type=checkbox&amp;gt;&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 function&quot;&gt;_toggleChecked&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled&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;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checked &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checked&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatchEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&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;CustomEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;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 literal-property property&quot;&gt;detail&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;checked&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checked&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;span class=&quot;token literal-property property&quot;&gt;bubbles&lt;/span&gt;&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 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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-checkbox&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowToCheckbox&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;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>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-tabs</title>
    <link href="https://web.dev/components-howto-tabs/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-howto-tabs/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; limit visible content by separating it into multiple panels. Only
one panel is visible at a time, while &lt;em&gt;all&lt;/em&gt; corresponding tabs are always
visible. To switch from one panel to another, the corresponding tab has to be
selected.&lt;/p&gt;
&lt;p&gt;By either clicking or by using the arrow keys the user can change the
selection of the active tab.&lt;/p&gt;
&lt;p&gt;If JavaScript is disabled, all panels are shown interleaved with the
respective tabs. The tabs now function as headings.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel&quot; rel=&quot;noopener&quot;&gt;Tabs pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-tabs/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  howto-tab {&lt;br /&gt;    border: 1px solid black;&lt;br /&gt;    padding: 20px;&lt;br /&gt;  }&lt;br /&gt;  howto-panel {&lt;br /&gt;    padding: 20px;&lt;br /&gt;    background-color: lightgray;&lt;br /&gt;  }&lt;br /&gt;  howto-tab[selected] {&lt;br /&gt;    background-color: bisque;&lt;br /&gt;  }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If JavaScript does not run, the element will not match &lt;code&gt;:defined&lt;/code&gt;. In that case this style adds spacing between tabs and previous panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;  howto-tabs:not(:defined), howto-tab:not(:defined), howto-panel:not(:defined) {&lt;br /&gt;    display: block;&lt;br /&gt;  }&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tabs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;tab&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;Tab 1&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;howto-tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;panel&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;Content 1&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;howto-panel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;tab&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;Tab 2&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;howto-tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;panel&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;Content 2&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;howto-panel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;tab&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;Tab 3&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;howto-tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&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;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&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;panel&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;Content 3&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;howto-panel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;howto-tabs&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;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&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 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Define key codes to help with handling keyboard events.&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;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&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 constant&quot;&gt;DOWN&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;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&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;RIGHT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&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;UP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&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;HOME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&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;END&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;35&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To avoid invoking the parser with &lt;code&gt;.innerHTML&lt;/code&gt; for every new instance, a template for the contents of the shadow DOM is shared by all &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; instances.&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;const&lt;/span&gt; template &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;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;template&#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;  template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &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 string&quot;&gt;&lt;br /&gt;    &amp;lt;style&gt;&lt;br /&gt;      :host {&lt;br /&gt;        display: flex;&lt;br /&gt;        flex-wrap: wrap;&lt;br /&gt;      }&lt;br /&gt;      ::slotted(howto-panel) {&lt;br /&gt;        flex-basis: 100%;&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/style&gt;&lt;br /&gt;    &amp;lt;slot name=&quot;tab&quot;&gt;&amp;lt;/slot&gt;&lt;br /&gt;    &amp;lt;slot name=&quot;panel&quot;&gt;&amp;lt;/slot&gt;&lt;br /&gt;  &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;HowtoTabs is a container element for tabs and panels.&lt;/p&gt;
&lt;p&gt;All children of &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; should be either &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;howto-tabpanel&amp;gt;&lt;/code&gt;. This element is stateless, meaning that no values are cached and therefore, changes during runtime work.&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTabs&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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;constructor&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;super&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;&lt;p&gt;Event handlers that are not attached to this element need to be bound if they need access to &lt;code&gt;this&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onSlotChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_onSlotChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&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;For progressive enhancement, the markup should alternate between tabs and panels. Elements that reorder their children tend to not work well with frameworks. Instead shadow DOM is used to reorder the elements by using slots.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attachShadow&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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;open&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Import the shared template to create the slots for tabs and panels.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cloneNode&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tabSlot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot&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;slot[name=tab]&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_panelSlot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot&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;slot[name=panel]&#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;This element needs to react to new children as it links up tabs and panel semantically using &lt;code&gt;aria-labelledby&lt;/code&gt; and &lt;code&gt;aria-controls&lt;/code&gt;. New children will get slotted automatically and cause slotchange to fire, so not MutationObserver is needed.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tabSlot&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;slotchange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onSlotChange&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_panelSlot&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;slotchange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onSlotChange&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;code&gt;connectedCallback()&lt;/code&gt; groups tabs and panels by reordering and makes sure exactly one tab is active.&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 function&quot;&gt;connectedCallback&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;&lt;p&gt;The element needs to do some manual input event handling to allow switching with arrow keys and &lt;kbd&gt;Home&lt;/kbd&gt; / &lt;kbd&gt;End&lt;/kbd&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 keyword&quot;&gt;this&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;keydown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onKeyDown&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;this&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; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tablist&#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;Up until recently, &lt;code&gt;slotchange&lt;/code&gt; events did not fire when an element was upgraded by the parser. For this reason, the element invokes the handler manually. Once the new behavior lands in all browsers, the code below can be removed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&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;        customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;whenDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-tab&#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;        customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;whenDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-panel&#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 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 function&quot;&gt;then&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;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_linkPanels&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;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;code&gt;disconnectedCallback()&lt;/code&gt; removes the event listeners that &lt;code&gt;connectedCallback()&lt;/code&gt; added.&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 function&quot;&gt;disconnectedCallback&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keydown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onKeyDown&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&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; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&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;code&gt;_onSlotChange()&lt;/code&gt; is called whenever an element is added or removed from one of the shadow DOM slots.&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 function&quot;&gt;_onSlotChange&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_linkPanels&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;&lt;code&gt;_linkPanels()&lt;/code&gt; links up tabs with their adjacent panels using aria-controls and &lt;code&gt;aria-labelledby&lt;/code&gt;. Additionally, the method makes sure only one tab is active.&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 function&quot;&gt;_linkPanels&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;&lt;p&gt;Give each panel a &lt;code&gt;aria-labelledby&lt;/code&gt; attribute that refers to the tab that controls it.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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 parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;const&lt;/span&gt; panel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nextElementSibling&lt;span class=&quot;token punctuation&quot;&gt;;&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;panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&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 string&quot;&gt;&#39;howto-panel&#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;          console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&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 string&quot;&gt;Tab #&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; is not a&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&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 string&quot;&gt;sibling of a &amp;lt;howto-panel&gt;&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;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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-controls&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&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;        panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-labelledby&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&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;The element checks if any of the tabs have been marked as selected. If not, the first tab is now selected.&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;const&lt;/span&gt; selectedTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;        tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&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 parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; tabs&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, switch to the selected tab. &lt;code&gt;_selectTab()&lt;/code&gt; takes care of marking all other tabs as deselected and hiding all other panels.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedTab&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;code&gt;_allPanels()&lt;/code&gt; returns all the panels in the tab panel. This function could memorize the result if the DOM queries ever become a performance issue. The downside of memorization is that dynamically added tabs and panels will not be handled.&lt;/p&gt;
&lt;p&gt;This is a method and not a getter, because a getter implies that it is cheap to read.&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 function&quot;&gt;_allPanels&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;return&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&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;howto-panel&#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;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_allTabs()&lt;/code&gt; returns all the tabs in the tab panel.&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 function&quot;&gt;_allTabs&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;return&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&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;howto-tab&#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;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_panelForTab()&lt;/code&gt; returns the panel that the given tab controls.&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 function&quot;&gt;_panelForTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tab&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;const&lt;/span&gt; panelId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-controls&#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;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&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 template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token 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;panelId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&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;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;code&gt;_prevTab()&lt;/code&gt; returns the tab that comes before the currently selected one, wrapping around when reaching the first one.&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 function&quot;&gt;_prevTab&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;&lt;p&gt;Use &lt;code&gt;findIndex()&lt;/code&gt; to find the index of the currently selected element and subtracts one to get the index of the previous element.&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;let&lt;/span&gt; newIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&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 parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&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;1&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;Add &lt;code&gt;tabs.length&lt;/code&gt; to make sure the index is a positive number and get the modulus to wrap around if necessary.&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;return&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIdx &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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;code&gt;_firstTab()&lt;/code&gt; returns the first tab.&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 function&quot;&gt;_firstTab&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;return&lt;/span&gt; tabs&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;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;code&gt;_lastTab()&lt;/code&gt; returns the last tab.&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 function&quot;&gt;_lastTab&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;return&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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;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;code&gt;_nextTab()&lt;/code&gt; gets the tab that comes after the currently selected one, wrapping around when reaching the last tab.&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 function&quot;&gt;_nextTab&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;let&lt;/span&gt; newIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&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 parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&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;1&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; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;newIdx &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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;code&gt;reset()&lt;/code&gt; marks all tabs as deselected and hides all the panels.&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 function&quot;&gt;reset&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;const&lt;/span&gt; tabs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allTabs&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;const&lt;/span&gt; panels &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_allPanels&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;      tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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 parameter&quot;&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected &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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      panels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&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 parameter&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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;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;code&gt;_selectTab()&lt;/code&gt; marks the given tab as selected. Additionally, it unhides the panel corresponding to the given tab.&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 function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;newTab&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;Deselect all tabs and hide all panels.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reset&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;&lt;p&gt;Get the panel that the &lt;code&gt;newTab&lt;/code&gt; is associated with.&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;const&lt;/span&gt; newPanel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_panelForTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If that panel doesn’t exist, abort.&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;if&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;newPanel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&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 string&quot;&gt;No panel with id &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newPanelId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      newTab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected &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;      newPanel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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;      newTab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&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;&lt;code&gt;_onKeyDown()&lt;/code&gt; handles key presses inside the tab panel.&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 function&quot;&gt;_onKeyDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;If the keypress did not originate from a tab element itself, it was a keypress inside the a panel or on empty space. Nothing to do.&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&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;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tab&#39;&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Don’t handle modifier shortcuts typically used by assistive technology.&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;altKey&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The switch-case will determine which tab should be marked as active depending on the key that was pressed.&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;let&lt;/span&gt; newTab&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&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;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_prevTab&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;break&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;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_nextTab&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;break&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;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_firstTab&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;break&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;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_lastTab&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;break&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;Any other key press is ignored and passed back to the browser.&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;default&lt;/span&gt;&lt;span class=&quot;token operator&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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The browser might have some native functionality bound to the arrow keys, home or end. The element calls &lt;code&gt;preventDefault()&lt;/code&gt; to prevent the browser from taking any actions.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&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;&lt;p&gt;Select the new tab, that has been determined in the switch-case.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTab&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;code&gt;_onClick()&lt;/code&gt; handles clicks inside the tab panel.&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 function&quot;&gt;_onClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;If the click was not targeted on a tab element itself, it was a click inside the a panel or on empty space. Nothing to do.&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&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;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tab&#39;&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If it was on a tab element, though, select that tab.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-tabs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTabs&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;&lt;code&gt;howtoTabCounter&lt;/code&gt; counts the number of &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; instances created. The number is used to generated new, unique IDs.&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;let&lt;/span&gt; howtoTabCounter &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;HowtoTab&lt;/code&gt; is a tab for a &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; tab panel. &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; should always be used with &lt;code&gt;role=&amp;quot;heading&amp;quot;&lt;/code&gt; in the markup so that the semantics remain useable when JavaScript is failing.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; declares which &lt;code&gt;&amp;lt;howto-panel&amp;gt;&lt;/code&gt; it belongs to by using that panel’s ID as the value for the aria-controls attribute.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; will automatically generate a unique ID if none is specified.&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTab&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observedAttributes&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;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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 function&quot;&gt;constructor&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;super&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;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;connectedCallback&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;&lt;p&gt;If this is executed, JavaScript is working and the element changes its role to &lt;code&gt;tab&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tab&#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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &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 string&quot;&gt;howto-tab-generated-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;howtoTabCounter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Set a well-defined initial state.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;false&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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;1&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;Check if a property has an instance value. If so, copy the value, and delete the instance property so it doesn&#39;t shadow the class property setter. Finally, pass the value to the class property setter so it can trigger any side effects. This is to safe guard against cases where, for instance, a framework may have added the element to the page and set a value on one of its properties, but lazy loaded its definition. Without this guard, the upgraded element would miss that property and the instance property would prevent the class property setter from ever being called.&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 function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prop&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prop&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;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&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;delete&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&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;Properties and their corresponding attributes should mirror one another. To this effect, the property setter for &lt;code&gt;selected&lt;/code&gt; handles truthy/falsy values and reflects those to the state of the attribute. It’s important to note that there are no side effects taking place in the property setter. For example, the setter does not set &lt;code&gt;aria-selected&lt;/code&gt;. Instead, that work happens in the &lt;code&gt;attributeChangedCallback&lt;/code&gt;. As a general rule, make property setters very dumb, and if setting a property or attribute should cause a side effect (like setting a corresponding ARIA attribute) do that work in the &lt;code&gt;attributeChangedCallback()&lt;/code&gt;. This will avoid having to manage complex attribute/property reentrancy scenarios.&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 function&quot;&gt;attributeChangedCallback&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;const&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-selected&#39;&lt;/span&gt;&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value &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 operator&quot;&gt;:&lt;/span&gt; &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;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 keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&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;      value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&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;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;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#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;else&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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 keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selected&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;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-tab&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTab&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;let&lt;/span&gt; howtoPanelCounter &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;HowtoPanel&lt;/code&gt; is a panel for a &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; tab panel.&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoPanel&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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 function&quot;&gt;constructor&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;super&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;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tabpanel&#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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &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 string&quot;&gt;howto-panel-generated-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;howtoPanelCounter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&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;br /&gt;&lt;br /&gt;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-panel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoPanel&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;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>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-tooltip</title>
    <link href="https://web.dev/components-howto-tooltip/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-howto-tooltip/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tooltip&amp;gt;&lt;/code&gt; is a popup that displays information related to an element
when the element receives keyboard focus or the mouse hovers over it.
The element that triggers the tooltip references the tooltip element with
&lt;code&gt;aria-describedby&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The element self-applies the role &lt;code&gt;tooltip&lt;/code&gt; and sets &lt;code&gt;tabindex&lt;/code&gt; to -1, as the
tooltip itself can never be focused.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#tooltip&quot; rel=&quot;noopener&quot;&gt;Tooltip pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-tooltip/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&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;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&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;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&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;name&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;Your name:&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;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&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;tp1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tooltip&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp1&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;Ideally your name is Batman&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;howto-tooltip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&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;cheese&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;Favourite type of cheese: &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;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cheese&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&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;tp2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tooltip&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp2&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;Help I am trapped inside a tooltip message&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;howto-tooltip&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;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTooltip&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&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 constructor does work that needs to be executed exactly once.&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 function&quot;&gt;constructor&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;super&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;&lt;p&gt;These functions are used in a bunch of places, and always need to bind the correct this reference, so do it once.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_show &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_hide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&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;&lt;code&gt;connectedCallback()&lt;/code&gt; fires when the element is inserted into the DOM. It&#39;s a good place to set the initial role, tabindex, internal state, and install event listeners.&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 function&quot;&gt;connectedCallback&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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tooltip&#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 keyword&quot;&gt;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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;1&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_hide&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;&lt;p&gt;The element that triggers the tooltip references the tooltip element with &lt;code&gt;aria-describedby&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&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;[aria-describedby=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;]&#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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The tooltip needs to listen to focus/blur events from the target, as well as hover events over the target.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;focus&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_show&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;blur&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseenter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_show&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseleave&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide&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;code&gt;disconnectedCallback()&lt;/code&gt; unregisters the event listeners that were set up in &lt;code&gt;connectedCallback()&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 function&quot;&gt;disconnectedCallback&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;if&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Remove the existing listeners, so that they don&#39;t trigger even though there&#39;s no tooltip to show.&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;focus&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_show&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;blur&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseenter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_show&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseleave&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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 function&quot;&gt;_show&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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 function&quot;&gt;_hide&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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 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;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-tooltip&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTooltip&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>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – Overview</title>
    <link href="https://web.dev/components-examples-overview/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-examples-overview/</id>
    <content type="html" mode="escaped">&lt;p&gt;&amp;quot;HowTo: Components&amp;quot; are a collection of web components that implement common UI
patterns. The purpose of these implementations is to be an educational resource.
You can read through the densely commented implementation of different
components and hopefully learn from them. Note that they are explicitly &lt;strong&gt;NOT&lt;/strong&gt;
a UI library and should &lt;strong&gt;NOT&lt;/strong&gt; be used in production.&lt;/p&gt;
&lt;h2 id=&quot;components&quot;&gt;Components &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#components&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-checkbox/&quot;&gt;&lt;code&gt;&amp;lt;howto-checkbox&amp;gt;&lt;/code&gt;&lt;/a&gt;: represents a boolean option in a form. The most common type of
checkbox is a dual-type which allows the user to toggle between two choices—checked and unchecked.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-tabs/&quot;&gt;&lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt;&lt;/a&gt;: limits visible content by separating it into multiple panels.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-tooltip/&quot;&gt;&lt;code&gt;&amp;lt;howto-tooltip&amp;gt;&lt;/code&gt;&lt;/a&gt;: a popup that displays information related to an element when the element
receives keyboard focus or the mouse hovers over it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;goals&quot;&gt;Goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our aim is to demonstrate best practices for writing robust components that are
accessible, performant, maintainable, and easy to style. Each component is
completely self-contained so it can serve as a reference implementation.&lt;/p&gt;
&lt;h3 id=&quot;accessibility&quot;&gt;Accessibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#accessibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The components closely follow the &lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/&quot; rel=&quot;noopener&quot;&gt;WAI ARIA Authoring
Practices&lt;/a&gt;, which is a guide to
explain and show ARIA, the &lt;a href=&quot;https://www.w3.org/TR/wai-aria-1.1/&quot; rel=&quot;noopener&quot;&gt;Accessible Rich Internet Application
standard&lt;/a&gt;. If you are unfamiliar with ARIA,
&lt;a href=&quot;https://web.dev/semantics-aria/&quot;&gt;check out our introduction on
WebFundamentals&lt;/a&gt;.
Each component links to the relevant section of the Authoring Practices. While
not strictly necessary, we do recommend reading the section of the Authoring
Practices before diving into the code.&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In web development the term &amp;quot;performance&amp;quot; can be applied to a multitude of
things. In the context of &lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt;, performance mostly refers to animations
consistently running at 60fps, even on mobile devices.&lt;/p&gt;
&lt;h3 id=&quot;visual-flexibility&quot;&gt;Visual Flexibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#visual-flexibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As much as possible, components are not styled, except for layout or to indicate
a selected or active state. This is to keep the implementation visually flexible
and focused. By not spending time on decoration, we limit the code to only what
is absolutely necessary to make the component function. If any style is required
for the component to function, the style will be marked with a comment
explaining why that is.&lt;/p&gt;
&lt;h3 id=&quot;maintainable-code&quot;&gt;Maintainable code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#maintainable-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As HowTo: Components is a reference
implementation, we spent extra time on writing readable and easily
comprehensible code that is densely commented.&lt;/p&gt;
&lt;h2 id=&quot;non-goals&quot;&gt;Non-Goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#non-goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;be-a-library-framework-toolkit&quot;&gt;Be a library / framework / toolkit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-a-library-framework-toolkit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt; components are not published on npm, bower or any other platform
because they are not meant to be used in production. For the sake of terse,
readable code, we are using modern JavaScript APIs and are supporting modern
browsers which implement the Web Components standards. You
will be able to adapt the code to fit your own needs after reading these
implementations.&lt;/p&gt;
&lt;h3 id=&quot;be-backwards-compatible&quot;&gt;Be backwards compatible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-backwards-compatible&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The code should not be relied on directly. We might, and very likely &lt;em&gt;will&lt;/em&gt;,
drastically change the implementation and API of any element if a better
implementation is discovered. This is a living resource where we can share,
explore, and discuss best practices for building web UIs.&lt;/p&gt;
&lt;h3 id=&quot;be-complete&quot;&gt;Be complete &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-complete&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We currently don&#39;t (and probably won&#39;t) implement *&lt;em&gt;all&lt;/em&gt; *components that can be
found in the WAI ARIA Authoring Practices. However, re-using the principles used
in other &lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt; components should enable readers to implement any components
that are missing.&lt;/p&gt;
</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>ResizeObserver: it’s like document.onresize for elements</title>
    <link href="https://web.dev/resize-observer/"/>
    <updated>2016-10-07T00:00:00Z</updated>
    <id>https://web.dev/resize-observer/</id>
    <content type="html" mode="escaped">&lt;p&gt;Before &lt;code&gt;ResizeObserver&lt;/code&gt;, you had to attach a listener to the document&#39;s &lt;code&gt;resize&lt;/code&gt;
event to get notified of any change of the viewport&#39;s dimensions. In the event
handler, you would then have to figure out which elements have been affected by
that change and call a specific routine to react appropriately. If you needed
the new dimensions of an element after a resize, you had to call
&lt;code&gt;getBoundingClientRect()&lt;/code&gt; or &lt;code&gt;getComputedStyle()&lt;/code&gt;, which can cause layout
thrashing if you don&#39;t take care of batching &lt;em&gt;all&lt;/em&gt; your reads and &lt;em&gt;all&lt;/em&gt; your
writes.&lt;/p&gt;
&lt;p&gt;This didn&#39;t even cover cases where elements change their size without the main
window having been resized. For example, appending new children, setting an
element&#39;s &lt;code&gt;display&lt;/code&gt; style to &lt;code&gt;none&lt;/code&gt;, or similar actions can change the size of
an element, its siblings, or its ancestors.&lt;/p&gt;
&lt;p&gt;This is why &lt;code&gt;ResizeObserver&lt;/code&gt; is a useful primitive. It reacts to changes in
size of any of the observed elements, independent of what caused the change.
It provides access to the new size of the observed elements too.&lt;/p&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 64, 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;
      64
    &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 69, 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;
      69
    &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 13.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;
      13.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/API/ResizeObserver#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;api&quot;&gt;API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All the APIs with the &lt;code&gt;Observer&lt;/code&gt; suffix we mentioned above share a simple API
design. &lt;code&gt;ResizeObserver&lt;/code&gt; is no exception. You create a &lt;code&gt;ResizeObserver&lt;/code&gt; object
and pass a callback to the constructor. The callback is passed an array of
&lt;code&gt;ResizeObserverEntry&lt;/code&gt; objects—one entry per observed element—which
contains the new dimensions for the element.&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; ro &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;ResizeObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entries&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;const&lt;/span&gt; cr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentRect&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Element:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Element size: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cr&lt;span class=&quot;token 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 x &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Element padding: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;top&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 interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;left&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;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;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;// Observe one or multiple elements&lt;/span&gt;&lt;br /&gt;ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someElement&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;some-details&quot;&gt;Some details &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#some-details&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;what-is-being-reported&quot;&gt;What is being reported? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#what-is-being-reported&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Generally, a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ResizeObserverEntry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;ResizeObserverEntry&lt;/code&gt;&lt;/a&gt;
reports the content box of an element through a property called
&lt;code&gt;contentRect&lt;/code&gt;, which returns a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/DOMRectReadOnly&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;DOMRectReadOnly&lt;/code&gt;&lt;/a&gt;
object. The content box is the box in which content can be placed. It is
the border box minus the padding.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram of the CSS box model.&quot; decoding=&quot;async&quot; height=&quot;562&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 727px) 727px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/CKxpe8LNq2CMPFdtLtVK.png?auto=format&amp;w=1454 1454w&quot; width=&quot;727&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;It&#39;s important to note that while &lt;code&gt;ResizeObserver&lt;/code&gt; &lt;em&gt;reports&lt;/em&gt; both the dimensions
of the &lt;code&gt;contentRect&lt;/code&gt; and the padding, it only &lt;em&gt;watches&lt;/em&gt; the &lt;code&gt;contentRect&lt;/code&gt;.
&lt;em&gt;Don&#39;t&lt;/em&gt; confuse &lt;code&gt;contentRect&lt;/code&gt; with the bounding box of the element. The bounding
box, as reported by &lt;code&gt;getBoundingClientRect()&lt;/code&gt;, is the box that contains the
entire element and its descendants. SVGs are an exception to the rule, where
&lt;code&gt;ResizeObserver&lt;/code&gt; will report the dimensions of the bounding box.&lt;/p&gt;
&lt;p&gt;As of Chrome 84, &lt;code&gt;ResizeObserverEntry&lt;/code&gt; has three new properties to provide more
detailed information. Each of these properties returns a &lt;code&gt;ResizeObserverSize&lt;/code&gt;
object containing a &lt;code&gt;blockSize&lt;/code&gt; property and an &lt;code&gt;inlineSize&lt;/code&gt; property. This
information is about the observered element at the time the callback is invoked.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;borderBoxSize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contentBoxSize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;devicePixelContentBoxSize&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these items return read-only arrays because in the future it&#39;s hoped that
they can support elements that have multiple fragments, which occur in
multi-column scenarios. For now, these arrays will only contain one element.&lt;/p&gt;
&lt;p&gt;Platform support for these properties is limited, but &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ResizeObserverEntry#Browser_compatibility&quot; rel=&quot;noopener&quot;&gt;Firefox already
supports&lt;/a&gt;
the first two.&lt;/p&gt;
&lt;h3 id=&quot;when-is-it-being-reported&quot;&gt;When is it being reported? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#when-is-it-being-reported&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The spec proscribes that &lt;code&gt;ResizeObserver&lt;/code&gt; should process all resize events
before paint and after layout. This makes the callback of a &lt;code&gt;ResizeObserver&lt;/code&gt; the
ideal place to make changes to your page&#39;s layout. Because &lt;code&gt;ResizeObserver&lt;/code&gt;
processing happens between layout and paint, doing so will only invalidate
layout, not paint.&lt;/p&gt;
&lt;h3 id=&quot;gotcha&quot;&gt;Gotcha &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#gotcha&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You might be asking yourself: what happens if I change the size of an observed
element inside the callback to &lt;code&gt;ResizeObserver&lt;/code&gt;? The answer is: you will trigger
another call to the callback right away. Fortunately, &lt;code&gt;ResizeObserver&lt;/code&gt; has a
mechanism to avoid infinite callback loops and cyclic dependencies. Changes will
only be processed in the same frame if the resized element is deeper in the DOM
tree than the &lt;em&gt;shallowest&lt;/em&gt; element processed in the previous callback.
Otherwise, they&#39;ll get deferred to the next frame.&lt;/p&gt;
&lt;h2 id=&quot;application&quot;&gt;Application &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#application&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One thing that &lt;code&gt;ResizeObserver&lt;/code&gt; allows you to do is to implement per-element
media queries. By observing elements, you can imperatively define your
design breakpoints and change an element&#39;s styles. In the following
&lt;a href=&quot;https://googlechrome.github.io/samples/resizeobserver/&quot; rel=&quot;noopener&quot;&gt;example&lt;/a&gt;, the second box
will change its border radius according to its width.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/resizeobserver/elem-mq_vp8.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/resizeobserver/elem-mq_x264.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
&lt;/figure&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;const&lt;/span&gt; ro &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;ResizeObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entries&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;    entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&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;borderRadius &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&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;&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;250&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentRect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;px&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span 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;// Only observe the second box&lt;/span&gt;&lt;br /&gt;ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&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;.box:nth-child(2)&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Another interesting example to look at is a chat window. The problem that arises
in a typical top-to-bottom conversation layout is scroll positioning. To avoid
confusing the user, it is helpful if the window sticks to the bottom of the
conversation, where the newest messages appear. Additionally, any kind of layout
change (think of a phone going from landscape to portrait or vice versa) should
achieve the same.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ResizeObserver&lt;/code&gt; allows you to write a &lt;em&gt;single&lt;/em&gt; piece of code that takes care of
&lt;em&gt;both&lt;/em&gt; scenarios. Resizing the window is an event that a &lt;code&gt;ResizeObserver&lt;/code&gt; can
capture by definition, but calling &lt;code&gt;appendChild()&lt;/code&gt; also resizes that element
(unless&lt;code&gt;overflow: hidden&lt;/code&gt; is set), because it needs to make space for the new
elements. With this in mind, it takes very few lines to achieve the desired
effect:&lt;/p&gt;
&lt;figure&gt;
 &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
   &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/resizeobserver/chat_vp8.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
   &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/resizeobserver/chat_x264.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
 &lt;/video&gt;
&lt;/figure&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;const&lt;/span&gt; ro &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;ResizeObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;scrollingElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollTop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollingElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollHeight&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;// Observe the scrollingElement for when the window gets resized&lt;/span&gt;&lt;br /&gt;ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&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;scrollingElement&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;// Observe the timeline to process new messages&lt;/span&gt;&lt;br /&gt;ro&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeline&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;Pretty neat, huh?&lt;/p&gt;
&lt;p&gt;From here, I could add more code to handle the case where the user has scrolled
up manually and wants scrolling to stick to &lt;em&gt;that&lt;/em&gt; message when a new message
comes in.&lt;/p&gt;
&lt;p&gt;Another use case is for any kind of custom element that is doing its own layout.
Until &lt;code&gt;ResizeObserver&lt;/code&gt;, there was no reliable way to get notified when its
dimensions change so its children can be laid out again.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resize-observer/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ResizeObserver&lt;/code&gt; is available in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ResizeObserver#Browser_compatibility&quot; rel=&quot;noopener&quot;&gt;most major
browsers&lt;/a&gt;.
In some cases, that availability is quite recent. There are &lt;a href=&quot;https://github.com/WICG/ResizeObserver/issues/3&quot; rel=&quot;noopener&quot;&gt;some polyfills
available&lt;/a&gt; but it is not
possible to completely duplicate the functionality of &lt;code&gt;ResizeObserver&lt;/code&gt;. Current
implementations either rely on polling or on adding sentinel elements to the
DOM. The former will drain your battery on mobile by keeping the CPU busy while
the latter modifies your DOM and might mess up styling and other DOM-reliant
code.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@markusspiske?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Markus
Spiske&lt;/a&gt;
on
&lt;a href=&quot;https://unsplash.com/s/photos/observe-growth?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author><author>
      <name>Joe Medley</name>
    </author>
  </entry>
  
  <entry>
    <title>Introduction to HTTP/2</title>
    <link href="https://web.dev/performance-http2/"/>
    <updated>2016-09-29T00:00:00Z</updated>
    <id>https://web.dev/performance-http2/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The following content is an excerpt from &lt;a href=&quot;http://shop.oreilly.com/product/0636920028048.do&quot;&gt;High Performance Browser Networking&lt;/a&gt; (O&#39;Reilly, Ilya Grigorik). For full version and related content, see &lt;a href=&quot;https://hpbn.co/&quot;&gt;hpbn.co&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;HTTP/2 will make our applications faster, simpler, and more robust — a rare
combination — by allowing us to undo many of the HTTP/1.1 workarounds previously
done within our applications and address these concerns within the transport
layer itself. Even better, it also opens up a number of entirely new
opportunities to optimize our applications and improve performance!&lt;/p&gt;
&lt;p&gt;The primary goals for HTTP/2 are to reduce latency by enabling full request and
response multiplexing, minimize protocol overhead via efficient compression of
HTTP header fields, and add support for request prioritization and server push.
To implement these requirements, there is a large supporting cast of other
protocol enhancements, such as new flow control, error handling, and upgrade
mechanisms, but these are the most important features that every web developer
should understand and leverage in their applications.&lt;/p&gt;
&lt;p&gt;HTTP/2 does not modify the application semantics of HTTP in any way. All the
core concepts, such as HTTP methods, status codes, URIs, and header fields,
remain in place. Instead, HTTP/2 modifies how the data is formatted (framed) and
transported between the client and server, both of which manage the entire
process, and hides all the complexity from our applications within the new
framing layer. As a result, all existing applications can be delivered without
modification.&lt;/p&gt;
&lt;h2 id=&quot;why-not-http12&quot;&gt;Why not HTTP/1.2? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#why-not-http12&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To achieve the performance goals set by the HTTP Working Group, HTTP/2
introduces a new binary framing layer that is not backward compatible with
previous HTTP/1.x servers and clients—hence the major protocol version increment
to HTTP/2.&lt;/p&gt;
&lt;p&gt;That said, unless you are implementing a web server (or a custom client) by
working with raw TCP sockets, then you won’t see any difference: all the new,
low-level framing is performed by the client and server on your behalf. The only
observable differences will be improved performance and availability of new
capabilities like request prioritization, flow control, and server push.&lt;/p&gt;
&lt;h2 id=&quot;a-brief-history-of-spdy-and-http2&quot;&gt;A brief history of SPDY and HTTP/2 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#a-brief-history-of-spdy-and-http2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SPDY was an experimental protocol, developed at Google and announced in
mid 2009, whose primary goal was to try to reduce the load latency of web pages
by addressing some of the well-known performance limitations of HTTP/1.1.
Specifically, the outlined project goals were set as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Target a 50% reduction in page load time (PLT).&lt;/li&gt;
&lt;li&gt;Avoid the need for any changes to content by website authors.&lt;/li&gt;
&lt;li&gt;Minimize deployment complexity, and avoid changes in network infrastructure.&lt;/li&gt;
&lt;li&gt;Develop this new protocol in partnership with the open-source community.&lt;/li&gt;
&lt;li&gt;Gather real performance data to (in)validate the experimental protocol.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; To achieve the 50% PLT improvement, SPDY aimed to make more efficient use of the underlying TCP connection by introducing a new binary framing layer to enable request and response multiplexing, prioritization, and header compression; see &lt;a href=&quot;https://hpbn.co/primer-on-web-performance/#latency-as-a-performance-bottleneck&quot;&gt;Latency as a Performance Bottleneck&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Not long after the initial announcement, Mike Belshe and Roberto Peon, both
software engineers at Google, shared their first results, documentation, and
source code for the experimental implementation of the new SPDY protocol:&lt;/p&gt;
&lt;p&gt;So far we have only tested SPDY in lab conditions. The initial results are
very encouraging: when we download the top 25 websites over simulated home
network connections, we see a significant improvement in performance—pages
loaded up to 55% faster.
&lt;a href=&quot;https://blog.chromium.org/2009/11/2x-faster-web.html&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;(Chromium Blog)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fast-forward to 2012 and the new experimental protocol was supported in Chrome,
Firefox, and Opera, and a rapidly growing number of sites, both large (for example,
Google, Twitter, Facebook) and small, were deploying SPDY within their
infrastructure. In effect, SPDY was on track to become a de facto standard
through growing industry adoption.&lt;/p&gt;
&lt;p&gt;Observing this trend, the HTTP Working Group (HTTP-WG) kicked off a new
effort to take the lessons learned from SPDY, build and improve on them, and
deliver an official &amp;quot;HTTP/2&amp;quot; standard. A new charter was drafted, an open call
for HTTP/2 proposals was made, and after a lot of discussion within the working
group, the SPDY specification was adopted as a starting point for the new HTTP/2
protocol.&lt;/p&gt;
&lt;p&gt;Over the next few years SPDY and HTTP/2 continued to coevolve in parallel,
with SPDY acting as an experimental branch that was used to test new features
and proposals for the HTTP/2 standard. What looks good on paper may not work in
practice, and vice versa, and SPDY offered a route to test and evaluate each
proposal before its inclusion in the HTTP/2 standard. In the end, this process
spanned three years and resulted in a over a dozen intermediate drafts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;March 2012: Call for proposals for HTTP/2&lt;/li&gt;
&lt;li&gt;November 2012: First draft of HTTP/2 (based on SPDY)&lt;/li&gt;
&lt;li&gt;August 2014: HTTP/2 draft-17 and HPACK draft-12 are published&lt;/li&gt;
&lt;li&gt;August 2014: Working Group last call for HTTP/2&lt;/li&gt;
&lt;li&gt;February 2015: IESG approved HTTP/2 and HPACK drafts&lt;/li&gt;
&lt;li&gt;May 2015: RFC 7540 (HTTP/2) and RFC 7541 (HPACK) are published&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In early 2015 the IESG reviewed and approved the new HTTP/2 standard for
publication. Shortly after that, the Google Chrome team announced their schedule
to deprecate SPDY and NPN extension for TLS:&lt;/p&gt;
&lt;p&gt;HTTP/2&#39;s primary changes from HTTP/1.1 focus on improved performance. Some key
features such as multiplexing, header compression, prioritization and protocol
negotiation evolved from work done in an earlier open, but non-standard
protocol named SPDY. Chrome has supported SPDY since Chrome 6, but since most
of the benefits are present in HTTP/2, it’s time to say goodbye. We plan to
remove support for SPDY in early 2016, and to also remove support for the TLS
extension named NPN in favor of ALPN in Chrome at the same time. Server
developers are strongly encouraged to move to HTTP/2 and ALPN.&lt;/p&gt;
&lt;p&gt;We’re happy to have contributed to the open standards process that led to
HTTP/2, and hope to see wide adoption given the broad industry engagement on
standardization and implementation. &lt;a href=&quot;https://blog.chromium.org/2015/02/hello-http2-goodbye-spdy.html&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;(Chromium
Blog)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The coevolution of SPDY and HTTP/2 enabled server, browser, and site developers
to gain real-world experience with the new protocol as it was being developed.
As a result, the HTTP/2 standard is one of the best and most extensively tested
standards right out of the gate. By the time HTTP/2 was approved by the IESG,
there were dozens of thoroughly tested and production-ready client and server
implementations. In fact, just weeks after the final protocol was approved, many
users were already enjoying its benefits as several popular browsers (and many
sites) deployed full HTTP/2 support.&lt;/p&gt;
&lt;h2 id=&quot;design-and-technical-goals&quot;&gt;Design and technical goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#design-and-technical-goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Earlier versions of the HTTP protocol were intentionally designed for simplicity
of implementation: HTTP/0.9 was a one-line protocol to bootstrap the World Wide
Web; HTTP/1.0 documented the popular extensions to HTTP/0.9 in an informational
standard; HTTP/1.1 introduced an official IETF standard; see
&lt;a href=&quot;https://hpbn.co/brief-history-of-http/&quot; rel=&quot;noopener&quot;&gt;Brief History of HTTP&lt;/a&gt;.
As such, HTTP/0.9-1.x delivered exactly what it set out to do: HTTP is one of
the most widely adopted application protocols on the Internet.&lt;/p&gt;
&lt;p&gt;Unfortunately, implementation simplicity also came at a cost of application
performance: HTTP/1.x clients need to use multiple connections to achieve
concurrency and reduce latency; HTTP/1.x does not compress request and response
headers, causing unnecessary network traffic; HTTP/1.x does not allow effective
resource prioritization, resulting in poor use of the underlying TCP connection;
and so on.&lt;/p&gt;
&lt;p&gt;These limitations were not fatal, but as the web applications continued to grow
in their scope, complexity, and importance in our everyday lives, they imposed a
growing burden on both the developers and users of the web, which is the exact
gap that HTTP/2 was designed to address:&lt;/p&gt;
&lt;p&gt;HTTP/2 enables a more efficient use of network resources and a reduced
perception of latency by introducing header field compression and allowing
multiple concurrent exchanges on the same connection… Specifically, it allows
interleaving of request and response messages on the same connection and uses
an efficient coding for HTTP header fields. It also allows prioritization of
requests, letting more important requests complete more quickly, further
improving performance.&lt;/p&gt;
&lt;p&gt;The resulting protocol is more friendly to the network, because fewer TCP
connections can be used in comparison to HTTP/1.x. This means less competition
with other flows, and longer-lived connections, which in turn leads to better
utilization of available network capacity. Finally, HTTP/2 also enables more
efficient processing of messages through use of binary message framing.
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-http2-17&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;(Hypertext Transfer Protocol version 2, Draft 17)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is important to note that HTTP/2 is extending, not replacing, the previous
HTTP standards. The application semantics of HTTP are the same, and no changes
were made to the offered functionality or core concepts such as HTTP methods,
status codes, URIs, and header fields. These changes were explicitly out of scope
for the HTTP/2 effort. That said, while the high-level API remains the same, it
is important to understand how the low-level changes address the performance
limitations of the previous protocols. Let’s take a brief tour of the binary
framing layer and its features.&lt;/p&gt;
&lt;h2 id=&quot;binary-framing-layer&quot;&gt;Binary framing layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#binary-framing-layer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the core of all performance enhancements of HTTP/2 is the new binary framing
layer, which dictates how the HTTP messages are encapsulated and transferred
between the client and server.&lt;/p&gt;
&lt;img alt=&quot;HTTP/2 binary framing layer&quot; decoding=&quot;async&quot; height=&quot;150&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2a2cw0nAXe9Mi5txM4Mw.svg&quot; width=&quot;291&quot; /&gt;
&lt;p&gt;The &amp;quot;layer&amp;quot; refers to a design choice to introduce a new optimized encoding
mechanism between the socket interface and the higher HTTP API exposed to our
applications: the HTTP semantics, such as verbs, methods, and headers, are
unaffected, but the way they are encoded while in transit is different.
Unlike the newline delimited plaintext HTTP/1.x protocol, all HTTP/2
communication is split into smaller messages and frames, each of which is
encoded in binary format.&lt;/p&gt;
&lt;p&gt;As a result, both client and server must use the new binary encoding mechanism
to understand each other: an HTTP/1.x client won’t understand an HTTP/2 only
server, and vice versa. Thankfully, our applications remain blissfully unaware
of all these changes, as the client and server perform all the necessary framing
work on our behalf.&lt;/p&gt;
&lt;h2 id=&quot;streams,-messages,-and-frames&quot;&gt;Streams, messages, and frames &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#streams,-messages,-and-frames&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The introduction of the new binary framing mechanism changes how the data is
exchanged between the client and server. To describe this process, let’s
familiarize ourselves with the HTTP/2 terminology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Stream&lt;/em&gt;: A bidirectional flow of bytes within an established connection,
which may carry one or more messages.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Message&lt;/em&gt;: A complete sequence of frames that map to a logical request or response message.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Frame&lt;/em&gt;: The smallest unit of communication in HTTP/2, each containing a frame header, which
at a minimum identifies the stream to which the frame belongs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The relation of these terms can be summarized as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All communication is performed over a single TCP connection that can carry any number of
bidirectional streams.&lt;/li&gt;
&lt;li&gt;Each stream has a unique identifier and optional priority information that is used to carry
bidirectional messages.&lt;/li&gt;
&lt;li&gt;Each message is a logical HTTP message, such as a request, or response, which consists of
one or more frames.&lt;/li&gt;
&lt;li&gt;The frame is the smallest unit of communication that carries a specific type of data—e.g.,
HTTP headers, message payload, and so on. Frames from different streams may be interleaved
and then reassembled via the embedded stream identifier in the header of each frame.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;HTTP/2 streams, messages, and frames&quot; decoding=&quot;async&quot; height=&quot;150&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/0bfdEw00aKXFDxT0yEKt.svg&quot; width=&quot;184&quot; /&gt;
&lt;p&gt;In short, HTTP/2 breaks down the HTTP protocol communication into an exchange of
binary-encoded frames, which are then mapped to messages that belong to a
particular stream, all of which are multiplexed within a single TCP
connection. This is the foundation that enables all other features and
performance optimizations provided by the HTTP/2 protocol.&lt;/p&gt;
&lt;h2 id=&quot;request-and-response-multiplexing&quot;&gt;Request and response multiplexing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#request-and-response-multiplexing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With HTTP/1.x, if the client wants to make multiple parallel requests to improve
performance, then multiple TCP connections must be used (see
&lt;a href=&quot;https://hpbn.co/http1x/#using-multiple-tcp-connections&quot; rel=&quot;noopener&quot;&gt;Using Multiple TCP Connections&lt;/a&gt;
). This behavior is a direct consequence of the HTTP/1.x delivery model, which
ensures that only one response can be delivered at a time (response queuing) per
connection. Worse, this also results in head-of-line blocking and inefficient
use of the underlying TCP connection.&lt;/p&gt;
&lt;p&gt;The new binary framing layer in HTTP/2 removes these limitations, and enables
full request and response multiplexing, by allowing the client and server to
break down an HTTP message into independent frames, interleave them, and then
reassemble them on the other end.&lt;/p&gt;
&lt;img alt=&quot;HTTP/2 request and response multiplexing within a shared connection&quot; decoding=&quot;async&quot; height=&quot;83&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/4RwALfscCwB7MDa1bGsV.svg&quot; width=&quot;300&quot; /&gt;
&lt;p&gt;The snapshot captures multiple streams in flight within the same connection. The
client is transmitting a &lt;code&gt;DATA&lt;/code&gt; frame (stream 5) to the server, while the server
is transmitting an interleaved sequence of frames to the client for streams 1
and 3. As a result, there are three parallel streams in flight.&lt;/p&gt;
&lt;p&gt;The ability to break down an HTTP message into independent frames, interleave
them, and then reassemble them on the other end is the single most important
enhancement of HTTP/2. In fact, it introduces a ripple effect of numerous
performance benefits across the entire stack of all web technologies, enabling
us to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interleave multiple requests in parallel without blocking on any one.&lt;/li&gt;
&lt;li&gt;Interleave multiple responses in parallel without blocking on any one.&lt;/li&gt;
&lt;li&gt;Use a single connection to deliver multiple requests and responses in parallel.&lt;/li&gt;
&lt;li&gt;Remove unnecessary HTTP/1.x workarounds (see
&lt;a href=&quot;https://hpbn.co/optimizing-application-delivery/#optimizing-for-http1x&quot; rel=&quot;noopener&quot;&gt;Optimizing for HTTP/1.x&lt;/a&gt;,
such as concatenated files, image sprites, and domain sharding).&lt;/li&gt;
&lt;li&gt;Deliver lower page load times by eliminating unnecessary latency and improving
utilization of available network capacity.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;And much more…&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new binary framing layer in HTTP/2 resolves the head-of-line blocking
problem found in HTTP/1.x and eliminates the need for multiple connections to
enable parallel processing and delivery of requests and responses. As a result,
this makes our applications faster, simpler, and cheaper to deploy.&lt;/p&gt;
&lt;h2 id=&quot;stream-prioritization&quot;&gt;Stream prioritization &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#stream-prioritization&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once an HTTP message can be split into many individual frames, and we allow for
frames from multiple streams to be multiplexed, the order in which the frames
are interleaved and delivered both by the client and server becomes a critical
performance consideration. To facilitate this, the HTTP/2 standard allows each
stream to have an associated weight and dependency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each stream may be assigned an integer weight between 1 and 256.&lt;/li&gt;
&lt;li&gt;Each stream may be given an explicit dependency on another stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The combination of stream dependencies and weights allows the client to
construct and communicate a &amp;quot;prioritization tree&amp;quot; that expresses how it would
prefer to receive responses. In turn, the server can use this information to
prioritize stream processing by controlling the allocation of CPU, memory, and
other resources, and once the response data is available, allocation of
bandwidth to ensure optimal delivery of high-priority responses to the client.&lt;/p&gt;
&lt;img alt=&quot;HTTP/2 stream dependencies and weights&quot; decoding=&quot;async&quot; height=&quot;127&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ydLldhPadjknvvrUiCai.svg&quot; width=&quot;300&quot; /&gt;
&lt;p&gt;A stream dependency within HTTP/2 is declared by referencing the unique
identifier of another stream as its parent; if the identifier is omitted the
stream is said to be dependent on the &amp;quot;root stream&amp;quot;. Declaring a stream
dependency indicates that, if possible, the parent stream should be allocated
resources ahead of its dependencies. In other words, &amp;quot;Please process and deliver
response D before response C&amp;quot;.&lt;/p&gt;
&lt;p&gt;Streams that share the same parent (in other words, sibling streams) should be allocated
resources in proportion to their weight. For example, if stream A has a weight
of 12 and its one sibling B has a weight of 4, then to determine the proportion
of the resources that each of these streams should receive:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sum all the weights: &lt;code&gt;4 + 12 = 16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Divide each stream weight by the total weight: &lt;code&gt;A = 12/16, B = 4/16&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thus, stream A should receive three-quarters and stream B should receive one-
quarter of available resources; stream B should receive one-third of the
resources allocated to stream A. Let’s work through a few more hands-on examples
in the image above. From left to right:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Neither stream A nor B specifies a parent dependency and are said to be dependent
on the implicit &amp;quot;root stream&amp;quot;; A has a weight of 12, and B has a weight of 4.
Thus, based on proportional weights: stream B should receive one-third of the
resources allocated to stream A.&lt;/li&gt;
&lt;li&gt;Stream D is dependent on the root stream; C is dependent on D. Thus, D should
receive full allocation of resources ahead of C. The weights are inconsequential
because C’s dependency communicates a stronger preference.&lt;/li&gt;
&lt;li&gt;Stream D should receive full allocation of resources ahead of C; C should receive
full allocation of resources ahead of A and B; stream B should receive one-third of
the resources allocated to stream A.&lt;/li&gt;
&lt;li&gt;Stream D should receive full allocation of resources ahead of E and C; E and C
should receive equal allocation ahead of A and B; A and B should receive proportional
allocation based on their weights.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As the above examples illustrate, the combination of stream dependencies and
weights provides an expressive language for resource prioritization, which is a
critical feature for improving browsing performance where we have many resource
types with different dependencies and weights. Even better, the HTTP/2 protocol
also allows the client to update these preferences at any point, which enables
further optimizations in the browser. In other words, we can change dependencies
and reallocate weights in response to user interaction and other signals.&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; Stream dependencies and weights express a transport preference, not a requirement, and as such do not guarantee a particular processing or transmission order. That is, the client cannot force the server to process the stream in a particular order using stream prioritization. While this may seem counterintuitive, it is in fact the desired behavior. We do not want to block the server from making progress on a lower priority resource if a higher priority resource is blocked. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;one-connection-per-origin&quot;&gt;One connection per origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#one-connection-per-origin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the new binary framing mechanism in place, HTTP/2 no longer needs multiple
TCP connections to multiplex streams in parallel; each stream is split into many
frames, which can be interleaved and prioritized. As a result, all HTTP/2
connections are persistent, and only one connection per origin is required,
which offers numerous performance benefits.&lt;/p&gt;
&lt;p&gt;For both SPDY and HTTP/2 the killer feature is arbitrary multiplexing on a
single well congestion controlled channel. It amazes me how important this is
and how well it works. One great metric around that which I enjoy is the
fraction of connections created that carry just a single HTTP transaction (and
thus make that transaction bear all the overhead). For HTTP/1 74% of our
active connections carry just a single transaction—persistent connections just
aren’t as helpful as we all want. But in HTTP/2 that number plummets to 25%.
That’s a huge win for overhead reduction. &lt;a href=&quot;http://bitsup.blogspot.co.uk/2015/02/http2-is-live-in-firefox.html&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;(HTTP/2 is Live in Firefox, Patrick
McManus)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Most HTTP transfers are short and bursty, whereas TCP is optimized for long-
lived, bulk data transfers. By reusing the same connection, HTTP/2 is able to
both make more efficient use of each TCP connection, and also significantly
reduce the overall protocol overhead. Further, the use of fewer connections
reduces the memory and processing footprint along the full connection path
(in other words, client, intermediaries, and origin servers). This reduces the overall
operational costs and improves network utilization and capacity. As a result,
the move to HTTP/2 should not only reduce network latency, but also help
improve throughput and reduce the operational costs.&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; Reduced number of connections is a particularly important feature for improving performance of HTTPS deployments: this translates to fewer expensive TLS handshakes, better session reuse, and an overall reduction in required client and server resources. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;flow-control&quot;&gt;Flow control &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#flow-control&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Flow control is a mechanism to prevent the sender from overwhelming the receiver
with data it may not want or be able to process: the receiver may be busy, under
heavy load, or may only be willing to allocate a fixed amount of resources for a
particular stream. For example, the client may have requested a large video
stream with high priority, but the user has paused the video and the client now
wants to pause or throttle its delivery from the server to avoid fetching and
buffering unnecessary data. Alternatively, a proxy server may have fast
downstream and slow upstream connections and similarly wants to regulate how
quickly the downstream delivers data to match the speed of upstream to control
its resource usage; and so on.&lt;/p&gt;
&lt;p&gt;Do the above requirements remind you of TCP flow control? They should, as the
problem is effectively identical (see
&lt;a href=&quot;https://hpbn.co/building-blocks-of-tcp/#flow-control&quot; rel=&quot;noopener&quot;&gt;Flow Control&lt;/a&gt;). However,
because the HTTP/2 streams are multiplexed within a single TCP connection, TCP
flow control is both not granular enough, and does not provide the necessary
application-level APIs to regulate the delivery of individual streams. To
address this, HTTP/2 provides a set of simple building blocks that allow the
client and server to implement their own stream- and connection-level flow
control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flow control is directional. Each receiver may choose to set any window size
that it desires for each stream and the entire connection.&lt;/li&gt;
&lt;li&gt;Flow control is credit-based. Each receiver advertises its initial connection
and stream flow control window (in bytes), which is reduced whenever the
sender emits a &lt;code&gt;DATA&lt;/code&gt; frame and incremented via a &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; frame sent
by the receiver.&lt;/li&gt;
&lt;li&gt;Flow control cannot be disabled. When the HTTP/2 connection is established the
client and server exchange &lt;code&gt;SETTINGS&lt;/code&gt; frames, which set the flow control window
sizes in both directions. The default value of the flow control window is set
to 65,535 bytes, but the receiver can set a large maximum window size
(&lt;code&gt;2^31-1&lt;/code&gt; bytes) and maintain it by sending a &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; frame whenever any
data is received.&lt;/li&gt;
&lt;li&gt;Flow control is hop-by-hop, not end-to-end. That is, an intermediary can use it
to control resource use and implement resource allocation mechanisms based on
own criteria and heuristics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTP/2 does not specify any particular algorithm for implementing flow control.
Instead, it provides the simple building blocks and defers the implementation to
the client and server, which can use it to implement custom strategies to
regulate resource use and allocation, as well as implement new delivery
capabilities that may help improve both the real and perceived performance (see
&lt;a href=&quot;https://hpbn.co/primer-on-web-performance/#speed-performance-and-human-perception&quot; rel=&quot;noopener&quot;&gt;Speed, Performance, and Human Perception&lt;/a&gt;)
of our web applications.&lt;/p&gt;
&lt;p&gt;For example, application-layer flow control allows the browser to fetch only a
part of a particular resource, put the fetch on hold by reducing the stream flow
control window down to zero, and then resume it later. In other words, it allows
the browser to fetch a preview or first scan of an image, display it and allow
other high priority fetches to proceed, and resume the fetch once more critical
resources have finished loading.&lt;/p&gt;
&lt;h2 id=&quot;server-push&quot;&gt;Server push &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#server-push&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another powerful new feature of HTTP/2 is the ability of the server to send
multiple responses for a single client request. That is, in addition to the
response to the original request, the server can push additional resources to
the client (Figure 12-5), without the client having to request each one
explicitly.&lt;/p&gt;
&lt;img alt=&quot;Server initiates new streams (promises) for push resources&quot; decoding=&quot;async&quot; height=&quot;117&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/o6lgFK9DiyJmdWwyJnyy.svg&quot; width=&quot;300&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; HTTP/2 breaks away from the strict request-response semantics and enables one-to-many and server-initiated push workflows that open up a world of new interaction possibilities both within and outside the browser. This is an enabling feature that will have important long-term consequences both for how we think about the protocol, and where and how it is used. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Why would we need such a mechanism in a browser? A typical web application
consists of dozens of resources, all of which are discovered by the client by
examining the document provided by the server. As a result, why not eliminate
the extra latency and let the server push the associated resources ahead of
time? The server already knows which resources the client will require; that’s
server push.&lt;/p&gt;
&lt;p&gt;In fact, if you have ever inlined a CSS, JavaScript, or any other asset via a
data URI (see &lt;a href=&quot;https://hpbn.co/http1x/#resource-inlining&quot; rel=&quot;noopener&quot;&gt;Resource Inlining&lt;/a&gt;),
then you already have hands-on experience with server push. By manually inlining
the resource into the document, we are, in effect, pushing that resource to the
client, without waiting for the client to request it. With HTTP/2 we can achieve
the same results, but with additional performance benefits. Push resources can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cached by the client&lt;/li&gt;
&lt;li&gt;Reused across different pages&lt;/li&gt;
&lt;li&gt;Multiplexed alongside other resources&lt;/li&gt;
&lt;li&gt;Prioritized by the server&lt;/li&gt;
&lt;li&gt;Declined by the client&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pushpromise-101&quot;&gt;PUSH_PROMISE 101 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#pushpromise-101&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All server push streams are initiated via &lt;code&gt;PUSH_PROMISE&lt;/code&gt; frames, which signal the
server’s intent to push the described resources to the client and need to be
delivered ahead of the response data that requests the pushed resources. This
delivery order is critical: the client needs to know which resources the server
intends to push to avoid creating duplicate requests for these
resources. The simplest strategy to satisfy this requirement is to send all
&lt;code&gt;PUSH_PROMISE&lt;/code&gt; frames, which contain just the HTTP headers of the promised
resource, ahead of the parent’s response (in other words, &lt;code&gt;DATA&lt;/code&gt; frames).&lt;/p&gt;
&lt;p&gt;Once the client receives a &lt;code&gt;PUSH_PROMISE&lt;/code&gt; frame it has the option to decline the
stream (via a &lt;code&gt;RST_STREAM&lt;/code&gt; frame) if it wants to. (This might occur for example
because the resource is already in cache.) This is an important improvement over
HTTP/1.x. By contrast, the use of resource inlining, which is a popular
&amp;quot;optimization&amp;quot; for HTTP/1.x, is equivalent to a &amp;quot;forced push&amp;quot;: the client cannot
opt-out, cancel it, or process the inlined resource individually.&lt;/p&gt;
&lt;p&gt;With HTTP/2 the client remains in full control of how server push is used. The
client can limit the number of concurrently pushed streams; adjust the initial
flow control window to control how much data is pushed when the stream is first
opened; or disable server push entirely. These preferences are communicated via
the &lt;code&gt;SETTINGS&lt;/code&gt; frames at the beginning of the HTTP/2 connection and may be updated
at any time.&lt;/p&gt;
&lt;p&gt;Each pushed resource is a stream that, unlike an inlined resource, allows it to
be individually multiplexed, prioritized, and processed by the client. The only
security restriction, as enforced by the browser, is that pushed resources must
obey the same-origin policy: the server must be authoritative for the provided
content.&lt;/p&gt;
&lt;h2 id=&quot;header-compression&quot;&gt;Header compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#header-compression&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each HTTP transfer carries a set of headers that describe the transferred
resource and its properties. In HTTP/1.x, this metadata is always sent as plain
text and adds anywhere from 500–800 bytes of overhead per transfer, and
sometimes kilobytes more if HTTP cookies are being used. (See
&lt;a href=&quot;https://hpbn.co/http1x/#measuring-and-controlling-protocol-overhead&quot; rel=&quot;noopener&quot;&gt;Measuring and Controlling Protocol Overhead&lt;/a&gt;
.) To reduce this overhead and improve performance, HTTP/2 compresses request
and response header metadata using the HPACK compression format that uses two
simple but powerful techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It allows the transmitted header fields to be encoded via a static Huffman
code, which reduces their individual transfer size.&lt;/li&gt;
&lt;li&gt;It requires that both the client and server maintain and update an indexed
list of previously seen header fields (in other words, it establishes a shared
compression context), which is then used as a reference to efficiently encode
previously transmitted values.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Huffman coding allows the individual values to be compressed when transferred,
and the indexed list of previously transferred values allows us to encode
duplicate values by transferring index values that can be used to efficiently
look up and reconstruct the full header keys and values.&lt;/p&gt;
&lt;img alt=&quot;HPACK: Header Compression for HTTP/2&quot; decoding=&quot;async&quot; height=&quot;150&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/IYfczfC6ZCTxUVboaEZy.svg&quot; width=&quot;178&quot; /&gt;
&lt;p&gt;As one further optimization, the HPACK compression context consists of a static
and dynamic table: the static table is defined in the specification and
provides a list of common HTTP header fields that all connections are likely to
use (e.g., valid header names); the dynamic table is initially empty and is
updated based on exchanged values within a particular connection. As a result,
the size of each request is reduced by using static Huffman coding for values
that haven’t been seen before, and substitution of indexes for values that are
already present in the static or dynamic tables on each side.&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; The definitions of the request and response header fields in HTTP/2 remains unchanged, with a few minor exceptions: all header field names are lowercase, and the request line is now split into individual &lt;code&gt;:method&lt;/code&gt;, &lt;code&gt;:scheme&lt;/code&gt;, &lt;code&gt;:authority&lt;/code&gt;, and &lt;code&gt;:path&lt;/code&gt; pseudo-header fields. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;security-and-performance-of-hpack&quot;&gt;Security and performance of HPACK &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#security-and-performance-of-hpack&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Early versions of HTTP/2 and SPDY used zlib, with a custom dictionary, to
compress all HTTP headers. This delivered an 85% to 88% reduction in the size
of the transferred header data, and a significant improvement in page load time
latency:&lt;/p&gt;
&lt;p&gt;On the lower-bandwidth DSL link, in which the upload link is only 375 Kbps,
request header compression in particular, led to significant page load time
improvements for certain sites (in other words, those that issued large number of
resource requests). We found a reduction of 45–1142 ms in page load time
simply due to header compression. &lt;a href=&quot;https://www.chromium.org/spdy/spdy-whitepaper&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;(SPDY whitepaper,
chromium.org)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, in the summer of 2012, a &amp;quot;CRIME&amp;quot; security attack was published against
TLS and SPDY compression algorithms, which could result in session hijacking. As
a result, the zlib compression algorithm was replaced by HPACK, which was
specifically designed to: address the discovered security issues, be efficient
and simple to implement correctly, and of course, enable good compression of
HTTP header metadata.&lt;/p&gt;
&lt;p&gt;For full details of the HPACK compression algorithm, see
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-header-compression&quot; rel=&quot;noopener&quot;&gt;IETF HPACK - Header Compression for HTTP/2&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hpbn.co/http2/&quot; rel=&quot;noopener&quot;&gt;“HTTP/2”&lt;/a&gt;
– The full article by Ilya Grigorik&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dassur.ma/things/h2setup/&quot; rel=&quot;noopener&quot;&gt;“Setting up HTTP/2”&lt;/a&gt;
– How to set up HTTP/2 in different backends by Surma&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1r7QXGYOLCh4fcUq0jDdDwKJWNqWK1o4xMtYpKZCJYjM/edit#slide=id.p19&quot; rel=&quot;noopener&quot;&gt;“HTTP/2 is here,
let’s optimize!”&lt;/a&gt;
– Presentation by Ilya Grigorik from Velocity 2015&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0/edit&quot; rel=&quot;noopener&quot;&gt;“Rules of Thumb for HTTP/2 Push”&lt;/a&gt;
– An analysis by Tom Bergan, Simon Pelchat and Michael Buettner on when and how to use push.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-http2/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>IntersectionObserver&#39;s coming into view</title>
    <link href="https://web.dev/intersectionobserver/"/>
    <updated>2016-04-20T00:00:00Z</updated>
    <id>https://web.dev/intersectionobserver/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;IntersectionObserver&lt;/code&gt; V2 has landed in Chromium-based browsers. &lt;a href=&quot;https://web.dev/intersectionobserver-v2/&quot;&gt;Read up on the updates&lt;/a&gt; for how that API improves on the first version of &lt;code&gt;IntersectionObserver&lt;/code&gt;. &lt;/div&gt;&lt;/aside&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 51, 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;
      51
    &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 55, 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;
      55
    &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 15, 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;
      15
    &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 12.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;
      12.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/API/IntersectionObserver#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Let&#39;s say you want to track when an element in your DOM enters the visible &lt;a href=&quot;https://en.wikipedia.org/wiki/Viewport&quot; rel=&quot;noopener&quot;&gt;viewport&lt;/a&gt;. You might want to do this so you can lazy-load images just in time or because you need to know if the user is actually looking at a certain ad banner. You can do that by hooking up the scroll event or by using a periodic timer and calling &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/getBoundingClientRect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getBoundingClientRect()&lt;/code&gt;&lt;/a&gt;
on that element.&lt;/p&gt;
&lt;p&gt;This approach, however, is painfully slow as each call to &lt;code&gt;getBoundingClientRect()&lt;/code&gt; forces the browser to &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; rel=&quot;noopener&quot;&gt;re-layout the entire page&lt;/a&gt; and will introduce considerable jank to your website. Matters get close to impossible when you know your site is being loaded inside an iframe and you want to know when the user can see an element. The Single Origin Model and the browser won&#39;t let you access any data from the web page that contains the iframe. This is a common problem for ads for example, that are frequently loaded using iframes.&lt;/p&gt;
&lt;p&gt;Making this visibility test more efficient is what &lt;a href=&quot;https://w3c.github.io/IntersectionObserver&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;&lt;/a&gt; was &lt;a href=&quot;https://github.com/WICG/IntersectionObserver/blob/gh-pages/explainer.md&quot; rel=&quot;noopener&quot;&gt;designed for&lt;/a&gt;, and it&#39;s landed in all modern browsers. &lt;code&gt;IntersectionObserver&lt;/code&gt; lets you know when an observed element enters or exits the browser&#39;s viewport.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Iframe visibility&quot; decoding=&quot;async&quot; height=&quot;649&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tID6JAeiVyE8Q1hOZfLK.gif?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;how-to-create-an-intersectionobserver&quot;&gt;How to create an &lt;code&gt;IntersectionObserver&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#how-to-create-an-intersectionobserver&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The API is rather small, and best described using an example:&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;const&lt;/span&gt; io &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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entries&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;span class=&quot;token comment&quot;&gt;/* Using default options. Details below */&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;// Start observing an element&lt;/span&gt;&lt;br /&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&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;// Stop observing an element&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// io.unobserve(element);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Disable entire IntersectionObserver&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// io.disconnect();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Using the default options for &lt;code&gt;IntersectionObserver&lt;/code&gt;, your callback will be called both when the element comes partially into view and when it completely leaves the viewport.&lt;/p&gt;
&lt;p&gt;If you &lt;em&gt;need&lt;/em&gt; to observe multiple elements, it is both possible and advised to observe multiple elements using the &lt;em&gt;same&lt;/em&gt; &lt;code&gt;IntersectionObserver&lt;/code&gt; instance by calling &lt;code&gt;observe()&lt;/code&gt; multiple times.&lt;/p&gt;
&lt;p&gt;An &lt;code&gt;entries&lt;/code&gt; parameter is passed to your callback which is an array of &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#intersection-observer-entry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;IntersectionObserverEntry&lt;/code&gt;&lt;/a&gt; objects. Each such object contains updated intersection data for one of your observed elements.&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 punctuation&quot;&gt;[&lt;/span&gt;IntersectionObserverEntry&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3893.92&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;🔽rootBounds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ClientRect&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;920&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&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;1024&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;left&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 literal-property property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;top&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 literal-property property&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;920&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;🔽boundingClientRect&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ClientRect&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;🔽intersectionRect&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ClientRect&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;intersectionRatio&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.54&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;🔽target&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; div#observee&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverentry-rootbounds&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rootBounds&lt;/code&gt;&lt;/a&gt; is the result of calling &lt;code&gt;getBoundingClientRect()&lt;/code&gt; on the root element, which is the viewport by default. &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverentry-boundingclientrect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;boundingClientRect&lt;/code&gt;&lt;/a&gt; is the result of &lt;code&gt;getBoundingClientRect()&lt;/code&gt; called on the observed element. &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverentry-intersectionrect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;intersectionRect&lt;/code&gt;&lt;/a&gt; is the intersection of these two rectangles and effectively tells you &lt;em&gt;which part&lt;/em&gt; of the observed element is visible. &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverentry-intersectionratio&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;intersectionRatio&lt;/code&gt;&lt;/a&gt; is closely related, and tells you &lt;em&gt;how much&lt;/em&gt; of the element is visible. With this info at your disposal, you are now able to implement features like just-in-time loading of assets before they become visible on screen. Efficiently.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Intersection ratio.&quot; decoding=&quot;async&quot; height=&quot;791&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/re5vpwXZrCpz0A5GYEiw.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;s deliver their data asynchronously, and your callback code will run in the main thread. Additionally, the spec actually says that &lt;code&gt;IntersectionObserver&lt;/code&gt; implementations should use &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#queue-intersection-observer-task&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;requestIdleCallback()&lt;/code&gt;&lt;/a&gt;. This means that the call to your provided callback is low priority and will be made by the browser during idle time. This is a conscious design decision.&lt;/p&gt;
&lt;h2 id=&quot;scrolling-divs&quot;&gt;Scrolling divs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#scrolling-divs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am not a big fan of scrolling inside an element, but I am not here to judge, and neither is &lt;code&gt;IntersectionObserver&lt;/code&gt;. The &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dictdef-intersectionobserverinit&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;options&lt;/code&gt;&lt;/a&gt; object takes a &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverinit-root&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;root&lt;/code&gt;&lt;/a&gt; option that lets you define an alternative to the viewport as your root. It is important to keep in mind that &lt;code&gt;root&lt;/code&gt; needs to be an ancestor of all the observed elements.&lt;/p&gt;
&lt;h2 id=&quot;intersect-all-the-things&quot;&gt;Intersect all the things! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#intersect-all-the-things&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;No! Bad developer! That&#39;s not mindful usage of your user&#39;s CPU cycles. Let&#39;s think about an infinite scroller as an example: In that scenario, it is definitely advisable to add &lt;a href=&quot;https://en.wikipedia.org/wiki/Sentinel_value&quot; rel=&quot;noopener&quot;&gt;sentinels&lt;/a&gt; to the DOM and observe (and recycle!) those. You should add a sentinel close to the last item in the infinite scroller. When that sentinel comes into view, you can use the callback to load data, create the next items, attach them to the DOM and reposition the sentinel accordingly. If you properly recycle the sentinel, no additional call to &lt;code&gt;observe()&lt;/code&gt; is needed. The &lt;code&gt;IntersectionObserver&lt;/code&gt; keeps working.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Infinite scroller&quot; decoding=&quot;async&quot; height=&quot;892&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/OZ2BUKXqzxtcZccTsWGL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;more-updates,-please&quot;&gt;More updates, please &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#more-updates,-please&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, the callback will be triggered a single time when the observed element comes partially into view and another time when it has left the viewport. This way &lt;code&gt;IntersectionObserver&lt;/code&gt; gives you an answer to the question, &amp;quot;Is element X in view?&amp;quot;. In some use cases, however, that might not be enough.&lt;/p&gt;
&lt;p&gt;That&#39;s where the &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverinit-threshold&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;threshold&lt;/code&gt;&lt;/a&gt; option comes into play. It allows you to define an array of &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverentry-intersectionratio&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;intersectionRatio&lt;/code&gt;&lt;/a&gt; thresholds. Your callback will be called every time &lt;code&gt;intersectionRatio&lt;/code&gt; crosses one of these values. The default value for &lt;code&gt;threshold&lt;/code&gt; is &lt;code&gt;[0]&lt;/code&gt;, which explains the default behavior. If we change &lt;code&gt;threshold&lt;/code&gt; to &lt;code&gt;[0, 0.25, 0.5, 0.75, 1]&lt;/code&gt;, we will get notified every time an additional quarter of the element becomes visible:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Threshold animation.&quot; decoding=&quot;async&quot; height=&quot;649&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/igJyLJwnl7OUShqsWmMY.gif?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;any-other-options&quot;&gt;Any other options? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#any-other-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As of now, there&#39;s only one additional option to the ones listed above. &lt;a href=&quot;https://w3c.github.io/IntersectionObserver#dom-intersectionobserverinit-rootmargin&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rootMargin&lt;/code&gt;&lt;/a&gt; allows you to specify the margins for the root, effectively allowing you to either grow or shrink the area used for intersections. These margins are specified using a CSS-style string, á la &lt;code&gt;&amp;quot;10px 20px 30px 40px&amp;quot;&lt;/code&gt;, specifying top, right, bottom and left margin respectively. To summarize, the &lt;code&gt;IntersectionObserver&lt;/code&gt; options struct offers the following options:&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// The root to use for intersection.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If not provided, use the top-level document&#39;s viewport.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&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;// Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If an explicit root element is specified, components may be percentages of the&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// root element size.  If no explicit root element is specified, using a&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// percentage is an error.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;rootMargin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0px&quot;&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;// Threshold(s) at which to trigger callback, specified as a ratio, or list of&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// ratios, of (visible area / total area) of the observed element (hence all&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// entries must be in the range [0, 1]).  Callback will be invoked when the&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// visible ratio of the observed element crosses a threshold in the list.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;threshold&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 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;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;h3 id=&quot;lessiframegreater-magic&quot;&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; magic &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#lessiframegreater-magic&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;s were designed specifically with ads services and social network widgets in mind, which frequently use &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements and could benefit from knowing whether they are in view. If an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; observes one of its elements, both scrolling the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; as well as scrolling the window &lt;em&gt;containing the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;&lt;/em&gt; will trigger the callback at the appropriate times. For the latter case, however, &lt;code&gt;rootBounds&lt;/code&gt; will be set to &lt;code&gt;null&lt;/code&gt; to avoid leaking data across origins.&lt;/p&gt;
&lt;h2 id=&quot;what-is-intersectionobserver-not-about&quot;&gt;What is &lt;code&gt;IntersectionObserver&lt;/code&gt; &lt;em&gt;Not&lt;/em&gt; about? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#what-is-intersectionobserver-not-about&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Something to keep in mind is that &lt;code&gt;IntersectionObserver&lt;/code&gt; is intentionally neither pixel perfect nor low latency. Using them to implement endeavours like scroll-dependent animations are bound to fail, as the data will be—strictly speaking—out of date by the time you&#39;ll get to use it. The &lt;a href=&quot;https://github.com/WICG/IntersectionObserver/blob/gh-pages/explainer.md&quot; rel=&quot;noopener&quot;&gt;explainer&lt;/a&gt; has more details about the original use cases for &lt;code&gt;IntersectionObserver&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;how-much-work-can-i-do-in-the-callback&quot;&gt;How much work can I do in the callback? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#how-much-work-can-i-do-in-the-callback&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Short &#39;n Sweet: Spending too much time in the callback will make your app lag—all the common practices apply.&lt;/p&gt;
&lt;h3 id=&quot;go-forth-and-intersect-thy-elements&quot;&gt;Go forth and intersect thy elements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intersectionobserver/#go-forth-and-intersect-thy-elements&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The browser support for &lt;code&gt;IntersectionObserver&lt;/code&gt; is good, &lt;a href=&quot;https://caniuse.com/intersectionobserver&quot; rel=&quot;noopener&quot;&gt;as it is available in all modern browsers&lt;/a&gt;. If necessary, a polyfill can be used in older browsers and is available in &lt;a href=&quot;https://github.com/WICG/IntersectionObserver&quot; rel=&quot;noopener&quot;&gt;WICG&#39;s repository&lt;/a&gt;. Obviously, you won&#39;t get the performance benefits using that polyfill that a native implementation would give you.&lt;/p&gt;
&lt;p&gt;You can start using &lt;code&gt;IntersectionObserver&lt;/code&gt; right now! Tell us what you came up with.&lt;/p&gt;
</content>
    <author>
      <name>Surma</name>
    </author>
  </entry>
</feed>
