<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Sam Dutton on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Sam Dutton</name>
  </author>
  <link href="https://web.dev/authors/samdutton/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/wPss4TJX9IJ1CJza7iFY.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Sam is a Developer Advocate</subtitle>
  
  
  <entry>
    <title>2021 Scroll Survey Report</title>
    <link href="https://web.dev/2021-scroll-survey-report/"/>
    <updated>2021-08-02T00:00:00Z</updated>
    <id>https://web.dev/2021-scroll-survey-report/</id>
    <content type="html" mode="escaped">&lt;p&gt;In April, the Chrome team &lt;a href=&quot;https://web.dev/2021-scroll-survey/&quot;&gt;released a scroll and touch-action
survey&lt;/a&gt; based on top reported issues from
the &lt;a href=&quot;https://mdn-web-dna.s3-us-west-2.amazonaws.com/MDN-Web-DNA-Report-2019.pdf&quot; rel=&quot;noopener&quot;&gt;2019 MDN Web DNA
Report&lt;/a&gt;.
The &lt;a href=&quot;https://storage.googleapis.com/web-dev-uploads/file/vS06HQ1YTsbMKSFTIPl2iogUQP73/QZopyELSk8T7IpsgOnRU.pdf&quot; rel=&quot;noopener&quot;&gt;2021 Scroll Survey
Report&lt;/a&gt;
is ready, and the Chrome team would like to share some thoughts and action items
we&#39;ve gleaned from the survey results. We hope these results will help browser
vendors and standards groups understand how to improve web scrolling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View the &lt;a href=&quot;https://storage.googleapis.com/web-dev-uploads/file/vS06HQ1YTsbMKSFTIPl2iogUQP73/QZopyELSk8T7IpsgOnRU.pdf&quot; rel=&quot;noopener&quot;&gt;2021 Scroll Survey
Report&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;noteworthy-results&quot;&gt;Noteworthy results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#noteworthy-results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The survey anonymously collected 880 submissions, with 366 answering every
question.&lt;/p&gt;
&lt;p&gt;While getting started with scrolling is one line of CSS, like &lt;code&gt;overflow-x: scroll;&lt;/code&gt;, the surface area of scroll APIs and options is large, spanning JavaScript to
CSS. The following results help to highlight the issues web developers are
encountering.&lt;/p&gt;
&lt;h3 id=&quot;overall-satisfaction-with-web-scrolling&quot;&gt;Overall satisfaction with web scrolling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#overall-satisfaction-with-web-scrolling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 27&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;45&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      are &lt;b&gt;somewhat&lt;/b&gt; or &lt;b&gt;extremely dissatisfied overall&lt;/b&gt; &lt;br /&gt;
      with &lt;b&gt;web scrolling&lt;/b&gt;.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This question was placed near the end of the survey intentionally, after
questions on 26 scroll use cases and features. From the response, it&#39;s clear that
the web community struggles with scroll. Almost half of the respondents report a
level of overall dissatisfaction.&lt;/p&gt;
&lt;p&gt;We believe overall sentiment about working with scroll should not be this low.
This metric needs to be changed; it&#39;s a clear signal there&#39;s work to be done.&lt;/p&gt;
&lt;h3 id=&quot;difficulty-working-with-scroll&quot;&gt;Difficulty working with scroll &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#difficulty-working-with-scroll&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 2&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;43&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      reported it&#39;s &lt;b&gt;somewhat&lt;/b&gt; or &lt;br /&gt;
      &lt;b&gt;extremely difficult &lt;br /&gt;
      to  work with scrolling&lt;/b&gt;.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;From our research, these difficulties come from the multitude of use cases for
scroll. When we talk about scrolling, that might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://elad.medium.com/css-position-sticky-how-it-really-works-54cd01dc2d46&quot; rel=&quot;noopener&quot;&gt;Positioning elements within scrollable areas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/infinite-scroll-techniques-in-react-adcfd7ff32bd/&quot; rel=&quot;noopener&quot;&gt;Infinite scroll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://greensock.com/scrolltrigger/&quot; rel=&quot;noopener&quot;&gt;Scroll linked animation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/css-only-carousel/&quot; rel=&quot;noopener&quot;&gt;Carousels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.alexandergottlieb.com/overflow-scroll-and-the-right-padding-problem-a-css-only-solution-6d442915b3f4&quot; rel=&quot;noopener&quot;&gt;Scrollview padding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.magictoolbox.com/magicscroll/integration/&quot; rel=&quot;noopener&quot;&gt;Cyclical scroll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tbranyen/hyperlist&quot; rel=&quot;noopener&quot;&gt;Virtualized scroll&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Missing browser features,
complex JavaScript, and the need to support input modes including touch,
keyboard, and gamepads, make all of these things harder.&lt;/p&gt;
&lt;h3 id=&quot;importance-of-touch-interactions&quot;&gt;Importance of touch interactions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#importance-of-touch-interactions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 3&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;51&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;touch interactions&lt;/b&gt; as &lt;br /&gt;
      &lt;b&gt;very&lt;/b&gt; or &lt;b&gt;extremely important&lt;/b&gt; &lt;br /&gt;
      to their work.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With &lt;a href=&quot;https://twitter.com/TheRealNooshu/status/1399676709125906432?s=20&quot; rel=&quot;noopener&quot;&gt;mobile web users still on the rise in visit
statistics&lt;/a&gt;,
it wasn&#39;t surprising to see half of the respondents report that touch is very
important to their work on the web. This signaled that web features like CSS
scroll snap and &lt;code&gt;touch-action&lt;/code&gt; need extra attention so the web can deliver on
high-quality touch interaction.&lt;/p&gt;
&lt;h3 id=&quot;difficulty-of-tab-key-or-gamepad-navigation&quot;&gt;Difficulty of tab key or gamepad navigation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#difficulty-of-tab-key-or-gamepad-navigation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 5a&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;44&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;somewhat&lt;/b&gt; or &lt;b&gt;extremely difficult&lt;/b&gt; &lt;br /&gt;
      to do &lt;b&gt;gamepad&lt;/b&gt; and &lt;b&gt;tab navigation&lt;/b&gt;.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Scrolling includes navigation methods such as keyboard arrows, tab keys,
spacebar presses, and gamepads, and it can be difficult to include these when doing
custom scroll work. Almost half of the respondents report it&#39;s
somewhat or extremely difficult to include these inputs.&lt;/p&gt;
&lt;h3 id=&quot;learning-touch-action&quot;&gt;Learning &lt;code&gt;touch-action&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#learning-touch-action&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 9&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;50&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;learning&lt;/b&gt; about &lt;br /&gt;
      &lt;b&gt;`touch-action: manipulation`&lt;/b&gt; &lt;br /&gt;
      from the survey.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Some of the survey questions asked about using certain APIs with a possible
answer of Yes, No, or &amp;quot;today I learned.&amp;quot; One notable piece of feedback was the
number of people who reported learning about &lt;code&gt;touch-action&lt;/code&gt; from the survey, as
it&#39;s a critical property when building custom touch gestures that need to
interact within scroll.&lt;/p&gt;
&lt;h3 id=&quot;cyclical-scrolling&quot;&gt;Cyclical scrolling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#cyclical-scrolling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 27&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;58&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;sometimes, often&lt;/b&gt; or on &lt;b&gt;every project&lt;/b&gt; &lt;br /&gt;
      using &lt;b&gt;cyclical scrolling&lt;/b&gt;.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; width=&quot;380&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/vS06HQ1YTsbMKSFTIPl2iogUQP73/xLwqQ6xGBIdI0uxFx566.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    The video shows cyclical seconds scrolling, &lt;br /&gt;
    after 60 seconds it begins at 0 again.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Those numbers are high for a scrolling feature with little or no support provided by the web platform.
The feature often incurs high amounts of technical
debt because of this, with duplication or JavaScript injected to force the
effect. It&#39;s popular for product carousels and when selecting time in seconds or minutes to
offer cyclical scrolling.&lt;/p&gt;
&lt;h3 id=&quot;are-scrollable-areas-important&quot;&gt;Are scrollable areas important &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#are-scrollable-areas-important&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 2&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;55&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;b&gt;very&lt;/b&gt; or &lt;br /&gt;
      &lt;b&gt;extremely important&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;16&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;not at all&lt;/b&gt; &lt;br /&gt;
      or &lt;b&gt;slightly important&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Respondents felt strongly about the importance of scrollable areas,
giving another signal about the struggles required to deliver high-quality scrolling.&lt;/p&gt;
&lt;h3 id=&quot;carousels&quot;&gt;Carousels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#carousels&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 20&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;87&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;b&gt;have used&lt;/b&gt; carousels.
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;24&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report they&#39;re &lt;br /&gt;
      &lt;b&gt;easy&lt;/b&gt; to manage.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Nearly every respondent delivers carousels in their web work, while only 25% find
them easy to manage. Off-the-shelf carousels were popular during our
research, but this statistic surprised us, as it doesn&#39;t sound very solved.&lt;/p&gt;
&lt;h3 id=&quot;infinite-scroll&quot;&gt;Infinite scroll &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#infinite-scroll&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 22&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;65&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      use it &lt;b&gt;sometimes&lt;/b&gt; &lt;br /&gt;
      to &lt;b&gt;every project&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;60&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;b&gt;somewhat&lt;/b&gt; or &lt;br /&gt;
      &lt;b&gt;extremely difficult&lt;/b&gt;.
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Two-thirds of respondents deliver infinite scroll in their web work, and an equal
amount report it&#39;s difficult to do. Another example of high usage paired with
high difficulty, which indicates to us an area needing attention.&lt;/p&gt;
&lt;p&gt;While &lt;a href=&quot;https://web.dev/content-visibility/&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt;&lt;/a&gt; and
&lt;code&gt;contain-intrinsic-size&lt;/code&gt; can be combined to reduce render costs for long
scrollable areas, it doesn&#39;t seem to be helping with &amp;quot;load more&amp;quot; infinite scroll
UX.&lt;/p&gt;
&lt;h3 id=&quot;scroll-linked-or-scroll-triggered-animations&quot;&gt;Scroll-linked or scroll-triggered animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#scroll-linked-or-scroll-triggered-animations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 24&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;47&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      use it &lt;b&gt;sometimes&lt;/b&gt;&lt;br /&gt;
      to &lt;b&gt;every project&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;56&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      report &lt;b&gt;somewhat&lt;/b&gt; or &lt;br /&gt;
      &lt;b&gt;extremely difficult&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Almost half of all respondents use scroll-orchestrated animations and half the
respondents find it difficult, once again linking high usage with difficulty.&lt;/p&gt;
&lt;h3 id=&quot;compete-with-built-in-scrolling&quot;&gt;Compete with built-in scrolling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#compete-with-built-in-scrolling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 26&lt;/small&gt;&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;32&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;b&gt;always&lt;/b&gt; or &lt;br /&gt;
      &lt;b&gt;most of the time&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;50&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;
      &lt;b&gt;sometimes&lt;/b&gt;
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The built-in scroll and touch interactions of phone and tablet applications are
often touted as a clear place where the web can catch up. The features include
scroll-linked animations, programmatic interfaces, voice integration, scroll
hints, and pull-to-refresh APIs.&lt;/p&gt;
&lt;p&gt;Just half of the respondents felt it was only sometimes possible to match the
experience of built-in scrolling.&lt;/p&gt;
&lt;h3 id=&quot;overall-satisfaction-building-scroll-interactions-on-the-web&quot;&gt;Overall satisfaction building scroll interactions on the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#overall-satisfaction-building-scroll-interactions-on-the-web&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;small&gt;Question 27&lt;/small&gt;&lt;/p&gt;
&lt;img alt=&quot;A pie chart showing 5 sections: 6.3% extremely dissatisfied, 2.7% extremely satisfied, 23.4% somewhat satisfied, 28.8% neither satisfied nor dissatisfied, 38.7% somewhat dissatisfied.&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vS06HQ1YTsbMKSFTIPl2iogUQP73/ycHj33nZb1KfUFxNBdH5.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;survey-takeaways&quot;&gt;Survey Takeaways &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#survey-takeaways&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The survey results are segmented into four categories:
&lt;a href=&quot;https://web.dev/2021-scroll-survey-report/#compatibility&quot;&gt;compatibility&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/2021-scroll-survey-report/#education&quot;&gt;education&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/2021-scroll-survey-report/#apis&quot;&gt;APIs&lt;/a&gt;,
and &lt;a href=&quot;https://web.dev/2021-scroll-survey-report/#features&quot;&gt;features&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;compatibility&quot;&gt;Compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#compatibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Chrome team has &lt;a href=&quot;https://web.dev/compat2021&quot;&gt;declared a goal&lt;/a&gt; to decrease
the number of web compatibility issues, including scroll compatibility.&lt;/p&gt;
&lt;p&gt;The first three compatibility issues to focus on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Horizontal scrolling compatibility.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;overscroll-behavior&lt;/code&gt; cross browser.&lt;/li&gt;
&lt;li&gt;Removing prefixes from &lt;code&gt;-webkit-scrollbar&lt;/code&gt; and following the standard.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;education&quot;&gt;Education &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#education&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The survey results showed that there needs to be more education around
&lt;code&gt;touch-action&lt;/code&gt; and &lt;a href=&quot;https://web.dev/logical-property-shorthands/&quot;&gt;logical
properties&lt;/a&gt;. The browser is at the
forefront of international layout, and it&#39;s apparent it&#39;s underutilized or
misunderstood.&lt;/p&gt;
&lt;p&gt;Areas to focus on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/touch-action&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;touch-action&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/css/logical-properties/&quot;&gt;Logical properties&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;apis&quot;&gt;APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#apis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Usage of scroll snapping is growing, and developers have responded that they
want to use features interoperably with popular libraries and
plugins. Shrinking this gap between CSS and plugin libraries will help the
satisfaction of scroll snap developer and user experience.&lt;/p&gt;
&lt;p&gt;We&#39;ll focus API work on &lt;code&gt;scroll-snap&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;API availability and compatibility across browsers.&lt;/li&gt;
&lt;li&gt;Begin work on &lt;a href=&quot;https://github.com/argyleink/ScrollSnapExplainers&quot; rel=&quot;noopener&quot;&gt;new CSS
APIs&lt;/a&gt; like &lt;code&gt;scroll-start&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Begin work on &lt;a href=&quot;https://github.com/argyleink/ScrollSnapExplainers&quot; rel=&quot;noopener&quot;&gt;new JS
events&lt;/a&gt; like
&lt;code&gt;snapChanged()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;features&quot;&gt;Features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#features&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The survey results showed that users struggle with some specific types of
scroll-related components on the web, as the platform doesn&#39;t provide the
primitives they need to build them without plugins or a high level of effort.
This is an area that we hope to explore more deeply.&lt;/p&gt;
&lt;p&gt;The features developers struggle to build include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Carousels&lt;/li&gt;
&lt;li&gt;Virtual scroll&lt;/li&gt;
&lt;li&gt;Infinite scroll&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey-report/#resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://storage.googleapis.com/web-dev-uploads/file/vS06HQ1YTsbMKSFTIPl2iogUQP73/QZopyELSk8T7IpsgOnRU.pdf&quot; rel=&quot;noopener&quot;&gt;Scroll Survey
Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/2021-scroll-survey/&quot;&gt;Survey Announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://insights.developer.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;Mozilla DNA Reports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/compat2021/&quot;&gt;Compat2021: Eliminating five top compatibility pain points on the
web&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thumbnail image: photo by &lt;a href=&quot;https://unsplash.com/@taypaigey&quot; rel=&quot;noopener&quot;&gt;Taylor Wilcox&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/aXeVH4FcS1k&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Adam Argyle</name>
    </author><author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Take the 2021 scroll survey to help improve scrolling on the web</title>
    <link href="https://web.dev/2021-scroll-survey/"/>
    <updated>2021-04-15T00:00:00Z</updated>
    <id>https://web.dev/2021-scroll-survey/</id>
    <content type="html" mode="escaped">&lt;p&gt;We&#39;ve been analyzing the results of the
&lt;a href=&quot;https://mdn-web-dna.s3-us-west-2.amazonaws.com/MDN-Web-DNA-Report-2019.pdf&quot; rel=&quot;noopener&quot;&gt;2019 Mozilla Developer Network Web DNA Report&lt;/a&gt;
for action items and follow up strategies to improve the top reported issues.
A small sub-group on the Chrome team identified a group of issues related
to scrolling, &lt;code&gt;scroll-snap&lt;/code&gt;, and &lt;code&gt;touch-action&lt;/code&gt;. Those issues were researched,
evaluated, and synthesized into a new, follow-up survey with more precise
questions about issues related to these topics.&lt;/p&gt;
&lt;h2 id=&quot;2021-scroll-survey&quot;&gt;2021 Scroll Survey &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/2021-scroll-survey/#2021-scroll-survey&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We believe that there are many ways to improve scrolling on the web for
developers, designers, and users alike. We&#39;ve created a survey which we
hope is respectful of your time. It should take no more than 10 minutes
to complete. The results will help browser vendors and standards groups
understand how to make scrolling better.&lt;/p&gt;
&lt;p&gt;You can take part in the survey at &lt;a href=&quot;https://google.qualtrics.com/jfe/form/SV_bjbaTccU1lpPlVX&quot; rel=&quot;noopener&quot;&gt;2021 Web Scrolling Survey&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Adam Argyle</name>
    </author><author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>What is FLoC?</title>
    <link href="https://web.dev/floc/"/>
    <updated>2021-03-30T00:00:00Z</updated>
    <id>https://web.dev/floc/</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; This post outlined the API design implemented in Chrome for the first origin trial of FLoC. The development of FLoC stopped in 2021.  In January 2022, the Privacy Sandbox released the &lt;a href=&quot;https://github.com/jkarlin/topics&quot;&gt;Topics API proposal&lt;/a&gt; to address interest-based advertising, without having to resort to tracking the sites a user visits. The design of the API was informed by community feedback from our earlier FLoC trials, and supersedes the FLoC proposal. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;FLoC provides a privacy-preserving mechanism for interest-based ad selection.&lt;/p&gt;
&lt;p&gt;As a user moves around the web, their browser uses the FLoC algorithm to work out its &amp;quot;interest
cohort&amp;quot;, which will be the same for thousands of browsers with a similar recent browsing history.
The browser recalculates its cohort periodically, on the user&#39;s device, without sharing
individual browsing data with the browser vendor or anyone else.&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; During the initial FLoC trial, a page visit was only included in the browser&#39;s FLoC computation for one of two reasons: * The FLoC API (&lt;code&gt;document.interestCohort()&lt;/code&gt;) is used on the page. * Chrome detects that the page &lt;a href=&quot;https://github.com/WICG/floc/issues/82&quot;&gt;loads ads or ads-related resources&lt;/a&gt;.  For other clustering algorithms, the trial may experiment with different inclusion criteria: that&#39;s part of the origin trial experiment process.  The origin trial for the initial version of FLoC, which ran from Chrome 89 to 91, &lt;a href=&quot;https://developer.chrome.com/origintrials/#/view_trial/213920982300098561&quot;&gt;is now closed&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Advertisers (sites that pay for advertisements) can include code on their own websites in order to
gather and provide cohort data to their adtech platforms (companies that provide software and tools
to deliver advertising). For example, an adtech platform might learn from an online shoe store that
browsers from cohorts 1101 and 1354 seem interested in the store&#39;s hiking gear. From other
advertisers, the adtech platform learns about other interests of those cohorts.&lt;/p&gt;
&lt;p&gt;Subsequently, the ad platform can use this data to select relevant ads (such as an ad for hiking
boots from the shoe store) when a browser from one of those cohorts requests a page from a site that
displays ads, such as a news website.&lt;/p&gt;
&lt;p&gt;The Privacy Sandbox is a series of proposals to satisfy third-party use cases without third-party
cookies or other tracking mechanisms. See &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox&quot;&gt;Digging into the Privacy Sandbox&lt;/a&gt;
for an overview of all the proposals.&lt;/p&gt;
&lt;p&gt;If you have comments on this proposal, &lt;a href=&quot;https://github.com/WICG/floc/issues/new&quot; rel=&quot;noopener&quot;&gt;create an
issue&lt;/a&gt; on the &lt;a href=&quot;https://github.com/WICG/floc&quot; rel=&quot;noopener&quot;&gt;FLoC Explainer&lt;/a&gt;
repository.  If you have feedback on Chrome&#39;s experiment with this proposal,
post a reply on the &lt;a href=&quot;https://groups.google.com/a/chromium.org/g/blink-dev/c/MmijXrmwrJs&quot; rel=&quot;noopener&quot;&gt;Intent to Experiment&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;why-do-we-need-floc&quot;&gt;Why do we need FLoC? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#why-do-we-need-floc&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many businesses rely on advertising to drive traffic to their sites, and many publisher websites
fund content by selling advertising inventory. People generally prefer to see ads that are
relevant and useful to them, and relevant ads also bring more business to advertisers and
&lt;a href=&quot;https://services.google.com/fh/files/misc/disabling_third-party_cookies_publisher_revenue.pdf&quot; rel=&quot;noopener&quot;&gt;more revenue to the websites that host them&lt;/a&gt;. In other words, ad space is more valuable when
it displays relevant ads. Thus, selecting relevant ads increases revenue for ad-supported websites.
That, in turn, means that relevant ads help fund content creation that benefits users.&lt;/p&gt;
&lt;p&gt;However, people are concerned about the privacy implications of tailored advertising, which
currently relies on techniques such as tracking cookies and device fingerprinting which can reveal
your browsing history across sites to advertisers or ad platforms. The FLoC proposal aims to allow
ad selection in a way that better protects privacy.&lt;/p&gt;
&lt;h2 id=&quot;what-can-floc-be-used-for&quot;&gt;What can FLoC be used for? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#what-can-floc-be-used-for&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Show ads to people whose browsers belong to a cohort that has been observed to frequently visit an
advertiser&#39;s site or shows interest in relevant topics.&lt;/li&gt;
&lt;li&gt;Use machine learning models to predict the probability a user will convert based on their cohort,
in order to inform ad auction bidding behavior.&lt;/li&gt;
&lt;li&gt;Recommend content to users. For example, suppose a news site observes that their sports podcast
page has become especially popular with visitors from cohorts 1234 and 7. They can recommend that
content to other visitors from those cohorts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-does-floc-work&quot;&gt;How does FLoC work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#how-does-floc-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The example below describes the different roles in selecting an ad using FLoC.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;strong&gt;advertiser&lt;/strong&gt; (a company that pays for advertising) in this example is an online shoe
retailer: &lt;br /&gt;
&lt;strong&gt;&lt;u&gt;shoestore.example&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;strong&gt;publisher&lt;/strong&gt; (a site that sells ad space) in the example is a news site: &lt;br /&gt;
&lt;strong&gt;&lt;u&gt;dailynews.example&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;strong&gt;adtech platform&lt;/strong&gt; (which provides software and tools to deliver advertising) is: &lt;br /&gt;
&lt;strong&gt;&lt;u&gt;adnetwork.example&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Diagram showing, step by step, the different roles in selecting and delivering an ad using   FLoC: FLoC service, Browser, Advertisers, Publisher (to observe cohorts), Adtech,   Publisher (to display ads)&quot; decoding=&quot;async&quot; height=&quot;359&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/wnJ1fSECf5STngywgE7V.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In this example we&#39;ve called the users &lt;strong&gt;Yoshi&lt;/strong&gt; and &lt;strong&gt;Alex&lt;/strong&gt;. Initially their browsers both belong
to the same cohort, 1354.&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; We&#39;ve called the users here Yoshi and Alex, but this is only for the purpose of the example. Names and individual identities are not revealed to the advertiser, publisher, or adtech platform with FLoC.  Don&#39;t think of a cohort as a collection of people. Instead, think of a cohort as a grouping of browsing activity. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;1-floc-service&quot;&gt;1. FLoC service &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#1-floc-service&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;The FLoC service used by the browser creates a mathematical model with thousands of &amp;quot;cohorts&amp;quot;,
each of which will correspond to thousands of web browsers with similar recent browsing histories.
More about how this works &lt;a href=&quot;https://web.dev/floc/#floc-server&quot;&gt;below&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Each cohort is given a number.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;2-browser&quot;&gt;2. Browser &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#2-browser&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;From the FLoC service, Yoshi&#39;s browser gets data describing the FLoC model.&lt;/li&gt;
&lt;li&gt;Yoshi&#39;s browser works out its cohort &lt;a href=&quot;https://web.dev/floc/#floc-algorithm&quot;&gt;by using the FLoC model&#39;s algorithm&lt;/a&gt;
to calculate which cohort corresponds most closely to its own browsing history. In this example,
that will be the cohort 1354. Note that Yoshi&#39;s browser does not share any data with the FLoC
service.&lt;/li&gt;
&lt;li&gt;In the same way, Alex&#39;s browser calculates its cohort ID. Alex&#39;s browsing history is
different from Yoshi&#39;s, but similar enough that their browsers both belong to cohort 1354.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;3-advertiser-shoestoreexample&quot;&gt;3. Advertiser: &lt;span style=&quot;font-weight:normal&quot;&gt;shoestore.example&lt;/span&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#3-advertiser-shoestoreexample&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Yoshi visits &lt;u&gt;shoestore.example&lt;/u&gt;.&lt;/li&gt;
&lt;li&gt;The site asks Yoshi&#39;s browser for its cohort: 1354.&lt;/li&gt;
&lt;li&gt;Yoshi looks at hiking boots.&lt;/li&gt;
&lt;li&gt;The site records that a browser from cohort 1354 showed interest in hiking boots.&lt;/li&gt;
&lt;li&gt;The site later records additional interest in its products from cohort 1354, as well as from other
cohorts.&lt;/li&gt;
&lt;li&gt;The site periodically aggregates and shares information about cohorts and product interests with
its adtech platform &lt;u&gt;adnetwork.example&lt;/u&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now it&#39;s Alex&#39;s turn.&lt;/p&gt;
&lt;h3 id=&quot;4-publisher-dailynewsexample&quot;&gt;4. Publisher: &lt;span style=&quot;font-weight:normal&quot;&gt;dailynews.example&lt;/span&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#4-publisher-dailynewsexample&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Alex visits &lt;u&gt;dailynews.example&lt;/u&gt;.&lt;/li&gt;
&lt;li&gt;The site asks Alex&#39;s browser for its cohort.&lt;/li&gt;
&lt;li&gt;The site then makes a request for an ad to its adtech platform, &lt;u&gt;adnetwork.example&lt;/u&gt;, including
Alex&#39;s browser&#39;s cohort: 1354.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;5-adtech-platform-adnetworkexample&quot;&gt;5. Adtech platform: &lt;span style=&quot;font-weight:normal&quot;&gt;adnetwork.example&lt;/span&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#5-adtech-platform-adnetworkexample&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;u&gt;adnetwork.example&lt;/u&gt; can select an ad suitable for Alex by combining the data it has from
the publisher &lt;u&gt;dailynews.example&lt;/u&gt; and the advertiser &lt;u&gt;shoestore.example&lt;/u&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Alex&#39;s browser&#39;s cohort (1354) provided by &lt;u&gt;dailynews.example&lt;/u&gt;.&lt;/li&gt;
&lt;li&gt;Data about cohorts and product interests from &lt;u&gt;shoestore.example&lt;/u&gt;: &amp;quot;Browsers from cohort 1354
might be interested in hiking boots.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;u&gt;adnetwork.example&lt;/u&gt; selects an ad appropriate to Alex: an ad for hiking boots on
&lt;u&gt;shoestore.example&lt;/u&gt;.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;dailynews.example&lt;/u&gt; displays the ad 🥾.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Current approaches for ad selection rely on techniques such as tracking cookies and device fingerprinting, which are used by third parties such as advertisers to track individual browsing behavior.  With FLoC, the browser &lt;strong&gt;does not share&lt;/strong&gt; its browsing history with the FLoC service or anyone else. The browser, on the user&#39;s device, works out which cohort it belongs to. The user&#39;s browsing history never leaves the device. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;who-runs-the-back-end-service-that-creates-the-floc-model&quot;&gt;Who runs the back-end service that creates the FLoC model? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#who-runs-the-back-end-service-that-creates-the-floc-model&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every browser vendor will need to make their own choice of how to group browsers into cohorts.
Chrome is running its own FLoC service; other browsers might choose to implement FLoC with a
different clustering approach, and would run their own service to do so.&lt;/p&gt;
&lt;h2 id=&quot;floc-server&quot;&gt;How does the FLoC service enable the browser to work out its cohort? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#floc-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The FLoC service used by the browser creates a multi-dimensional mathematical representation
of all potential web browsing histories. We&#39;ll call this model &amp;quot;cohort space&amp;quot;.&lt;/li&gt;
&lt;li&gt;The service divides up this space into thousands of segments. Each segment represents a
cluster of thousands of similar browsing histories. These groupings aren&#39;t based on knowing
any actual browsing histories; they&#39;re simply based on picking random centers in &amp;quot;cohort space&amp;quot; or
cutting up the space with random lines.&lt;/li&gt;
&lt;li&gt;Each segment is given a cohort number.&lt;/li&gt;
&lt;li&gt;The web browser gets this data describing &amp;quot;cohort space&amp;quot; from its FLoC service.&lt;/li&gt;
&lt;li&gt;As a user moves around the web, their browser &lt;a href=&quot;https://web.dev/floc/#floc-algorithm&quot;&gt;uses an algorithm&lt;/a&gt; to
periodically calculate the region in &amp;quot;cohort space&amp;quot; that corresponds most closely to its own
browsing history.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure style=&quot;text-align: center&quot;&gt;
&lt;img alt=&quot;Diagram of the &amp;#x27;browsing history space&amp;#x27; created by a FLoC server, showing multiple segments, each with a cohort number.&quot; decoding=&quot;async&quot; height=&quot;359&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/32k5jByqLrgwSMwb9mqo.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
&lt;figcaption&gt;The FLoC service divides up &quot;cohort space&quot; into
thousands of segments (only a few are shown here).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; At no point in this process is the user&#39;s browsing history shared with the FLoC service, or any third party. The browser&#39;s cohort is calculated by the browser, on the user&#39;s device. No user data is acquired or stored by the FLoC service. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;can-a-browsers-cohort-change&quot;&gt;Can a browser&#39;s cohort change? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#can-a-browsers-cohort-change&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Yes&lt;/em&gt;! A browser&#39;s cohort definitely can change! You probably don&#39;t visit the same websites every
week, and your browser&#39;s cohort will reflect that.&lt;/p&gt;
&lt;p&gt;A cohort represents a cluster of browsing activity, not a collection of people. The activity
characteristics of a cohort are generally consistent over time, and cohorts are useful for ad selection
because they group similar recent browsing behavior. Individual people&#39;s browsers will float in and
out of a cohort as their browsing behavior changes. Initially, we expect the browser to recalculate
its cohort every seven days.&lt;/p&gt;
&lt;p&gt;In the example above, both Yoshi and Alex&#39;s browser&#39;s cohort is 1354. In the future, Yoshi&#39;s
browser and Alex&#39;s browser may move to a different cohort if their interests change. In the
example below, Yoshi&#39;s browser moves to cohort 1101 and Alex&#39;s browser moves to cohort 1378. Other
people&#39;s browsers will move into and out of cohorts as their browsing interests change.&lt;/p&gt;
&lt;figure style=&quot;text-align: center&quot;&gt;
&lt;img alt=&quot;Diagram of the &amp;#x27;browsing history space&amp;#x27; created by a FLoC server, showing multiple segments, each with a cohort number. The diagram shows browsers belonging to users Yoshi and Alex moving from one cohort to another as their browsing interests change over time.&quot; decoding=&quot;async&quot; height=&quot;533&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/80mq7dk16vVEg8BBhsVe42n6zn82/LMkb62V3iJTqkOrFACnM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Yoshi&#39;s and Alex&#39;s browser cohort may change if their interests
change.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; A cohort defines a grouping of browsing activity, not a group of people. Browsers will move in and out of a cohort as their activity changes. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;floc-algorithm&quot;&gt;How does the browser work out its cohort? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#floc-algorithm&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As described above, the user&#39;s browser gets data from its FLoC service that describes the
mathematical model for cohorts: a multi-dimensional space that represents the browsing activity of
all users. The browser then uses an algorithm to work out which region of this &amp;quot;cohort space&amp;quot; (that
is, which cohort) most closely matches its own recent browsing behavior.&lt;/p&gt;
&lt;h2 id=&quot;how-does-floc-work-out-the-right-size-of-cohort&quot;&gt;How does FLoC work out the right size of cohort? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#how-does-floc-work-out-the-right-size-of-cohort&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There will be thousands of browsers in each cohort.&lt;/p&gt;
&lt;p&gt;A smaller cohort size might be more useful for personalizing ads, but is less likely to stop user
tracking—and vice versa. A mechanism for assigning browsers to cohorts needs to make a trade off
between privacy and utility. The Privacy Sandbox uses &lt;a href=&quot;https://en.wikipedia.org/wiki/K-anonymity&quot; rel=&quot;noopener&quot;&gt;k-anonymity&lt;/a&gt;
to allow a user to &amp;quot;hide in a crowd&amp;quot;. A cohort is k-anonymous if it is shared by at least k users. The higher the k
number, the more privacy-preserving the cohort.&lt;/p&gt;
&lt;h2 id=&quot;can-floc-be-used-to-group-people-based-on-sensitive-categories&quot;&gt;Can FLoC be used to group people based on sensitive categories? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#can-floc-be-used-to-group-people-based-on-sensitive-categories&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The clustering algorithm used to construct the FLoC cohort model is designed to evaluate whether a
cohort may be correlated with sensitive categories, without learning why a category is sensitive.
Cohorts that might reveal sensitive categories such as race, sexuality, or medical history will be
blocked. In other words, when working out its cohort, a browser will only be choosing between
cohorts that won&#39;t reveal sensitive categories.&lt;/p&gt;
&lt;h2 id=&quot;is-floc-just-another-way-of-categorizing-people-online&quot;&gt;Is FLoC just another way of categorizing people online? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#is-floc-just-another-way-of-categorizing-people-online&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With FLoC, a user&#39;s browser will belong to one of thousands of cohorts, along with thousands of
other users&#39; browsers. Unlike with third-party cookies and other targeting mechanisms, FLoC only
reveals the cohort a user&#39;s browser is in, and not an individual user ID. It does not enable others
to distinguish an individual within a cohort. In addition, the information about browsing activity
that is used to work out a browser&#39;s cohort is kept local on the browser or device, and is not
uploaded elsewhere. The browser may further leverage other anonymization methods, such as
&lt;a href=&quot;https://en.wikipedia.org/wiki/Differential_privacy&quot; rel=&quot;noopener&quot;&gt;differential privacy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;do-websites-have-to-participate-and-share-information&quot;&gt;Do websites have to participate and share information? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#do-websites-have-to-participate-and-share-information&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Websites will have the ability to opt in or out of FLoC, so sites about sensitive topics will be
able to prevent visits to their site from being included in the FLoC calculation. As additional
protection, analysis by the FLoC service will evaluate whether a cohort may reveal sensitive
information about users without learning why that cohort is sensitive. If a cohort might represent a
greater-than-typical number of people who visit sites in a sensitive category, that entire cohort is
removed. Negative financial status and mental health are among the sensitive categories covered by
this analysis.&lt;/p&gt;
&lt;p&gt;Websites &lt;a href=&quot;https://github.com/WICG/floc#opting-out-of-computation&quot; rel=&quot;noopener&quot;&gt;can exclude a page from the FLoC calculation&lt;/a&gt;
by setting a &lt;a href=&quot;https://developer.chrome.com/docs/privacy-sandbox/permissions-policy/&quot; rel=&quot;noopener&quot;&gt;Permissions-Policy&lt;/a&gt; header
&lt;code&gt;interest-cohort=()&lt;/code&gt; for that page. For pages that haven&#39;t been excluded, a page visit will be included
in the browser&#39;s FLoC calculation if &lt;code&gt;document.interestCohort()&lt;/code&gt; is used on the page. During the current
&lt;a href=&quot;https://developer.chrome.com/origintrials/#/view_trial/213920982300098561&quot; rel=&quot;noopener&quot;&gt;FLoC origin trial&lt;/a&gt;, a
page will also be included in the calculation if Chrome detects that the page
&lt;a href=&quot;https://github.com/WICG/floc/issues/82&quot; rel=&quot;noopener&quot;&gt;loads ads or ads-related resources&lt;/a&gt;.
(&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/docs/ad_tagging.md&quot; rel=&quot;noopener&quot;&gt;Ad Tagging in Chromium&lt;/a&gt;
explains how Chrome&#39;s ad detection mechanism works.)&lt;/p&gt;
&lt;p&gt;Pages served from private IP addresses, such as intranet pages, won&#39;t be part of the FLoC
computation.&lt;/p&gt;
&lt;h2 id=&quot;how-does-the-floc-javascript-api-work&quot;&gt;How does the FLoC JavaScript API work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#how-does-the-floc-javascript-api-work&quot;&gt;#&lt;/a&gt;&lt;/h2&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 origin trial for the initial version of FLoC, which ran from Chrome 89 to 91, &lt;a href=&quot;https://developer.chrome.com/origintrials/#/view_trial/213920982300098561&quot;&gt;is now closed&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The FLoC API is very simple: just a single method that returns a promise that resolves to an object
providing the cohort &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&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; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version &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; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;interestCohort&lt;/span&gt;&lt;span class=&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 string&quot;&gt;&#39;FLoC ID:&#39;&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;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;FLoC version:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&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 cohort data made available looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;14159&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;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;chrome.2.1&quot;&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 &lt;code&gt;version&lt;/code&gt; value enables sites using FLoC to know which browser and which FLoC model the cohort
ID refers to. As described below, the promise returned by &lt;code&gt;document.interestCohort()&lt;/code&gt; will reject
for any frame that is not allowed the &lt;code&gt;interest-cohort&lt;/code&gt; permission.&lt;/p&gt;
&lt;h2 id=&quot;can-websites-opt-out-of-being-included-in-the-floc-computation&quot;&gt;Can websites opt out of being included in the FLoC computation? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#can-websites-opt-out-of-being-included-in-the-floc-computation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;interest-cohort&lt;/code&gt; permissions policy enables a site to declare that it does not want to be
included in the user&#39;s list of sites for cohort calculation. The policy will be &lt;code&gt;allow&lt;/code&gt; by default.
The promise returned by &lt;code&gt;document.interestCohort()&lt;/code&gt; will reject for any frame that is not allowed
&lt;code&gt;interest-cohort&lt;/code&gt; permission. If the main frame does not have the &lt;code&gt;interest-cohort&lt;/code&gt; permission, then the
page visit will not be included in the interest cohort calculation.&lt;/p&gt;
&lt;p&gt;For example, a site can opt out of all FLoC cohort calculation by sending the following HTTP
response header:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;  Permissions-Policy: interest-cohort=()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;can-a-user-stop-sites-from-getting-their-browsers-floc-cohort&quot;&gt;Can a user stop sites from getting their browser&#39;s FLoC cohort? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#can-a-user-stop-sites-from-getting-their-browsers-floc-cohort&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If a user disables Privacy Sandbox in &lt;code&gt;chrome://settings/privacySandbox&lt;/code&gt;, the browser will not provide
the user&#39;s cohort when asked for it via JavaScript: the promise returned by
&lt;code&gt;document.interestCohort()&lt;/code&gt; will reject.&lt;/p&gt;
&lt;h2 id=&quot;how-can-i-make-suggestions-or-provide-feedback&quot;&gt;How can I make suggestions or provide feedback? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#how-can-i-make-suggestions-or-provide-feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have comments on the API, &lt;a href=&quot;https://github.com/WICG/floc/issues/new&quot; rel=&quot;noopener&quot;&gt;create an issue&lt;/a&gt;
on the &lt;a href=&quot;https://github.com/WICG/floc&quot; rel=&quot;noopener&quot;&gt;FLoC Explainer&lt;/a&gt; repository.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/floc/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/&quot;&gt;Digging in to the Privacy Sandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/floc&quot; rel=&quot;noopener&quot;&gt;FLoC Explainer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/Home/chromium-privacy/privacy-sandbox/floc&quot; rel=&quot;noopener&quot;&gt;FLoC Origin Trial &amp;amp; Clustering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google/ads-privacy/blob/master/proposals/FLoC/README.md&quot; rel=&quot;noopener&quot;&gt;Evaluation of cohort Algorithms for the FLoC API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@rhyskentish&quot; rel=&quot;noopener&quot;&gt;Rhys Kentish&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/I5AYxsxSuVA&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Payment and address form best practices</title>
    <link href="https://web.dev/payment-and-address-form-best-practices/"/>
    <updated>2020-12-09T00:00:00Z</updated>
    <id>https://web.dev/payment-and-address-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;xfGKmvvyhdM&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Well-designed forms help users and increase conversion rates. One small fix can make a big difference!&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewbox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you prefer to learn these best practices with a hands-on tutorial, check out the two codelabs for this post:  * &lt;a href=&quot;https://web.dev/codelab-payment-form-best-practices&quot;&gt;Payment form best practices codelab&lt;/a&gt; * &lt;a href=&quot;https://web.dev/codelab-address-form-best-practices&quot;&gt;Address form best practices codelab&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Here is an example of a simple payment form that demonstrates all of the best practices:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 720px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Here is an example of a simple address form that demonstrates all of the best practices:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 980px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/address-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;address-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;checklist&quot;&gt;Checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#checklist&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#meaningful-html&quot;&gt;Use meaningful HTML elements&lt;/a&gt;: &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-label&quot;&gt;Label each form field with a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use HTML element attributes to &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-attributes&quot;&gt;access built-in browser features&lt;/a&gt;, in particular
&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#type-attribute&quot;&gt;&lt;code&gt;type&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#autocomplete-attribute&quot;&gt;&lt;code&gt;autocomplete&lt;/code&gt;&lt;/a&gt; with appropriate values.&lt;/li&gt;
&lt;li&gt;Avoid using &lt;code&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt; for numbers that aren&#39;t meant to be
incremented, such as payment card numbers. Use &lt;code&gt;type=&amp;quot;text&amp;quot;&lt;/code&gt; and &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#inputmode-attribute&quot;&gt;&lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt;&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;If an &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#autocomplete-attribute&quot;&gt;appropriate autocomplete value&lt;/a&gt; is available for an &lt;code&gt;input&lt;/code&gt;,
&lt;code&gt;select&lt;/code&gt;, or &lt;code&gt;textarea&lt;/code&gt;, you should use it.&lt;/li&gt;
&lt;li&gt;To help browsers autofill forms, give input &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; attributes &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#stable-name-id&quot;&gt;stable values&lt;/a&gt;
that don&#39;t change between page loads or website deployments.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#disable-submit&quot;&gt;Disable submit buttons&lt;/a&gt; once they&#39;ve been tapped or clicked.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#validate&quot;&gt;Validate&lt;/a&gt; data during entry—not just on form submission.&lt;/li&gt;
&lt;li&gt;Make &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#guest-checkout&quot;&gt;guest checkout&lt;/a&gt; the default and make account creation simple once checkout
is complete.&lt;/li&gt;
&lt;li&gt;Show &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#checkout-progress&quot;&gt;progress through the checkout process&lt;/a&gt; in clear steps with clear calls
to action.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#reduce-checkout-exits&quot;&gt;Limit potential checkout exit points&lt;/a&gt; by removing clutter and distractions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#checkout-details&quot;&gt;Show full order details&lt;/a&gt; at checkout and make order adjustments easy.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#unneeded-data&quot;&gt;Don&#39;t ask for data you don&#39;t need&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#single-name-input&quot;&gt;Ask for names with a single input&lt;/a&gt; unless you have a good reason not to.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#unicode-matching&quot;&gt;Don&#39;t enforce Latin-only characters&lt;/a&gt; for names and usernames.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-variety&quot;&gt;Allow for a variety of address formats&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Consider using a &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-textarea&quot;&gt;single &lt;code&gt;textarea&lt;/code&gt; for address&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#billing-address&quot;&gt;autocomplete for billing address&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#internationalization-localization&quot;&gt;Internationalize and localize&lt;/a&gt; where necessary.&lt;/li&gt;
&lt;li&gt;Consider avoiding &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#postal-code-address-lookup&quot;&gt;postal code address lookup&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#payment-form-autocomplete&quot;&gt;appropriate payment card autocomplete values&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#single-number-input&quot;&gt;single input for payment card numbers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#avoid-custom-elements&quot;&gt;Avoid using custom elements&lt;/a&gt; if they break the autofill experience.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#analytics-rum&quot;&gt;Test in the field as well as the lab&lt;/a&gt;: page analytics, interaction analytics, and
real-user performance measurement.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#test-platforms&quot;&gt;Test on a range of browsers, devices, and platforms&lt;/a&gt;.&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 article is about frontend best practices for address and payment forms. It does not explain how to implement transactions on your site. To find out more about adding payment functionality to your website, see &lt;a href=&quot;https://web.dev/payments&quot;&gt;Web Payments&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;meaningful-html&quot;&gt;Use meaningful HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#meaningful-html&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the elements and attributes built for the job:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;, &lt;code&gt;autocomplete&lt;/code&gt;, and &lt;code&gt;inputmode&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These enable built-in browser functionality, improve accessibility, and add meaning to your markup.&lt;/p&gt;
&lt;h3 id=&quot;html-elements&quot;&gt;Use HTML elements as intended &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-elements&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;html-form&quot;&gt;Put your form in a &amp;lt;form&amp;gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-form&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You might be tempted not to bother wrapping your &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements in a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, and to handle data
submission purely with JavaScript.&lt;/p&gt;
&lt;p&gt;Don&#39;t do it!&lt;/p&gt;
&lt;p&gt;An HTML &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; gives you access to a powerful set of built-in features across all modern browsers,
and can help make your site accessible to screen readers and other assistive devices. A &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;
also makes it simpler to build basic functionality for older browsers with limited JavaScript
support, and to enable form submission even if there&#39;s a glitch with your code—and for the
small number of users who actually disable JavaScript.&lt;/p&gt;
&lt;p&gt;If you have more than one page component for user input, make sure to put each in its own &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;
element. For example, if you have search and sign-up on the same page, put each in its own &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;html-label&quot;&gt;Use &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; to label elements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-label&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To label an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;, use a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/label&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Associate a label with an input by giving the label&#39;s &lt;code&gt;for&lt;/code&gt; attribute the same value as
the input&#39;s &lt;code&gt;id&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 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;address-line1&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;Address line 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;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;address-line1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;…&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;Use a single label for a single input: don&#39;t try to label multiple inputs with only one label. This
works best for browsers, and best for screenreaders. A tap or click on a label moves focus to the input
it&#39;s associated with, and screenreaders announce label text when the &lt;em&gt;label&lt;/em&gt; or the label&#39;s &lt;em&gt;input&lt;/em&gt;
gets focus.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t use &lt;a href=&quot;https://www.smashingmagazine.com/2018/06/placeholder-attribute/&quot;&gt;placeholders&lt;/a&gt; on their own instead of labels. Once you start entering text in an input, the placeholder is hidden, so it can be easy to forget what the input is for. The same is true if you use the placeholder to show the correct format for values such as dates. This can be especially problematic for users on phones, particularly if they&#39;re distracted or feeling stressed! &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;html-button&quot;&gt;Make buttons helpful &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-button&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/button&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/a&gt;
for buttons! You can also use &lt;code&gt;&amp;lt;input type=&amp;quot;submit&amp;quot;&amp;gt;&lt;/code&gt;, but don&#39;t use a &lt;code&gt;div&lt;/code&gt; or some
other random element acting as a button. Button elements provide accessible behaviour, built-in
form submission functionality, and can easily be styled.&lt;/p&gt;
&lt;p&gt;Give each form submit button a value that says what it does. For each step towards checkout, use
a descriptive call-to-action that shows progress and makes the next step obvious. For example, label
the submit button on your delivery address form &lt;strong&gt;Proceed to Payment&lt;/strong&gt; rather than &lt;strong&gt;Continue&lt;/strong&gt;
or &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;
&lt;p id=&quot;disable-submit&quot;&gt;&lt;/p&gt;
&lt;p&gt;Consider disabling a submit button once the user has tapped or clicked it—especially when the user is
making a payment or placing an order. Many users click buttons repeatedly, even if they&#39;re working fine.
That can mess up checkout and add to server load.&lt;/p&gt;
&lt;p&gt;On the other hand, don&#39;t disable a submit button waiting on complete and valid user input. For
example, don&#39;t just leave a &lt;strong&gt;Save Address&lt;/strong&gt; button disabled because something is missing or invalid.
That doesn&#39;t help the user—they may continue to tap or click the button and assume that it&#39;s broken.
Instead, if users attempt to submit a form with invalid data, explain to them what&#39;s gone wrong
and what to do to fix it. This is particularly important on mobile, where data entry is more
difficult and missing or invalid form data may not be visible on the user&#39;s screen by the time they
attempt to submit a form.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The default type for a button in a form is &lt;code&gt;submit&lt;/code&gt;. If you want to add another button in a form (for &lt;strong&gt;Show password&lt;/strong&gt;, for example) add &lt;code&gt;type=&amp;quot;button&amp;quot;&lt;/code&gt;. Otherwise clicking or tapping on the button will submit the form.  Pressing the &lt;code&gt;Enter&lt;/code&gt; key while any form field has focus simulates a click on the first &lt;code&gt;submit&lt;/code&gt; button in the form. If you include a button in your form before the &lt;strong&gt;Submit&lt;/strong&gt; button, and don&#39;t specify the type, that button will have the default type for buttons in a form (&lt;code&gt;submit&lt;/code&gt;) and receive the click event before the form is submitted. For an example of this, see our &lt;a href=&quot;https://enter-button.glitch.me/&quot;&gt;demo&lt;/a&gt;: fill in the form, then press &lt;code&gt;Enter&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;html-attributes&quot;&gt;Make the most of HTML attributes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#html-attributes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;make-it-easy-for-users-to-enter-data&quot;&gt;Make it easy for users to enter data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#make-it-easy-for-users-to-enter-data&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p id=&quot;type-attribute&quot;&gt;&lt;/p&gt;
&lt;p&gt;Use the appropriate input &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/input/email&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;type&lt;/code&gt; attribute&lt;/a&gt;
to provide the right keyboard on mobile and enable basic built-in validation by the browser.&lt;/p&gt;
&lt;p&gt;For example, use &lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt; for email addresses and &lt;code&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; for phone numbers.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of   Android phones, showing a keyboard appropriate for entering an email address (using type=email)   and for entering a telephone number (with type=tel).&quot; decoding=&quot;async&quot; height=&quot;683&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bi7J9Z1TLP4IsQLyhbQm.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Keyboards appropriate for email and telephone.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p id=&quot;inputmode-attribute&quot;&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; Using type=&amp;quot;number&amp;quot; adds an up/down arrow to increment numbers, which makes no sense for data such as telephone, payment card or account numbers.  For numbers like these, set &lt;code&gt;type=&amp;quot;text&amp;quot;&lt;/code&gt; (or leave off the attribute, since &lt;code&gt;text&lt;/code&gt; is the default). For telephone numbers, use &lt;code&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; to get the appropriate keyboard on mobile. For other numbers use &lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt; to get a numeric keyboard on mobile.  Some sites still use &lt;code&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; for payment card numbers to ensure that mobile users get the right keyboard. However, &lt;code&gt;inputmode&lt;/code&gt; is &lt;a href=&quot;https://caniuse.com/input-inputmode&quot;&gt;very widely supported now&lt;/a&gt;, so you shouldn&#39;t have to do that—but do check your users&#39; browsers. &lt;/div&gt;&lt;/aside&gt;
&lt;p id=&quot;avoid-custom-elements&quot;&gt;&lt;/p&gt;
&lt;p&gt;For dates, try to avoid using custom &lt;code&gt;select&lt;/code&gt; elements. They break the autofill experience if not
properly implemented and don&#39;t work on older browsers. For numbers such as birth year, consider
using an &lt;code&gt;input&lt;/code&gt; element rather than a &lt;code&gt;select&lt;/code&gt;, since entering digits manually can be easier and less
error prone than selecting from a long drop-down list—especially on mobile. Use &lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt;
to ensure the right keyboard on mobile and add validation and format hints with text or a
placeholder to make sure the user enters data in the appropriate format.&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 &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/datalist&quot;&gt;&lt;code&gt;datalist&lt;/code&gt;&lt;/a&gt; element enables a user to select from a list of available options and provides matching suggestions as the user enters text. Try out &lt;code&gt;datalist&lt;/code&gt; for &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;range&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt; inputs at &lt;a href=&quot;https://simpl.info/datalist&quot;&gt;simpl.info/datalist&lt;/a&gt;. For birth year input, you can compare a &lt;code&gt;select&lt;/code&gt; with an &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;datalist&lt;/code&gt; at &lt;a href=&quot;https://datalist-select.glitch.me/&quot;&gt;datalist-select.glitch.me&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;autocomplete-attribute&quot;&gt;Use autocomplete to improve accessibility and help users avoid re-entering data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#autocomplete-attribute&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using appropriate &lt;code&gt;autocomplete&lt;/code&gt; values enables browsers to help users by securely storing data and
autofilling &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, and &lt;code&gt;textarea&lt;/code&gt; values. This is particularly important on mobile, and
crucial for avoiding &lt;a href=&quot;https://www.zuko.io/blog/8-surprising-insights-from-zukos-benchmarking-data&quot; rel=&quot;noopener&quot;&gt;high form abandonment rates&lt;/a&gt;. Autocomplete also provides &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/identify-input-purpose.html&quot; rel=&quot;noopener&quot;&gt;multiple accessibility benefits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If an appropriate autocomplete value is available for a form field, you should use it. &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Attributes/autocomplete&quot; rel=&quot;noopener&quot;&gt;MDN web docs&lt;/a&gt; has a full list of values and explanations of how to use
them correctly.&lt;/p&gt;
&lt;p id=&quot;stable-name-id&quot;&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; As well as using appropriate autocomplete values, help browsers autofill forms by giving form fields &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; attributes &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#stable-name-id&quot;&gt;stable values&lt;/a&gt; that don&#39;t change between page loads or website deployments. &lt;/div&gt;&lt;/aside&gt;
&lt;p id=&quot;billing-address&quot;&gt;&lt;/p&gt;
&lt;p&gt;By default, set the billing address to be the same as the delivery address. Reduce visual clutter by
providing a link to edit the billing address (or use &lt;a href=&quot;https://simpl.info/details/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;summary&lt;/code&gt; and &lt;code&gt;details&lt;/code&gt; elements&lt;/a&gt;)
rather than displaying the billing address in a form.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Example checkout page showing link to change billing address.&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TIan7TU8goyoOXwLPYyd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Add a link to review billing.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Use appropriate autocomplete values for the billing address, just as you do for shipping address,
so the user doesn&#39;t have to enter data more than once. Add a prefix word to autocomplete
attributes if you have different values for inputs with the same name in different sections.&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;shipping address-line-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;billing address-line-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&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;h4 id=&quot;validation&quot;&gt;Help users enter the right data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#validation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Try to avoid &amp;quot;telling off&amp;quot; customers because they &amp;quot;did something wrong&amp;quot;. Instead, help users complete
forms more quickly and easily by helping them fix problems as they happen. Through the checkout process,
customers are trying to give your company money for a product or service—your job is to assist them,
not to punish them!&lt;/p&gt;
&lt;p&gt;You can add &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5/Constraint_validation#Intrinsic_and_basic_constraints&quot; rel=&quot;noopener&quot;&gt;constraint attributes&lt;/a&gt; to form elements to specify acceptable
values, including &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, and &lt;code&gt;pattern&lt;/code&gt;. The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ValidityState&quot; rel=&quot;noopener&quot;&gt;validity state&lt;/a&gt;
of the element is set automatically depending on whether the element&#39;s value is valid, as are the
&lt;code&gt;:valid&lt;/code&gt; and &lt;code&gt;:invalid&lt;/code&gt; CSS pseudo-classes which can be used to style elements with valid or invalid
values.&lt;/p&gt;
&lt;p&gt;For example, the following HTML specifies input for a birth year between
1900 and 2020. Using &lt;code&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt; constrains input values to numbers only, within the range
specified by &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt;. If you attempt to enter a number outside the range, the input will be
set to have an invalid state.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 170px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/constraints?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;constraints on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The following example uses &lt;code&gt;pattern=&amp;quot;[\d ]{10,30}&amp;quot;&lt;/code&gt; to ensure a valid payment card number, while
allowing spaces:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 170px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-card-input?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-card-input on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Modern browsers also do basic validation for inputs with type &lt;code&gt;email&lt;/code&gt; or &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 250px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/type-validation?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;type-validation on Glitch&quot;&gt;&lt;/iframe&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; Add the &lt;code&gt;multiple&lt;/code&gt; attribute to an input with &lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt; to enable built-in validation for multiple comma-separated email addresses in a single input. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;On form submission, browsers automatically set focus on fields with problematic or missing required
values. No JavaScript required!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a sign-in form in Chrome on desktop showing browser prompt and focus for an invalid email value.&quot; decoding=&quot;async&quot; height=&quot;483&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mPyN7THWJNRQIiBezq6l.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
  &lt;figcaption&gt;Basic built-in validation by the browser.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Validate inline and provide feedback to the user as they enter data, rather than providing a list of
errors when they click the submit button. If you need to validate data on your server after form
submission, list all problems that are found and clearly highlight all form fields with invalid
values, as well as displaying a message inline next to each problematic field explaining what needs
to be fixed. Check server logs and analytics data for common errors—you may need to redesign your form.&lt;/p&gt;
&lt;p&gt;You should also use JavaScript to do more robust validation while users are entering data and
on form submission. Use the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/forms.html#constraints&quot; rel=&quot;noopener&quot;&gt;Constraint Validation API&lt;/a&gt;
(which is &lt;a href=&quot;https://caniuse.com/#feat=constraint-validation&quot; rel=&quot;noopener&quot;&gt;widely supported&lt;/a&gt;) to add custom
validation using built-in browser UI to set focus and display prompts.&lt;/p&gt;
&lt;p&gt;Find out more in &lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ux/input/forms#use_javascript_for_more_complex_real-time_validation&quot; rel=&quot;noopener&quot;&gt;Use JavaScript for more complex real-time validation&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; Even with client-side validation and data input constraints, you must still ensure that your back-end securely handles input and output of data from users. Never trust user input: it could be malicious. Find out more in &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP Input Validation Cheat Sheet&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;required&quot;&gt;Help users avoid missing required data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#required&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Attributes/required&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;required&lt;/code&gt; attribute&lt;/a&gt;
on inputs for mandatory values.&lt;/p&gt;
&lt;p&gt;When a form is submitted &lt;a href=&quot;https://caniuse.com/mdn-api_htmlinputelement_required&quot; rel=&quot;noopener&quot;&gt;modern browsers&lt;/a&gt;
automatically prompt and set focus on &lt;code&gt;required&lt;/code&gt; fields with missing data, and you can use the
&lt;a href=&quot;https://caniuse.com/?search=required&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;:required&lt;/code&gt; pseudo-class&lt;/a&gt; to highlight required fields. No
JavaScript required!&lt;/p&gt;
&lt;p&gt;Add an asterisk to the label for every required field, and add a note at the start of the form to
explain what the asterisk means.&lt;/p&gt;
&lt;h2 id=&quot;checkout-forms&quot;&gt;Simplify checkout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#checkout-forms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;m-commerce-gap&quot;&gt;Mind the mobile commerce gap! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#m-commerce-gap&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Imagine that your users have a &lt;em&gt;fatigue budget&lt;/em&gt;. Use it up, and your users will leave.&lt;/p&gt;
&lt;p&gt;You need to reduce friction and maintain focus, especially on mobile. Many sites get more &lt;em&gt;traffic&lt;/em&gt;
on mobile but more &lt;em&gt;conversions&lt;/em&gt; on desktop—a phenomenon known as the &lt;a href=&quot;https://www.comscore.com/Insights/Presentations-and-Whitepapers/2017/Mobiles-Hierarchy-of-Needs&quot; rel=&quot;noopener&quot;&gt;mobile commerce gap&lt;/a&gt;. Customers may simply prefer to
complete a purchase on desktop, but lower mobile conversion rates are also a result of poor user
experience. Your job is to &lt;em&gt;minimize&lt;/em&gt; lost conversions on mobile and &lt;em&gt;maximize&lt;/em&gt; conversions
on desktop. &lt;a href=&quot;https://www.comscore.com/Insights/Presentations-and-Whitepapers/2017/Mobiles-Hierarchy-of-Needs&quot; rel=&quot;noopener&quot;&gt;Research has shown&lt;/a&gt; that there&#39;s a huge opportunity to provide a better mobile form experience.&lt;/p&gt;
&lt;p&gt;Most of all, users are more likely to abandon forms that look long, that are complex, and without a sense of
direction. This is especially true when users are on smaller screens, distracted, or in a rush. Ask for as
little data as possible.&lt;/p&gt;
&lt;h3 id=&quot;guest-checkout&quot;&gt;Make guest checkout the default &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#guest-checkout&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For an online store, the simplest way to reduce form friction is to make guest checkout the default.
Don&#39;t force users to create an account before making a purchase. Not allowing guest checkout is cited
as a major reason for shopping cart abandonment.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Reasons for shopping cart abandonment during checkout.&quot; decoding=&quot;async&quot; height=&quot;503&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/a7OQLnCRb0FZglj07N7z.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;From &lt;a href=&quot;https://baymard.com/checkout-usability&quot;&gt;baymard.com/checkout-usability&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can offer account sign-up after checkout.  At that point, you already have most of the data you
need to set up an account, so account creation should be quick and easy for the user.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you do offer sign-up after checkout, make sure that the purchase the user just made is linked to their newly created account! &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;checkout-progress&quot;&gt;Show checkout progress  &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#checkout-progress&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can make your checkout process feel less complex by showing progress and making it clear what
needs to be done next. The video below shows how UK retailer &lt;a href=&quot;https://www.johnlewis.com/&quot; rel=&quot;noopener&quot;&gt;johnlewis.com&lt;/a&gt;
achieves this.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ViftAUUUHr4TDXNec0Ch.png?auto=format&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/6gIb1yWrIMZFiv775B2y.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;Show checkout progress.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You need to maintain momentum! For each step towards payment, use page headings and descriptive
button values that make it clear what needs to be done now, and what checkout step is next.&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://samdutton.com/address-form-save.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;Give form buttons meaningful names that show what&#39;s next.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Use the &lt;code&gt;enterkeyhint&lt;/code&gt; attribute on form inputs to set the mobile keyboard enter key label. For
example, use &lt;code&gt;enterkeyhint=&amp;quot;previous&amp;quot;&lt;/code&gt; and &lt;code&gt;enterkeyhint=&amp;quot;next&amp;quot;&lt;/code&gt; within a multi-page form,
&lt;code&gt;enterkeyhint=&amp;quot;done&amp;quot;&lt;/code&gt; for the final input in the form, and &lt;code&gt;enterkeyhint=&amp;quot;search&amp;quot;&lt;/code&gt; for a search
input.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of an address form on Android showing how the enterkeyhint input attribute changes the enter key button icon.&quot; decoding=&quot;async&quot; height=&quot;684&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QoY8Oynpw0CqjPACtCdG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Enter key buttons on Android: &#39;next&#39; and &#39;done&#39;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;enterkeyhint&lt;/code&gt; attribute is &lt;a href=&quot;https://caniuse.com/mdn-html_global_attributes_enterkeyhint&quot; rel=&quot;noopener&quot;&gt;supported on Android and iOS&lt;/a&gt;.
You can find out more from the &lt;a href=&quot;https://github.com/dtapuska/enterkeyhint&quot; rel=&quot;noopener&quot;&gt;enterkeyhint explainer&lt;/a&gt;.&lt;/p&gt;
&lt;p id=&quot;checkout-details&quot;&gt;&lt;/p&gt;
&lt;p&gt;Make it easy for users to go back and forth within the checkout process, to easily adjust
their order, even when they&#39;re at the final payment step. Show full details of the order, not just
a limited summary. Enable users to easily adjust item quantities from the payment page. Your priority at checkout is to
avoid interrupting progress towards conversion.&lt;/p&gt;
&lt;h3 id=&quot;reduce-checkout-exits&quot;&gt;Remove distractions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#reduce-checkout-exits&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Limit potential exit points by removing visual clutter and distractions such as product promotions.
Many successful retailers even remove navigation and search from checkout.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots on mobile showing progress through johnlewis.com checkout. Search, navigation and other distractions are removed.&quot; decoding=&quot;async&quot; height=&quot;683&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/UR97R2LqJ5MRkL5H4V0U.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Search, navigation and other distractions removed for checkout.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Keep the journey focused. This is not the time to tempt users to do something else!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of checkout page on mobile showing distracting promotion for FREE STICKERS.&quot; decoding=&quot;async&quot; height=&quot;735&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 350px) 350px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lKJwd5e2smBfDjNxV22N.jpg?auto=format&amp;w=700 700w&quot; width=&quot;350&quot; /&gt;
  &lt;figcaption&gt;Don&#39;t distract customers from completing their purchase.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For returning users you can simplify the checkout flow even more, by hiding data they don&#39;t need to
see. For example: display the delivery address in plain text (not in a form) and allow users to
change it via a link.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of &amp;#x27;Review order&amp;#x27; section of checkout page, showing text in plain text, with links to change delivery address, payment method and billing address, which are not displayed.&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 450px) 450px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xEAYOeEFYhOZLaB2aeCY.png?auto=format&amp;w=900 900w&quot; width=&quot;450&quot; /&gt;
  &lt;figcaption&gt;Hide data customers don&#39;t need to see.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;address-forms&quot;&gt;Make it easy to enter name and address &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-forms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;unneeded-data&quot;&gt;Only ask for the data you need &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#unneeded-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before you start coding your name and address forms, make sure to understand what data is required.
Don&#39;t ask for data you don&#39;t need! The simplest way to reduce form complexity is to remove unnecessary
fields. That&#39;s also good for customer privacy and can reduce back-end data cost and liability.&lt;/p&gt;
&lt;h3 id=&quot;single-name-input&quot;&gt;Use a single name input &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#single-name-input&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Allow your users to enter their name using a single input, unless you have a good reason for
separately storing given names, family names, honorifics, or other name parts. Using a single name
input makes forms less complex, enables cut-and-paste, and makes autofill simpler.&lt;/p&gt;
&lt;p&gt;In particular, unless you have good reason not to, don&#39;t bother adding a separate input for a
prefix or title (like Mrs, Dr or Lord). Users can type that in with their name if they want to. Also,
&lt;code&gt;honorific-prefix&lt;/code&gt; autocomplete currently doesn&#39;t work in most browsers, and so adding a field for
name prefix or title will break the address form autofill experience for most users.&lt;/p&gt;
&lt;h3 id=&quot;enable-name-autofill&quot;&gt;Enable name autofill &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#enable-name-autofill&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;name&lt;/code&gt; for a full name:&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;...&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;If you really do have a good reason to split out name parts, make sure to use appropriate
autocomplete values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;honorific-prefix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;given-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nickname&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;additional-name-initial&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;additional-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;family-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;honorific-suffix&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;unicode-matching&quot;&gt;Allow international names &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#unicode-matching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You might want to validate your name inputs, or restrict the characters allowed for name data. However,
you need to be as unrestrictive as possible with alphabets. It&#39;s rude to be told your name is &amp;quot;invalid&amp;quot;!&lt;/p&gt;
&lt;p&gt;For validation, avoid using regular expressions that only match Latin characters. Latin-only excludes
users with names or addresses that include characters that aren&#39;t in the Latin alphabet. Allow Unicode
letter matching instead—and ensure your back-end supports Unicode securely as both input and output.
Unicode in regular expressions is well supported by modern browsers.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&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 comment&quot;&gt;&amp;lt;!-- Names with non-Latin characters (such as Françoise or Jörg) are &#39;invalid&#39;. --&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;pattern&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;[\w \-]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&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 comment&quot;&gt;&amp;lt;!-- Accepts Unicode letters. --&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;pattern&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;[\p{L} \-]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;
   &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
     &lt;source src=&quot;https://samdutton.com/unicode-letter-matching.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;Unicode letter matching compared to Latin-only letter matching.&lt;/figcaption&gt;
&lt;/figure&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 find out more about &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#internationalization-localization&quot;&gt;internationalization and localization&lt;/a&gt; below, but make sure your forms work for names in all regions where you have users. For example, for Japanese names you should consider having a field for phonetic names. This helps customer support staff say the customer&#39;s name on the phone. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;address-variety&quot;&gt;Allow for a variety of address formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-variety&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When you&#39;re designing an address form, bear in mind the bewildering variety of address formats,
even within a single country. Be careful not to make assumptions about &amp;quot;normal&amp;quot; addresses. (Take a
look at &lt;a href=&quot;http://www.paulplowman.com/stuff/uk-address-oddities/&quot; rel=&quot;noopener&quot;&gt;UK Address Oddities!&lt;/a&gt; if you&#39;re not
convinced!)&lt;/p&gt;
&lt;h4 id=&quot;make-address-forms-flexible&quot;&gt;Make address forms flexible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#make-address-forms-flexible&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Don&#39;t force users to try to squeeze their address into form fields that don&#39;t fit.&lt;/p&gt;
&lt;p&gt;For example, don&#39;t insist on a house number and street name in separate inputs, since many addresses
don&#39;t use that format, and incomplete data can break browser autofill.&lt;/p&gt;
&lt;p&gt;Be especially careful with &lt;code&gt;required&lt;/code&gt; address fields. For example, addresses in large cities in the
UK do not have a county, but many sites still force users to enter one.&lt;/p&gt;
&lt;p&gt;Using two flexible address lines can work well enough for a variety of address formats.&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;address-line-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;address-line1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;address-line-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;address-line2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&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;Add labels to match:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;address-line-1&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;Address line 1 (or company name)&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;address-line-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;address-line1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;address-line-2&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;Address line 2 (optional)&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;address-line-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;address-line2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can try this out by remixing and editing the demo embedded below.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Research shows that &lt;a href=&quot;https://baymard.com/blog/address-line-2&quot;&gt;&lt;strong&gt;Address line 2&lt;/strong&gt; may be problematic for users&lt;/a&gt;. Bear this in mind when designing address forms—you should consider alternatives such as using a single &lt;code&gt;textarea&lt;/code&gt; (see below) or other options. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;address-textarea&quot;&gt;Consider using a single textarea for address &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-textarea&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The most flexible option for addresses is to provide a single &lt;code&gt;textarea&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;textarea&lt;/code&gt; approach fits any address format, and it&#39;s great for cutting and pasting—but bear in
mind that it may not fit your data requirements, and users may miss out on autofill if they previously
only used forms with &lt;code&gt;address-line1&lt;/code&gt; and &lt;code&gt;address-line2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a textarea, use &lt;code&gt;street-address&lt;/code&gt; as the autocomplete value.&lt;/p&gt;
&lt;p&gt;Here is an example of a form that demonstrates the use of a single &lt;code&gt;textarea&lt;/code&gt; for address:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 980px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/address-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;address-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h4 id=&quot;internationalization-localization&quot;&gt;Internationalize and localize your address forms &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#internationalization-localization&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It&#39;s especially important for address forms to consider &lt;a href=&quot;https://www.smashingmagazine.com/2020/11/internationalization-localization-static-sites/&quot; rel=&quot;noopener&quot;&gt;internationalization and localization&lt;/a&gt;, depending
on where your users are located.&lt;/p&gt;
&lt;p&gt;Be aware that the naming of address parts varies as well as address formats, even within the same
language.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;    ZIP code: US&lt;br /&gt; Postal code: Canada&lt;br /&gt;    Postcode: UK&lt;br /&gt;     Eircode: Ireland&lt;br /&gt;         PIN: India&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It can be irritating or puzzling to be presented with a form that doesn&#39;t fit your address or that
doesn&#39;t use the words you expect.&lt;/p&gt;
&lt;p&gt;Customizing address forms &lt;a href=&quot;https://www.smashingmagazine.com/2020/11/internationalization-localization-static-sites#determining-user-s-language-and-region&quot; rel=&quot;noopener&quot;&gt;for multiple locales&lt;/a&gt; may be necessary for your site, but using techniques
to maximize form flexibility (as described above) may be adequate. If you don&#39;t localize your address
forms, make sure to understand the key priorities to cope with a range of address formats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid being over-specific about address parts, such as insisting on a street name or house number.&lt;/li&gt;
&lt;li&gt;Where possible avoid making fields &lt;code&gt;required&lt;/code&gt;. For example, addresses in many countries don&#39;t
have a postal code, and rural addresses may not have a street or road name.&lt;/li&gt;
&lt;li&gt;Use inclusive naming: &#39;Country/region&#39; not &#39;Country&#39;; &#39;ZIP/postal code&#39; not &#39;ZIP&#39;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep it flexible! The &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#address-textarea&quot;&gt;simple address form example above&lt;/a&gt; can be adapted to work
&#39;well enough&#39; for many locales.&lt;/p&gt;
&lt;h4 id=&quot;postal-code-address-lookup&quot;&gt;Consider avoiding postal code address lookup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#postal-code-address-lookup&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some websites use a service to look up addresses based on postal code or ZIP. This may be sensible
for some use cases, but you should be aware of the potential downsides.&lt;/p&gt;
&lt;p&gt;Postal code address suggestion doesn&#39;t work for all countries—and in some regions, post codes can
include a large number of potential addresses.&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://samdutton.com/long-list-of-addresses.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;ZIP or postal codes may include a lot of addresses!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It&#39;s difficult for users to select from a long list of addresses—especially on mobile if they&#39;re
rushed or stressed. It can be easier and less error prone to let users take advantage of autofill,
and enter their complete address filled with a single tap or click.&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://samdutton.com/full-name-autofill.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;A single name input enables one-tap (one-click) address entry.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;general-guidelines&quot;&gt;Simplify payment forms &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#general-guidelines&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Payment forms are the single most critical part of the checkout process. Poor payment form design is
a &lt;a href=&quot;https://www.comscore.com/Insights/Presentations-and-Whitepapers/2017/Mobiles-Hierarchy-of-Needs&quot; rel=&quot;noopener&quot;&gt;common cause of shopping cart abandonment&lt;/a&gt;. The &lt;a href=&quot;https://en.wikipedia.org/wiki/The_devil_is_in_the_detail#cite_note-Titelman-1&quot; rel=&quot;noopener&quot;&gt;devil&#39;s in the details&lt;/a&gt;: small
glitches can tip users towards abandoning a purchase, particularly on mobile. Your job is to design
forms to make it as easy as possible for users to enter data.&lt;/p&gt;
&lt;h3 id=&quot;payment-form-autocomplete&quot;&gt;Help users avoid re-entering payment data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#payment-form-autocomplete&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Make sure to add appropriate &lt;code&gt;autocomplete&lt;/code&gt; values in payment card forms, including the payment card
number, name on the card, and the expiry month and year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cc-number&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-exp-month&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-exp-year&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This enables browsers to help users by securely storing payment card details and correctly entering form
data. Without autocomplete, users may be more likely to keep a physical record of payment card details,
or store payment card data insecurely on their device.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t add a selector for payment card type, since this can always be inferred from the payment card number. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;avoid-using-custom-elements-for-payment-card-dates&quot;&gt;Avoid using custom elements for payment card dates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#avoid-using-custom-elements-for-payment-card-dates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If not properly designed, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Web_Components/Using_custom_elements&quot; rel=&quot;noopener&quot;&gt;custom elements&lt;/a&gt;
can interrupt payment flow by breaking autofill, and won&#39;t work on older browsers. If all other
payment card details are available from autocomplete but a user is forced to find their physical
payment card to look up an expiry date because autofill didn&#39;t work for a custom element, you&#39;re
likely to lose a sale. Consider using standard HTML elements instead, and style them accordingly.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of payment form showing custom elements for card expiry date that interrupt autofill.&quot; decoding=&quot;async&quot; height=&quot;916&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1LIQm2Jt5PHxN0I7tni3.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Autocomplete filled all the fields—except the expiry date!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;single-number-input&quot;&gt;Use a single input for payment card and phone numbers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#single-number-input&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For payment card and phone numbers use a single input: don&#39;t split the number into parts. That makes
it easier for users to enter data, makes validation simpler, and enables browsers to autofill.
Consider doing the same for other numeric data such as PIN and bank codes.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of payment form showing a credit card field split into four input elements.&quot; decoding=&quot;async&quot; height=&quot;833&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cUwamPstwSQTlbmQ4CT.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Don&#39;t use multiple inputs for a credit card number.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;validate&quot;&gt;Validate carefully &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#validate&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You should validate data entry both in realtime and before form submission. One way to do this is by
adding a &lt;code&gt;pattern&lt;/code&gt; attribute to a payment card input. If the user attempts to submit the payment form
with an invalid value, the browser displays a warning message and sets focus on the input. No
JavaScript required!&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 170px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-card-input?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-card-input on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;However, your &lt;code&gt;pattern&lt;/code&gt; regular expression must be flexible enough to handle &lt;a href=&quot;https://github.com/jaemok/credit-card-input/blob/master/creditcard.js#L35&quot; rel=&quot;noopener&quot;&gt;the range of payment card
number lengths&lt;/a&gt;: from 14
digits (or possibly less) to 20 (or more). You can find out more about payment card number structuring
from &lt;a href=&quot;https://ldapwiki.com/wiki/Bank%20Card%20Number&quot; rel=&quot;noopener&quot;&gt;LDAPwiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Allow users to include spaces when they&#39;re entering a new payment card number, since this is how
numbers are displayed on physical cards. That&#39;s friendlier to the user (you won&#39;t have to tell them
&amp;quot;they did something wrong&amp;quot;), less likely to interrupt conversion flow, and it&#39;s straightforward to
remove spaces in numbers before processing.&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 may want to use a one-time passcode for identity or payment verification. However, asking users to manually enter a code or copy it from an email or an SMS text is error-prone and a source of friction. Learn about better ways to enable one-time passcodes in &lt;a href=&quot;https://web.dev/sms-otp-form&quot;&gt;SMS OTP form best practices&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;test-platforms&quot;&gt;Test on a range of devices, platforms, browsers and versions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#test-platforms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s particularly important to test address and payment forms on the platforms most common for
your users, since form element functionality and appearance may vary, and differences in viewport
size can lead to problematic positioning. BrowserStack enables &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;free testing for open source projects&lt;/a&gt; on a range of devices and browsers.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of a payment form, payment-form.glitch.me, on iPhone 7 and 11. The Complete Payment button is shown on iPhone 11 but not 7&quot; decoding=&quot;async&quot; height=&quot;707&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Uk7WhpDMuHtvjmWlFnJE.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The same page on iPhone 7 and iPhone 11.&lt;br /&gt;Reduce padding for
    smaller mobile viewports to ensure the &lt;strong&gt;Complete payment&lt;/strong&gt; button isn&#39;t hidden.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;analytics-rum&quot;&gt;Implement analytics and RUM &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#analytics-rum&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Testing usability and performance locally can be helpful, but you need real-world data to properly
understand how users experience your payment and address forms.&lt;/p&gt;
&lt;p&gt;For that you need analytics and Real User Monitoring—data for the experience of actual users, such
as how long checkout pages take to load or how long payment takes to complete:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Page analytics&lt;/strong&gt;: page views, bounce rates and exits for every page with a form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interaction analytics&lt;/strong&gt;: &lt;a href=&quot;https://support.google.com/analytics/answer/6180923?hl=en&quot; rel=&quot;noopener&quot;&gt;goal funnels&lt;/a&gt;
and &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/gtagjs/events&quot; rel=&quot;noopener&quot;&gt;events&lt;/a&gt;
indicate where users abandon your checkout flow and what actions do they take when interacting
with your forms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Website performance&lt;/strong&gt;: &lt;a href=&quot;https://web.dev/user-centric-performance-metrics&quot;&gt;user-centric metrics&lt;/a&gt; can tell you if your
checkout pages are slow to load and, if so—what&#39;s the cause.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Page analytics, interaction analytics, and real user performance measurement become especially
valuable when combined with server logs, conversion data, and A/B testing, enabling you to answer
questions such as whether discount codes increase revenue, or whether a change in form layout
improves conversions.&lt;/p&gt;
&lt;p&gt;That, in turn, gives you a solid basis for prioritizing effort, making changes, and rewarding success.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Keep learning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/payment-and-address-form-best-practices/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices&quot;&gt;Sign-in form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices&quot;&gt;Sign-up form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web-otp&quot;&gt;Verify phone numbers on the web with the WebOTP API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/forms/&quot;&gt;Create Amazing Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2018/08/best-practices-for-mobile-form-design/&quot; rel=&quot;noopener&quot;&gt;Best Practices For Mobile Form Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/more-capable-form-controls&quot;&gt;More capable form controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webaim.org/techniques/forms/&quot; rel=&quot;noopener&quot;&gt;Creating Accessible Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/credential-management-api/&quot; rel=&quot;noopener&quot;&gt;Streamlining the Sign-up Flow Using Credential Management API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.columbia.edu/~fdc/postal/&quot; rel=&quot;noopener&quot;&gt;Frank&#39;s Compulsive Guide to Postal Addresses&lt;/a&gt; provides
useful links and extensive guidance for address formats in over 200 countries.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.countries-list.info/Download-List&quot; rel=&quot;noopener&quot;&gt;Countries Lists&lt;/a&gt; has a tool for downloading country
codes and names in multiple languages, in multiple formats.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@rupixen&quot; rel=&quot;noopener&quot;&gt;@rupixen&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/Q59HmzK38eQ&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Sign-up form best practices</title>
    <link href="https://web.dev/sign-up-form-best-practices/"/>
    <updated>2020-12-09T00:00:00Z</updated>
    <id>https://web.dev/sign-up-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;Ev2mCzJZLtY&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;If users ever need to log in to your site, then good sign-up form design is
critical. This is especially true for people on poor connections, on mobile, in
a hurry, or under stress. Poorly designed sign-up forms get high bounce rates.
Each bounce could mean a lost and disgruntled user—not just a missed sign-up
opportunity.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewbox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you would prefer to learn these best practices with a hands-on tutorial, check out the &lt;a href=&quot;https://web.dev/codelab-sign-up-form-best-practices&quot;&gt;Sign-up form best practices codelab&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Here is an example of a very simple sign-up form that demonstrates all of the best practices:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 700px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/signup-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;signup-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; This post is about form best practices.  It does not explain how to implement sign-up via a third-party identity provider (federated login) or show how to build backend services to authenticate users, store credentials, and manage accounts.  &lt;a href=&quot;https://developers.google.com/identity/sign-in/web/sign-in&quot;&gt;Integrating Google Sign-In into your web app&lt;/a&gt; explains how to add federated login to your sign-up options.  &lt;a href=&quot;https://cloud.google.com/blog/products/gcp/12-best-practices-for-user-account&quot;&gt;12 best practices for user account, authorization and password management&lt;/a&gt; outlines core back-end principles for managing user accounts. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;checklist&quot;&gt;Checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#checklist&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#no-forced-sign-in&quot;&gt;Avoid sign-in if you can&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#obvious-account-creation&quot;&gt;Make it obvious how to create an account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#obvious-account-details&quot;&gt;Make it obvious how to access account details&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#cut-clutter&quot;&gt;Cut form clutter&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#session-length&quot;&gt;Consider session length&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#help-password-managers&quot;&gt;Help password managers securely suggest and store passwords&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#no-compromised-passwords&quot;&gt;Don&#39;t allow compromised passwords&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#allow-password-pasting&quot;&gt;Do allow password pasting&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#salt-and-hash&quot;&gt;Never store or transmit passwords in plain text&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#no-forced-password-updates&quot;&gt;Don&#39;t force password updates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#password-change&quot;&gt;Make it simple to change or reset passwords&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#federated-login&quot;&gt;Enable federated login&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#account-switching&quot;&gt;Make account switching simple&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#multi-factor-authentication&quot;&gt;Consider offering multi-factor authentication&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#username&quot;&gt;Take care with usernames&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#analytics-rum&quot;&gt;Test in the field as well as the lab&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#test-platforms&quot;&gt;Test on a range of browsers, devices and platforms&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;no-forced-sign-in&quot;&gt;Avoid sign-in if you can &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#no-forced-sign-in&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you implement a sign-up form and ask users to create an account on your site, consider
whether you really need to. Wherever possible you should avoid gating features behind login.&lt;/p&gt;
&lt;p&gt;The best sign-up form is no sign-up form!&lt;/p&gt;
&lt;p&gt;By asking a user to create an account, you come between them and what they&#39;re trying to achieve.
You&#39;re asking a favor, and asking the user to trust you with personal data. Every password and item
of data you store carries privacy and security &amp;quot;data debt&amp;quot;, becoming a cost and liability for
your site.&lt;/p&gt;
&lt;p&gt;If the main reason you ask users to create an account is to save information between navigations or
browsing sessions, &lt;a href=&quot;https://web.dev/storage-for-the-web&quot;&gt;consider using client-side storage&lt;/a&gt; instead. For shopping
sites, forcing users to create an account to make a purchase is cited as a major reason for shopping
cart abandonment. You should &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#guest-checkout&quot;&gt;make guest checkout the default&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;obvious-account-creation&quot;&gt;Make sign-in obvious &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#obvious-account-creation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make it obvious how to create an account on your site, for example with a &lt;strong&gt;Login&lt;/strong&gt; or &lt;strong&gt;Sign in&lt;/strong&gt;
button at the top right of the page. Avoid using an ambiguous icon or vague wording (&amp;quot;Get on board!&amp;quot;,
&amp;quot;Join us&amp;quot;) and don&#39;t hide login in a navigational menu. The usability expert Steve Krug summed up
this approach to website usability: &lt;a href=&quot;https://uxplanet.org/dont-make-me-think-20-wise-thoughts-about-usability-from-steve-krug-876b563f1d63&quot; rel=&quot;noopener&quot;&gt;Don&#39;t make me think!&lt;/a&gt; If you need to
convince others on your web team, use &lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#analytics-rum&quot;&gt;analytics&lt;/a&gt; to show the impact of different
options.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of a mockup ecommerce website viewed on an Android phone. The one on the left uses an icon for the sign-in link that&amp;#x27;s somewhat ambiguous; the one on the right simply says &amp;#x27;Sign in&amp;#x27;&quot; decoding=&quot;async&quot; height=&quot;737&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/KeztoU8KgAqrQ5CKBSWw.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Make sign-in obvious. An icon may be ambiguous, but a &lt;b&gt;Sign
  in&lt;/b&gt; button or link is obvious.&lt;/figcaption&gt;
&lt;/figure&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 may be wondering whether to add a button (or link) to create an account and another one for existing users to sign in. Many popular sites now simply display a single &lt;strong&gt;Sign in&lt;/strong&gt; button. When the user taps or clicks on that, they also get a link to create an account if necessary. That&#39;s a common pattern now, and your users are likely to understand it, but you can use &lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#analytics-rum&quot;&gt;interaction analytics&lt;/a&gt; to monitor whether or not a single button works best. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of sign-in for Gmail: one page, showing Sign in button, when clicked leads to form that also has a Create account link.&quot; decoding=&quot;async&quot; height=&quot;545&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/WUgCNqhEgvoWEVwGjfrA.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The Gmail sign-in page has a link to create an account.&lt;br /&gt;
    At window sizes larger than shown here, Gmail displays a &lt;b&gt;Sign in&lt;/b&gt; link and a &lt;b&gt;Create an
  account&lt;/b&gt; button.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Make sure to link accounts for users who sign up via an identity provider such as Google and who
also sign up using email and password. That&#39;s easy to do if you can access a user&#39;s email address
from the profile data from the identity provider, and match the two accounts. The code below shows
how to access email data for a Google Sign-in user.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// auth2 is initialized with gapi.auth2.init()&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;auth2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isSignedIn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&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;var&lt;/span&gt; profile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; auth2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentUser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&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;getBasicProfile&lt;/span&gt;&lt;span class=&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 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;Email: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;profile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;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 id=&quot;obvious-account-details&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once a user has signed in, make it clear how to access account details. In particular, make it
obvious how to &lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#password-change&quot;&gt;change or reset passwords&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;cut-clutter&quot;&gt;Cut form clutter &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#cut-clutter&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the sign-up flow, your job is to minimize complexity and keep the user focused. Cut the clutter.
This is not the time for distractions and temptations!&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://samdutton.com/avoid-distractions.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;Don&#39;t distract users from completing sign-up.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;On sign-up, ask for as little as possible. Collect additional user data (such as name and address)
only when you need to, and when the user sees a clear benefit from providing that data. Bear in mind
that every item of data you communicate and store incurs cost and liability.&lt;/p&gt;
&lt;p&gt;Don&#39;t double up your inputs just to make sure users get their contact details right. That slows down
form completion and doesn&#39;t make sense if form fields are autofilled. Instead, send a confirmation
code to the user once they&#39;ve entered their contact details, then continue with account creation
once they respond. This is a common sign-up pattern: users are used to it.&lt;/p&gt;
&lt;p&gt;You may want to consider password-free sign-in by sending users a code every time they sign in on
a new device or browser. Sites such as Slack and Medium use a version of this.&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://samdutton.com/medium-sign-in.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;Password-free sign-in on medium.com.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As with federated login, this has the added benefit that you don&#39;t have to manage user passwords.&lt;/p&gt;
&lt;h2 id=&quot;session-length&quot;&gt;Consider session length &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#session-length&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whatever approach you take to user identity, you&#39;ll need to make a careful decision about session
length: how long the user remains logged in, and what might cause you to log them out.&lt;/p&gt;
&lt;p&gt;Consider whether your users are on mobile or desktop, and whether
they are sharing on desktop, or sharing devices.&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 get around some of the issues of shared devices by enforcing re-authentication for sensitive features, for example when a purchase is made or an account updated. You can find out more about ways to implement re-authentication from the codelab &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webauthn-reauth/#0&quot;&gt;Your First WebAuthn App&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;help-password-managers&quot;&gt;Help password managers securely suggest and store passwords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#help-password-managers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can help third party and built-in browser password managers suggest and store passwords, so that
users don&#39;t need to choose, remember or type passwords themselves. Password managers work well in
modern browsers, synchronizing accounts across devices, across platform-specific and web apps—and
for new devices.&lt;/p&gt;
&lt;p&gt;This makes it extremely important to code sign-up forms correctly, in particular to use the correct
autocomplete values. For sign-up forms use &lt;code&gt;autocomplete=&amp;quot;new-password&amp;quot;&lt;/code&gt; for new passwords, and add
correct autocomplete values to other form fields wherever possible, such as &lt;code&gt;autocomplete=&amp;quot;email&amp;quot;&lt;/code&gt;
and &lt;code&gt;autocomplete=&amp;quot;tel&amp;quot;&lt;/code&gt;. You can also help password managers by using different &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt;
values in sign-up and sign-in forms, for the &lt;code&gt;form&lt;/code&gt; element itself, as well as any &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;
and &lt;code&gt;textarea&lt;/code&gt; elements.&lt;/p&gt;
&lt;p&gt;You should also use the appropriate &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/input/email&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;type&lt;/code&gt; attribute&lt;/a&gt;
to provide the right keyboard on mobile and enable basic built-in validation by the browser.
You can find out more from &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#type&quot;&gt;Payment and address form best practices&lt;/a&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; &lt;a href=&quot;https://web.dev/sign-in-form-best-practices&quot;&gt;Sign-in form best practices&lt;/a&gt; has lots more tips on how to improve form design, layout and accessibility, and how to code forms correctly in order to take advantage of built-in browser features. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;secure-passwords&quot;&gt;Ensure users enter secure passwords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#secure-passwords&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enabling password managers to suggest passwords is the best option, and you should encourage
users to accept the strong passwords suggested by browsers and third-party browser managers.&lt;/p&gt;
&lt;p&gt;However, many users want to enter their own passwords, so you need to implement rules for password
strength. The US National Institute of Standards and Technology explains
&lt;a href=&quot;https://pages.nist.gov/800-63-3/sp800-63b.html#5-authenticator-and-verifier-requirements&quot; rel=&quot;noopener&quot;&gt;how to avoid insecure passwords&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; Sign-up forms on some sites have password validation rules that don&#39;t allow the strong passwords generated by browser and third-party password managers. Make sure your site doesn&#39;t do this, since it interrupts form completion, annoys users, and requires users to make up their own passwords, which may be less secure than those generated by password managers. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;no-compromised-passwords&quot;&gt;Don&#39;t allow compromised passwords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#no-compromised-passwords&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whatever rules you choose for passwords, you should never allow passwords that have been &lt;a href=&quot;https://haveibeenpwned.com/PwnedWebsites&quot; rel=&quot;noopener&quot;&gt;exposed in
security breaches&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once a user has entered a password, you need to check that it&#39;s not a password that&#39;s already been
compromised. The site &lt;a href=&quot;https://haveibeenpwned.com/Passwords&quot; rel=&quot;noopener&quot;&gt;Have I Been Pwned&lt;/a&gt; provides an API for password
checking, or you can run this as a service yourself.&lt;/p&gt;
&lt;p&gt;Google&#39;s Password Manager also allows you to &lt;a href=&quot;https://passwords.google.com/checkup&quot; rel=&quot;noopener&quot;&gt;check if any of your existing passwords have been
compromised&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you do reject the password that a user proposes, tell them specifically why it was rejected.
&lt;a href=&quot;https://baymard.com/blog/inline-form-validation&quot; rel=&quot;noopener&quot;&gt;Show problems inline and explain how to fix them&lt;/a&gt;,
as soon as the user has entered a value—not after they&#39;ve submitted the sign-up form and had to
wait for a response from your server.&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://samdutton.com/password-validation.mp4&quot; type=&quot;video/mp4&quot; /&gt;
   &lt;/video&gt;
  &lt;figcaption&gt;Be clear why a password is rejected.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;allow-password-pasting&quot;&gt;Don&#39;t prohibit password pasting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#allow-password-pasting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some sites don&#39;t allow text to be pasted into password inputs.&lt;/p&gt;
&lt;p&gt;Disallowing password pasting annoys users, encourages passwords that are memorable (and therefore
may be easier to compromise) and, according to organizations such as the UK National
Cyber Security Centre, may actually &lt;a href=&quot;https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords&quot; rel=&quot;noopener&quot;&gt;reduce security&lt;/a&gt;.
Users only become aware that pasting is disallowed &lt;em&gt;after&lt;/em&gt; they try to paste their password, so
&lt;a href=&quot;https://github.com/OWASP/owasp-masvs/issues/106&quot; rel=&quot;noopener&quot;&gt;disallowing password pasting doesn&#39;t avoid clipboard vulnerabilities&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;salt-and-hash&quot;&gt;Never store or transmit passwords in plain text &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#salt-and-hash&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make sure to &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#Use_a_cryptographically_strong_credential-specific_salt&quot; rel=&quot;noopener&quot;&gt;salt and hash&lt;/a&gt; passwords—and &lt;a href=&quot;https://www.schneier.com/blog/archives/2011/04/schneiers_law.html&quot; rel=&quot;noopener&quot;&gt;don&#39;t try to invent your own hashing algorithm&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;no-forced-password-updates&quot;&gt;Don&#39;t force password updates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#no-forced-password-updates&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pages.nist.gov/800-63-3/sp800-63b.html#-5112-memorized-secret-verifiers:~:text=Verifiers%20SHOULD%20NOT%20require%20memorized%20secrets%20to%20be%20changed%20arbitrarily%20(e.g.%2C%20periodically).&quot; rel=&quot;noopener&quot;&gt;Don&#39;t force users to update their passwords arbitrarily.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Forcing password updates can be costly for IT departments, annoying to users, and &lt;a href=&quot;https://pages.nist.gov/800-63-FAQ/#q-b05&quot; rel=&quot;noopener&quot;&gt;doesn&#39;t have much
impact on security&lt;/a&gt;. It&#39;s also likely to encourage people
to use insecure memorable passwords, or to keep a physical record of passwords.&lt;/p&gt;
&lt;p&gt;Rather than force password updates, you should monitor for unusual account activity and warn users.
If possible you should also monitor for passwords that become compromised because of data breaches.&lt;/p&gt;
&lt;p&gt;You should also provide your users with access to their account login history, showing them where
and when a login happened.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Gmail account activity page&quot; decoding=&quot;async&quot; height=&quot;469&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/zZXmhWc9bZ1GtvrE5Ooq.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;a href=&quot;https://support.google.com/mail/answer/45938?hl=en-GB&quot; title=&quot;Find out how to view Gmail account activity.&quot;&gt;Gmail account activity page&lt;/a&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;password-change&quot;&gt;Make it simple to change or reset passwords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#password-change&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make it obvious to users where and how to &lt;strong&gt;update&lt;/strong&gt; their account password. On some sites, it&#39;s
surprisingly difficult.&lt;/p&gt;
&lt;p&gt;You should, of course, also make it simple for users to &lt;strong&gt;reset&lt;/strong&gt; their password if they forget it.
The Open Web Application Security Project provides detailed guidance on &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html&quot; rel=&quot;noopener&quot;&gt;how to handle lost
passwords&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To keep your business and your users safe, it&#39;s especially important to help users change their
password if they discover that it&#39;s been compromised. To make this easier, you should add a
&lt;a href=&quot;https://w3c.github.io/webappsec-change-password-url/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;/.well-known/change-password&lt;/code&gt;&lt;/a&gt; URL to your
site that redirects to your password management page. This enables password managers to navigate
your users directly to the page where they can change their password for your site. This feature is
now implemented in Safari, Chrome, and is coming to other browsers. &lt;a href=&quot;https://web.dev/change-password-url&quot;&gt;Help users change passwords
easily by adding a well-known URL for changing passwords&lt;/a&gt; explains how to
implement this.&lt;/p&gt;
&lt;p&gt;You should also make it simple for users to delete their account if that&#39;s what they want.&lt;/p&gt;
&lt;h2 id=&quot;federated-login&quot;&gt;Offer login via third-party identity providers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#federated-login&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many users prefer to log in to websites using an email address and password sign-up form.
However, you should also enable users to log in via a third party identity provider, also known as
federated login.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;WordPress login page&quot; decoding=&quot;async&quot; height=&quot;513&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/jubgwX1shLB7qAIiioTU.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;WordPress login page, with Google and Apple login options.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This approach has several advantages. For users who create an account using federated login, you
don&#39;t need to ask for, communicate, or store passwords.&lt;/p&gt;
&lt;p&gt;You may also be able to access additional verified profile information from federated login, such
as an email address—which means the user doesn&#39;t have to enter that data and you don&#39;t need to do
the verification yourself. Federated login can also make it much easier for users when they get a
new device.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/identity/sign-in/web/sign-in&quot; rel=&quot;noopener&quot;&gt;Integrating Google Sign-In into your web app&lt;/a&gt;
explains how to add federated login to your sign-up options. &lt;a href=&quot;https://en.wikipedia.org/wiki/Federated_identity#Examples&quot; rel=&quot;noopener&quot;&gt;Many other&lt;/a&gt; identity platforms are available.&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; &amp;quot;First day experience&amp;quot; when you get a new device is increasingly important. Users expect to log in from multiple devices including their phone, laptop, desktop, tablet, TV, or from a car. If your sign-up and sign-in forms aren&#39;t seamless, this is a moment where you risk losing users, or at least losing contact with them until they get set up again. You need to make it as quick and easy as possible for users on new devices to get up and running on your site. This is another area where federated login can help. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;account-switching&quot;&gt;Make account switching simple &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#account-switching&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many users share devices and swap between accounts using the same browser. Whether users access
federated login or not, you should make account switching simple.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Gmail, showing account switching&quot; decoding=&quot;async&quot; height=&quot;494&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sPDZJIY5Vo2ijqyuofCy.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Account switching on Gmail.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;multi-factor-authentication&quot;&gt;Consider offering multi-factor authentication &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#multi-factor-authentication&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Multi-factor authentication means ensuring that users provide authentication in more than one way.
For example, as well as requiring the user to set a password, you might also enforce verification
using a one-time-passcode sent by email or an SMS text message, or by using an app-based one-time
code, security key or fingerprint sensor. &lt;a href=&quot;https://web.dev/sms-otp-form&quot;&gt;SMS OTP best practices&lt;/a&gt; and
&lt;a href=&quot;https://developer.chrome.com/blog/webauthn/&quot; rel=&quot;noopener&quot;&gt;Enabling Strong Authentication with WebAuthn&lt;/a&gt;
explain how to implement multi-factor authentication.&lt;/p&gt;
&lt;p&gt;You should certainly offer (or enforce) multi-factor authentication if your site handles personal or
sensitive information.&lt;/p&gt;
&lt;h2 id=&quot;username&quot;&gt;Take care with usernames &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#username&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Don&#39;t insist on a username unless (or until) you need one. Enable users to sign up and sign in with
only an email address (or telephone number) and password—or &lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#federated-login&quot;&gt;federated login&lt;/a&gt;
if they prefer. Don&#39;t force them to choose and remember a username.&lt;/p&gt;
&lt;p&gt;If your site does require usernames, don&#39;t impose unreasonable rules on them, and don&#39;t stop users
from updating their username. On your backend you should generate a unique ID for every user account,
not an identifier based on personal data such as username.&lt;/p&gt;
&lt;p&gt;Also make sure to use &lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt; for usernames.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; As with personal names, ensure that usernames aren&#39;t restricted to characters from the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes#Types:~:text=Latin%20alphabet&quot;&gt;Latin alphabet&lt;/a&gt;. &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#unicode-matching&quot;&gt;Payment and address form best practices&lt;/a&gt; explains how and why to validate using Unicode letter matching. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;test-platforms&quot;&gt;Test on a range of devices, platforms, browsers and versions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#test-platforms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Test sign-up forms on the platforms most common for your users. Form element functionality may vary,
and differences in viewport size can cause layout problems. BrowserStack enables &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;free testing for
open source projects&lt;/a&gt; on a range of devices and browsers.&lt;/p&gt;
&lt;h2 id=&quot;analytics-rum&quot;&gt;Implement analytics and Real User Monitoring &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#analytics-rum&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You need &lt;a href=&quot;https://web.dev/how-to-measure-speed/#lab-data-vs-field-data&quot;&gt;field data as well as lab data&lt;/a&gt;
to understand how users experience your sign-up forms. Analytics and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Performance/Rum-vs-Synthetic#Real_User_Monitoring&quot; rel=&quot;noopener&quot;&gt;Real User Monitoring&lt;/a&gt;
(RUM) provide data for the actual experience of your users, such as how long sign-up pages take to
load, which UI components users interact with (or not) and how long it takes users to complete
sign-up.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Page analytics&lt;/strong&gt;: &lt;a href=&quot;https://analytics.google.com/analytics/academy/course/6&quot; rel=&quot;noopener&quot;&gt;page views, bounce rates and exits&lt;/a&gt; for every page in your sign-up flow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interaction analytics&lt;/strong&gt;: &lt;a href=&quot;https://support.google.com/analytics/answer/6180923?hl=en&quot; rel=&quot;noopener&quot;&gt;goal funnels&lt;/a&gt;
and &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/gtagjs/events&quot; rel=&quot;noopener&quot;&gt;events&lt;/a&gt;
indicate where users abandon the sign-up flow and what proportion of users click buttons,
links, and other components of your sign-up pages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Website performance&lt;/strong&gt;: &lt;a href=&quot;https://web.dev/user-centric-performance-metrics&quot;&gt;user-centric metrics&lt;/a&gt;
can tell you if your sign-up flow is slow to load or &lt;a href=&quot;https://web.dev/cls&quot;&gt;visually unstable&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Small changes can make a big difference to completion rates for sign-up forms. Analytics and RUM
enable you to optimize and prioritize changes, and monitor your site for problems that aren&#39;t
exposed by local testing.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Keep learning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-up-form-best-practices/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices&quot;&gt;Sign-in form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices&quot;&gt;Payment and address form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/forms/&quot;&gt;Create Amazing Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2018/08/best-practices-for-mobile-form-design/&quot; rel=&quot;noopener&quot;&gt;Best Practices For Mobile Form Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/more-capable-form-controls&quot;&gt;More capable form controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webaim.org/techniques/forms/&quot; rel=&quot;noopener&quot;&gt;Creating Accessible Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/credential-management-api/&quot; rel=&quot;noopener&quot;&gt;Streamlining the Sign-up Flow Using Credential Management API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web-otp&quot;&gt;Verify phone numbers on the web with the WebOTP API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@ecowarriorprincess&quot; rel=&quot;noopener&quot;&gt;@ecowarriorprincess&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/lUShu7PHIGA&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Address form best practices codelab</title>
    <link href="https://web.dev/codelab-address-form-best-practices/"/>
    <updated>2020-12-09T00:00:00Z</updated>
    <id>https://web.dev/codelab-address-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;How can you design a form that works well for a variety of names and address formats? Minor form
glitches irritate users and can cause them to leave your site or give up on completing a purchase
or sign-up.&lt;/p&gt;
&lt;p&gt;This codelab shows you how to build an easy-to-use, accessible form that works well for most users.&lt;/p&gt;
&lt;h2 id=&quot;step-1-make-the-most-of-html-elements-and-attributes&quot;&gt;Step 1: Make the most of HTML elements and attributes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#step-1-make-the-most-of-html-elements-and-attributes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll start this part of the codelab with an empty form, just a heading and a button all on
their own. Then you&#39;ll begin adding inputs. (CSS and a little bit of JavaScript are already
included.)&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 300px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/address-form-codelab-0?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=css%2Fmain.css&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;address-form-codelab-0 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Add a name field to your &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element with the following code:&lt;/li&gt;
&lt;/ul&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;section&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;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;name&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;&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;section&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;That may look complicated and repetitive for just one name field, but it already does a lot.&lt;/p&gt;
&lt;p&gt;You associated the &lt;code&gt;label&lt;/code&gt; with the &lt;code&gt;input&lt;/code&gt; by matching the &lt;code&gt;label&lt;/code&gt;&#39;s &lt;code&gt;for&lt;/code&gt; attribute with the
&lt;code&gt;input&lt;/code&gt;&#39;s &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt;. A tap or click on a label moves focus to its input, making a much bigger
target than the input on its own—which is good for fingers, thumbs and mouse clicks! Screenreaders
announce label text when the label or the label&#39;s input gets focus.&lt;/p&gt;
&lt;p&gt;What about &lt;code&gt;name=&amp;quot;name&amp;quot;&lt;/code&gt;? This is the name (which happens to be &#39;name&#39;!) associated with the data
from this input which is sent to the server when the form is submitted. Including a &lt;code&gt;name&lt;/code&gt; attribute
also means that the data from this element can be accessed by the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FormData/Using_FormData_Objects&quot; rel=&quot;noopener&quot;&gt;FormData API&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;step-2-add-attributes-to-help-users-enter-data&quot;&gt;Step 2: Add attributes to help users enter data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#step-2-add-attributes-to-help-users-enter-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What happens when you tap or click in the &lt;strong&gt;Name&lt;/strong&gt; input in Chrome? You should see autofill
suggestions that the browser has stored and guesses are appropriate for this input, given its
&lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;Now add &lt;code&gt;autocomplete=&amp;quot;name&amp;quot;&lt;/code&gt; to your input code so it looks like this:&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;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;name&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;autocomplete&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;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Reload the page in Chrome and tap or click in the &lt;strong&gt;Name&lt;/strong&gt; input. What differences do you see?&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; Ever wondered how to delete autofill suggestions in Chrome? * Windows: &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;delete&lt;/code&gt; * Mac: &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;fn&lt;/code&gt; + &lt;code&gt;delete&lt;/code&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You should notice a subtle change: with &lt;code&gt;autocomplete=&amp;quot;name&amp;quot;&lt;/code&gt;, the suggestions are now specific
values that were used previously in form inputs that also had &lt;code&gt;autocomplete=&amp;quot;name&amp;quot;&lt;/code&gt;. The browser
isn&#39;t just guessing what might be appropriate: you have control. You&#39;ll also see the &lt;strong&gt;Manage…&lt;/strong&gt;
option to view and edit the names and addresses stored by your browser.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of Chrome on an Android phone showing a form with a single input, with and without an autocomplete value. One shows browser UI heuristicically suggestions values; the other shows UI when there are stored autocomplete values.&quot; decoding=&quot;async&quot; height=&quot;684&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uSc6aqRgHoL2qIDyj803.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;UI for autofill with guessed values, versus autocomplete.&lt;/figcaption&gt;
&lt;/figure&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 an &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#autocomplete-attribute&quot;&gt;appropriate autocomplete value&lt;/a&gt; is available for an &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt; or &lt;code&gt;textarea&lt;/code&gt;, you should use it! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now add &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5/Constraint_validation&quot; rel=&quot;noopener&quot;&gt;constraint validation attributes&lt;/a&gt;
&lt;code&gt;maxlength&lt;/code&gt;, &lt;code&gt;pattern&lt;/code&gt; and &lt;code&gt;required&lt;/code&gt; so your input code looks like this:&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;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;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;/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;name&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;autocomplete&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;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;100&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;pattern&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;[\p{L} \-\.]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&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;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;maxlength=&amp;quot;100&amp;quot;&lt;/code&gt; means the browser won&#39;t allow any input longer than 100 characters.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pattern=&amp;quot;[\p{L} \-\.]+&amp;quot;&lt;/code&gt; uses a regular expression that allows &lt;a href=&quot;https://javascript.info/regexp-unicode&quot; rel=&quot;noopener&quot;&gt;Unicode letter characters&lt;/a&gt;,
hyphens and periods (full stops). That means names such as Françoise or Jörg aren&#39;t classed as
&#39;invalid&#39;. That isn&#39;t the case if you use the value &lt;code&gt;\w&lt;/code&gt; which [only allows characters from the
&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices/#unicode-matching&quot;&gt;Latin alphabet&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;required&lt;/code&gt; means… required! The browser will not allow the form to be submitted without data for
this field, and will warn and highlight the input if you attempt to submit it. No extra code
required!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; This codelab doesn&#39;t address (😜) localization or internationalization. Depending on where your users are located, you need to consider address &lt;em&gt;formats&lt;/em&gt; as well as the different &lt;em&gt;names&lt;/em&gt; used for address components, even within the same language: ZIP, postal code, Eircode or PIN? It may be necessary for your site to &lt;a href=&quot;https://www.smashingmagazine.com/2020/11/internationalization-localization-static-sites#determining-user-s-language-and-region&quot;&gt;customize for multiple locales&lt;/a&gt;, but the address form in this codelab is designed for flexibility, and should work &amp;quot;well enough&amp;quot; for a range of addresses. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To test how the form works with and without these attributes, try entering data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try entering values that don&#39;t fit the &lt;code&gt;pattern&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Try submitting the form with an empty input. You&#39;ll see built-in browser functionality warning of
the empty required field and setting focus on it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-3-add-a-flexible-address-field-to-your-form&quot;&gt;Step 3: Add a flexible address field to your form &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#step-3-add-a-flexible-address-field-to-your-form&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add an address field, add the following code to your form:&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;section&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;address&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;Address&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;textarea&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;address&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;address&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;address&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&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;textarea&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;section&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;A &lt;code&gt;textarea&lt;/code&gt; is the most flexible way for your users to enter their address, and it&#39;s great for
cutting and pasting.&lt;/p&gt;
&lt;p&gt;You should avoid splitting your address form into components such as street name and number unless
you really need to. Don&#39;t force users to try to fit their address into fields that don&#39;t make sense.&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; Consider the way that address data from your form is being used. What&#39;s it for? Make sure to understand data requirements and who sets them. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now add fields for &lt;strong&gt;ZIP or postal code&lt;/strong&gt;, and &lt;strong&gt;Country or region&lt;/strong&gt;. For simplicity,
only the first five countries are included here. A full list is included in the &lt;a href=&quot;https://address-form.glitch.me/&quot; rel=&quot;noopener&quot;&gt;completed address form&lt;/a&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&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;postal-code&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;ZIP or postal code (optional)&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;postal-code&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;postal-code&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;postal-code&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;20&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;section&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;section&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;country-region&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;&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;Country or region&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;select&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;country&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;country&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;country&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;required&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;selected&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;SPACER&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;option&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;AF&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;Afghanistan&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;option&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;AX&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;Åland Islands&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;option&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;AL&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;Albania&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;option&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;DZ&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;Algeria&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;option&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;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;AS&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;American Samoa&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;option&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;select&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;section&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;You&#39;ll see that &lt;strong&gt;Postal code&lt;/strong&gt; is optional: that&#39;s because &lt;a href=&quot;https://hellowahab.wordpress.com/2011/05/24/list-of-countries-without-postal-codes/&quot; rel=&quot;noopener&quot;&gt;many countries don&#39;t use postal codes&lt;/a&gt;.
(&lt;a href=&quot;https://www.grcdi.nl/gsb/global%20sourcebook.html&quot; rel=&quot;noopener&quot;&gt;Global Sourcebook&lt;/a&gt; provides information about
address formats for 194 different countries, including sample addresses.) The label &lt;strong&gt;Country or
region&lt;/strong&gt;  is used instead of &lt;strong&gt;Country&lt;/strong&gt;, because some options from the full list (such as the
United Kingdom) are not single countries (despite the &lt;code&gt;autocomplete&lt;/code&gt; value).&lt;/p&gt;
&lt;h2 id=&quot;step-4-enable-customers-to-easily-enter-shipping-and-billing-addresses&quot;&gt;Step 4: Enable customers to easily enter shipping and billing addresses &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#step-4-enable-customers-to-easily-enter-shipping-and-billing-addresses&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ve built a highly functional address form, but what if your site requires more than one
address, for shipping and billing, for example? Try updating your form to enable customers to enter
shipping and billing addresses. How can you make data entry as quick and easy as possible,
especially if the two addresses are the same? The article that goes with this codelab explains
&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#billing-address&quot;&gt;techniques for handling multiple addresses&lt;/a&gt;.
Whatever you do, make sure to use the correct &lt;code&gt;autocomplete&lt;/code&gt; values!&lt;/p&gt;
&lt;h2 id=&quot;step-5-add-a-telephone-number-field&quot;&gt;Step 5: Add a telephone number field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#step-5-add-a-telephone-number-field&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add a telephone number input, add the following code to the form:&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;section&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;tel&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;Telephone&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;tel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;tel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;tel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;30&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;pattern&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;[\d \-\+]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;enterkeyhint&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;done&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;required&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;section&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;For phone numbers use a single input: don&#39;t split the number into parts. That makes it easier for
users to enter data or copy and paste, makes validation simpler, and enables browsers to autofill.&lt;/p&gt;
&lt;p&gt;There are two attributes that can improve the user experience of entering a telephone number:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; ensures mobile users get the right keyboard.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enterkeyhint=&amp;quot;done&amp;quot;&lt;/code&gt; sets the mobile keyboard enter key label to show that this is the last
field and the form can now be submitted (the default is &lt;code&gt;next&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of a form on Android showing how the enterkeyhint input attribute changes the enter key button icon.&quot; decoding=&quot;async&quot; height=&quot;684&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vReqMRQjLSI7e6UQ5WwX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Use the enterkeyhint attribute to set the Enter button label:
    &#39;next&#39; and &#39;done&#39;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Using &lt;code&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt; adds an up/down arrow to increment numbers, which makes no sense for data such as telephone, payment card, or account numbers. Instead, you should use &lt;code&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; for telephone numbers. For other numbers, use &lt;code&gt;type=&amp;quot;text&amp;quot;&lt;/code&gt; (or leave off the attribute, since &lt;code&gt;text&lt;/code&gt; is the default) and add &lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt; to get a numeric keyboard on mobile. &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Can you see any problems with using a single input for telephone number? Do you store phone number parts (country and area code) separately? If so, why? &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Your complete address form should now look like this:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 970px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/address-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;address-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Try out your form on different devices. What devices and browsers are you targeting? How could
the form be improved?&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;different-devices&quot;&gt;There are several ways to test your form on different devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/&quot; rel=&quot;noopener&quot;&gt;Use Chrome DevTools Device Mode&lt;/a&gt;
to simulate mobile devices.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/9430554&quot; rel=&quot;noopener&quot;&gt;Send the URL from your computer to your phone&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a service such as &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;BrowserStack&lt;/a&gt; to test on a range
of devices and browsers.&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; Before you start building forms, make sure to understand what data is required and if that data is strictly necessary. Don&#39;t ask for data you don&#39;t need! The simplest way to reduce form complexity and improve privacy is to remove unnecessary fields. Storing less data also reduces back-end data cost and liability. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going further &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-address-form-best-practices/#going-further&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#analytics-rum&quot;&gt;Analytics and Real User Monitoring&lt;/a&gt;:
enable the performance and usability of your form design to be tested and monitored for real users,
and to check if changes are successful. You should monitor load performance and other &lt;a href=&quot;https://web.dev/vitals&quot;&gt;Web Vitals&lt;/a&gt;,
as well as page analytics (what proportion of users bounce from your address form without completing
it? how long do users spend on your address form pages?) and interaction analytics (which page
components do users interact with, or not?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Where are your users located? How do they format their address? What names do they use for address
components, such as ZIP or postal code? &lt;a href=&quot;http://www.columbia.edu/~fdc/postal/&quot; rel=&quot;noopener&quot;&gt;Frank&#39;s Compulsive Guide to Postal Addresses&lt;/a&gt;
provides useful links and extensive guidance detailing address formats in over 200 countries.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Country selectors are notorious for &lt;a href=&quot;https://www.smashingmagazine.com/2011/11/redesigning-the-country-selector/&quot; rel=&quot;noopener&quot;&gt;poor usability&lt;/a&gt;.
It&#39;s &lt;a href=&quot;https://baymard.com/blog/drop-down-usability#in-general-avoid-drop-downs-when-there-are-more-than-10-or-fewer-than-5-options&quot; rel=&quot;noopener&quot;&gt;best to avoid select elements for a long list of items&lt;/a&gt;
and the ISO 3166 country-code standard &lt;a href=&quot;https://www.iso.org/obp/ui/#search/code/&quot; rel=&quot;noopener&quot;&gt;currently lists 249 countries&lt;/a&gt;!
Instead of a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; you may want to consider an alternative such as the
&lt;a href=&quot;https://baymard.com/labs/country-selector&quot; rel=&quot;noopener&quot;&gt;Baymard Institute country selector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Can you design a better selector for lists with a lot of items? How would you ensure your design is
accessible across a range of devices and platforms? (The &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element doesn&#39;t work well for a
large number of items, but at least it&#39;s usable on virtually all browsers and assistive devices!)&lt;/p&gt;
&lt;p&gt;The blog post
&lt;a href=&quot;https://shkspr.mobi/blog/2017/11/input-type-country/&quot; rel=&quot;noopener&quot;&gt;&amp;lt;input type=&amp;quot;country&amp;quot; /&amp;gt;&lt;/a&gt; discusses
the complexity of standardizing a country selector. Localization of country names can also be
problematic. &lt;a href=&quot;http://www.countries-list.info/Download-List&quot; rel=&quot;noopener&quot;&gt;Countries Lists&lt;/a&gt; has a tool for
downloading country codes and names in multiple languages, in multiple formats.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Payment form best practices codelab</title>
    <link href="https://web.dev/codelab-payment-form-best-practices/"/>
    <updated>2020-12-09T00:00:00Z</updated>
    <id>https://web.dev/codelab-payment-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This codelab shows you how to build a payment form that is secure, accessible and easy to use.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; This article is about frontend best practices for payment forms. It does not explain how to implement transactions on your site. To find out more about adding payment functionality to your website, see &lt;a href=&quot;https://web.dev/payments&quot;&gt;Web Payments&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;step-1-use-html-as-intended&quot;&gt;Step 1: Use HTML as intended &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-payment-form-best-practices/#step-1-use-html-as-intended&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use elements built for the job:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you&#39;ll see, these elements enable built-in browser functionality, improve accessibility, and
add meaning to your markup.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take a look at the HTML for your form in &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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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;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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Payment form&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;h1&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Card number&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 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;section&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Name on card&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 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;section&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;section&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;cc-exp-csc&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;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Expiry date&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Security code&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;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;explanation&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;Last 3 digits on back of card&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&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;button&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;complete-payment&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;Complete payment&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;button&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;form&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;There are &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements for card number, name on card, expiry date and security code. They&#39;re all
wrapped in &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; elements, and each has a label. The &lt;strong&gt;Complete Payment&lt;/strong&gt; button is an HTML
&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. Later in this codelab you&#39;ll learn about the browser features you can access by using
these elements.&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;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements don&#39;t have closing tags. That&#39;s because they are &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#syntax-elements&quot;&gt;void&lt;/a&gt; (empty) elements: they don&#39;t have any content in themselves. A &amp;quot;/&amp;quot; character at the end of a void element is optional: either &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; is OK. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; to preview your payment form.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the form work well enough as it is?&lt;/li&gt;
&lt;li&gt;Is there anything you would change to make it work better?&lt;/li&gt;
&lt;li&gt;How about on mobile?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click &lt;strong&gt;View Source&lt;/strong&gt; to return to your source code.&lt;/p&gt;
&lt;h2 id=&quot;step-2-design-for-mobile-and-desktop&quot;&gt;Step 2: Design for mobile and desktop &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-payment-form-best-practices/#step-2-design-for-mobile-and-desktop&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The HTML you added is valid, but the default browser styling makes the form hard to use, especially
on mobile. It doesn&#39;t look too good, either.&lt;/p&gt;
&lt;p&gt;You need to ensure your forms work well on a range of devices by adjusting padding, margins, and
font sizes.&lt;/p&gt;
&lt;p&gt;Copy all the CSS below and paste it into your own &lt;code&gt;css/main.css&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 400px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-form-codelab-2?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=css%2Fmain.css&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-form-codelab-2 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;That&#39;s a lot of CSS! The main things to be aware of are the changes to sizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;margin&lt;/code&gt; are added to inputs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-size&lt;/code&gt; and other values are different for different viewport sizes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you&#39;re ready, click &lt;strong&gt;View App&lt;/strong&gt; to see the styled form.  You&#39;ll also notice that borders have
been adjusted, and &lt;code&gt;display: block;&lt;/code&gt; is used for labels so they go on a line on their own, and
inputs can be full width. &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#label:~:text=put%20your%20labels%20above%20your%20inputs&quot;&gt;Sign-in form best practices&lt;/a&gt;
explains the benefits of this approach in more detail.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;:invalid&lt;/code&gt; selector is used to indicate when an input has an invalid value. (You&#39;ll use this
later in the codelab.)&lt;/p&gt;
&lt;p&gt;The CSS is mobile-first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The default CSS is for viewports less than &lt;code&gt;400px&lt;/code&gt; wide.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Media_queries&quot; rel=&quot;noopener&quot;&gt;Media queries&lt;/a&gt; are
used to override the default for viewports that are at least &lt;code&gt;400px&lt;/code&gt; wide, and then again for
viewports that are at least &lt;code&gt;500px&lt;/code&gt; wide. This should work well for smaller phones, mobile devices
with larger screens, and on desktop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whenever you build for the web, you need to test on different devices and viewport sizes. That&#39;s
especially true for forms, because one small glitch can make them unusable. You should always adjust
&lt;a href=&quot;https://web.dev/responsive-web-design-basics/#breakpoints&quot;&gt;CSS breakpoints&lt;/a&gt; to ensure they work well with your
content and your target devices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the whole form visible?&lt;/li&gt;
&lt;li&gt;Are the form inputs big enough?&lt;/li&gt;
&lt;li&gt;Is all the text readable?&lt;/li&gt;
&lt;li&gt;Did you notice any differences between using a real mobile device, and viewing the form in
Device Mode in Chrome DevTools?&lt;/li&gt;
&lt;li&gt;Did you need to adjust breakpoints?&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;different-devices&quot;&gt;There are several ways to test your form on different devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/&quot; rel=&quot;noopener&quot;&gt;Use Chrome DevTools Device Mode&lt;/a&gt;
to simulate mobile devices.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/9430554&quot; rel=&quot;noopener&quot;&gt;Send the URL from your computer to your phone&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a service such as &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;BrowserStack&lt;/a&gt; to test on a range
of devices and browsers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-3-add-attributes-to-help-users-enter-data&quot;&gt;Step 3: Add attributes to help users enter data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-payment-form-best-practices/#step-3-add-attributes-to-help-users-enter-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enable the browser to store and autofill input values, and provide access to secure built-in
payment and validation features.&lt;/p&gt;
&lt;p&gt;Add attributes to the form in your &lt;code&gt;index.html&lt;/code&gt; file so it looks like this:&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;highlight-line&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;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Payment form&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;cc-number&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;Card number&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;cc-number&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;cc-number&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;cc-number&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;inputmode&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;numeric&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;pattern&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;[\d ]{10,30}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;cc-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;Name on card&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;cc-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;name&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;cc-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;autocomplete&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;cc-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;pattern&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;[\p{L} \-\.]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&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;cc-exp-csc&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;cc-exp&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;Expiry date&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;cc-exp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;cc-exp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;cc-exp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&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;MM/YY&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;cc-csc&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;Security code&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;cc-csc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;cc-csc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;cc-csc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;inputmode&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;numeric&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;maxlength&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;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;explanation&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;Back of card, last 3 digits&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;button&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;complete-payment&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;Complete payment&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;View your app again and then tap or click in the &lt;strong&gt;Card number&lt;/strong&gt; field. Depending on the device and
platform, you may see a chooser showing payment methods stored for the browser, like the one below.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of a payment form in Chrome on an Android phone. One shows the built-in browser payment card selector; the other shows placeholder autofilled values.&quot; decoding=&quot;async&quot; height=&quot;684&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yPPoZYFMeSILBjgyjcFI.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Built-in browser payment chooser and autofill.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once you select a payment method and enter your security code, the browser autofills the form using
the payment card &lt;code&gt;autocomplete&lt;/code&gt; values you added to the form:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cc-number&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-exp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cc-csc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many browsers also check and confirm the validity of credit card numbers and security codes.&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 be alarmed! Your browser may be able to autofill the form with stored payment card data, but no payment can be made, and no data is transferred or saved. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;On a mobile device you&#39;ll also notice that you get a numeric keyboard as soon as you tap into the
&lt;strong&gt;Card number&lt;/strong&gt; field. That&#39;s because you used &lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt;. For numeric fields this makes
it easier to enter numbers and impossible to enter non-numeric characters, and nudges users to
remember the type of data they&#39;re entering.&lt;/p&gt;
&lt;p&gt;It&#39;s extremely important to correctly add all available &lt;code&gt;autocomplete&lt;/code&gt; values to payment forms. It&#39;s
quite common for sites to miss out the &lt;code&gt;autocomplete&lt;/code&gt; value for the card expiry date and other
fields. If a single &lt;code&gt;autofill&lt;/code&gt; value is wrong or missing, users will need to retrieve their actual
card to manually enter card data, and you may lose out on a sale. If autofill on payment forms
doesn&#39;t work properly, users may also decide to keep a record of payment card details on their phone
or computer, which is highly insecure.&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; Research shows that &lt;a href=&quot;https://baymard.com/blog/how-to-format-expiration-date-fields&quot;&gt;it may be better to use separate select elements for month and year&lt;/a&gt; rather than a single input. It&#39;s up to you which you think is best. Test this out by remixing and editing the HTML from our complete &lt;a href=&quot;https://glitch.com/~payment-form&quot;&gt;payment form demo&lt;/a&gt;, which includes code for both types of expiry date field. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Try submitting the payment form with an empty field. The browser prompts to complete missing
data. Now add a letter to the value in the &lt;strong&gt;Card number&lt;/strong&gt; field and try submitting the form. The
browser warns that value is invalid. This happens because you used the &lt;code&gt;pattern&lt;/code&gt; attribute to
specify valid values for a field. The same works for &lt;code&gt;maxlength&lt;/code&gt; and other
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5/Constraint_validation&quot; rel=&quot;noopener&quot;&gt;validation constraints&lt;/a&gt;
No JavaScript required.&lt;/p&gt;
&lt;p&gt;Your payment form should now look this:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 750px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-form-codelab-3?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-form-codelab-3 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Try removing &lt;code&gt;autocomplete&lt;/code&gt; values and filling in the payment form. What difficulties do you
encounter?&lt;/li&gt;
&lt;li&gt;Try out payment forms on online stores. Consider what works well and what goes wrong. Are there
any common problems or best practices you should follow?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-4-disable-the-payment-button-once-the-form-is-submitted&quot;&gt;Step 4: Disable the payment button once the form is submitted &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-payment-form-best-practices/#step-4-disable-the-payment-button-once-the-form-is-submitted&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You should consider disabling a submit button once the user has tapped or clicked it—especially when
the user is making payment. &lt;a href=&quot;https://baymard.com/blog/users-double-click-online&quot; rel=&quot;noopener&quot;&gt;Many users tap or click buttons repeatedly&lt;/a&gt;,
even if they&#39;re working fine. That can cause problems with payment processing and add to server load.&lt;/p&gt;
&lt;p&gt;Add the following JavaScript to your &lt;code&gt;js/main.js&lt;/code&gt; file:&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; form &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;form&#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; completePaymentButton &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;button#complete-payment&#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;form&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;submit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleFormSubmission&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleFormSubmission&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;  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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkValidity&lt;/span&gt;&lt;span class=&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 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 comment&quot;&gt;// Handle invalid form data.&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;    completePaymentButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Making payment...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    completePaymentButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#39;&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;setTimeout&lt;/span&gt;&lt;span class=&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Made payment!&#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 number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &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;Try submitting the payment form and see what happens.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Some sites leave form submit buttons disabled until the user has correctly completed all form fields. It&#39;s best not to do that, since users may accidentally leave out a required value, or use an invalid value, then tap or click the disabled submit button and assume your site is broken! Even if you mark form values as invalid or missing, the user may not see the warnings (especially for longer forms, and on mobile). Better to validate inline while the user is entering data–&lt;em&gt;and&lt;/em&gt; when they try to submit the form. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Here is how your code should look at this point, with the addition of some comments and a
&lt;code&gt;validate()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 500px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-form-codelab-4?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=js%2Fmain.js&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-form-codelab-4 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You&#39;ll notice that the JavaScript includes commented-out code for data validation. This code uses
the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/forms.html#constraints&quot; rel=&quot;noopener&quot;&gt;Constraint Validation API&lt;/a&gt;
(which is &lt;a href=&quot;https://caniuse.com/#search=constraint%20validation&quot; rel=&quot;noopener&quot;&gt;widely supported&lt;/a&gt;) to add custom
validation, accessing built-in browser UI to set focus and display prompts. Un-comment the code and
try it out. You&#39;ll need to set appropriate values for &lt;code&gt;someregex&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt;, and set a value for
&lt;code&gt;someField&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What &lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#analytics-rum&quot;&gt;analytics and Real User Monitoring data&lt;/a&gt;
would you monitor in order to identify ways to improve your forms?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your complete payment form should now look like this:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 750px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/payment-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;payment-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/codelab-payment-form-best-practices/#different-devices&quot;&gt;Try out your form on different devices&lt;/a&gt;. What devices and browsers are you
targeting? How could the form be improved?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going further &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-payment-form-best-practices/#going-further&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider the following crucial form features that are not covered in this codelab:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Link to your Terms of Service and privacy policy documents: make it clear to users how you
safeguard their data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Style and branding: make sure these match the rest of your site. When entering names and addresses
and making payment, users need to feel comfortable, trusting that they&#39;re still in the right place.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#analytics-rum&quot;&gt;Analytics and Real User Monitoring&lt;/a&gt;:
enable the performance and usability of your form design to be tested and monitored for real users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Sign-up form best practices codelab</title>
    <link href="https://web.dev/codelab-sign-up-form-best-practices/"/>
    <updated>2020-12-09T00:00:00Z</updated>
    <id>https://web.dev/codelab-sign-up-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This codelab shows you how to build a sign-up form that&#39;s secure, accessible, and easy to use.&lt;/p&gt;
&lt;h2 id=&quot;step-1-use-meaningful-html&quot;&gt;Step 1: Use meaningful HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#step-1-use-meaningful-html&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this step you&#39;ll learn how to use form elements to make the most of built-in browser features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take a look at the HTML for your form in &lt;code&gt;index.html&lt;/code&gt;. You&#39;ll see there are inputs for name, email
and password. Each is in a section, and each has a label. The &lt;strong&gt;Sign up&lt;/strong&gt; button is… a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;!
Later in this codelab, you&#39;ll learn the special powers of all these elements.&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;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements don&#39;t have closing tags. That&#39;s because they are &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#syntax-elements&quot;&gt;void&lt;/a&gt; (empty) elements: they don&#39;t have any content in themselves. A &amp;quot;/&amp;quot; character at the end of a void element is optional: either &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; is OK. &lt;/div&gt;&lt;/aside&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;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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;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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sign up&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;h1&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Full 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 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;section&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Email&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 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;section&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Password&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 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;section&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;button&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;sign-up&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;Sign up&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;button&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;form&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;Click &lt;strong&gt;View App&lt;/strong&gt; to preview your sign-up form. This shows you what a form looks like with no CSS other than the
&lt;a href=&quot;https://bitsofco.de/a-look-at-css-resets-in-2018&quot; rel=&quot;noopener&quot;&gt;default browser styles&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do the default styles &lt;em&gt;look&lt;/em&gt; OK? What would you change to make the form look better?&lt;/li&gt;
&lt;li&gt;Do the default styles &lt;em&gt;work&lt;/em&gt; OK? What problems might be encountered using your form as it is? What
about on mobile? What about for screenreaders or other &lt;a href=&quot;https://web.dev/a11y-tips-for-web-dev&quot;&gt;assistive technologies&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;Who are your users, and what devices and browsers are you targeting?&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; &lt;a href=&quot;https://browserdefaultstyles.com/&quot;&gt;Browser Default Styles&lt;/a&gt; lists default CSS for HTML elements. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;test-your-form&quot;&gt;Test your form &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#test-your-form&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You could acquire a lot of hardware and set up a
&lt;a href=&quot;https://www.smashingmagazine.com/2016/11/worlds-best-open-device-labs/&quot; rel=&quot;noopener&quot;&gt;device lab&lt;/a&gt;, but there are
cheaper and simpler ways to try out your form on a range of browsers, platforms and devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/&quot; rel=&quot;noopener&quot;&gt;Use Chrome DevTools Device Mode&lt;/a&gt;
to simulate mobile devices.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/9430554&quot; rel=&quot;noopener&quot;&gt;Send the URL from your computer to your phone&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a service such as &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;BrowserStack&lt;/a&gt; to test on a range
of devices and browsers.&lt;/li&gt;
&lt;li&gt;Try out the form using a screenreader tool such as the &lt;a href=&quot;https://chrome.google.com/webstore/detail/chromevox-classic-extensi/kgejglhpjiefppelpmljglcjbhoiplfn?hl=en&quot; rel=&quot;noopener&quot;&gt;ChromeVox&lt;/a&gt; Chrome extension.&lt;/li&gt;
&lt;/ul&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; Testing your site on a range of devices and browsers is especially important for forms, because &lt;a href=&quot;https://baymard.com/checkout-usability&quot;&gt;small problems can cause high bounce rates&lt;/a&gt; and cause users to give up on creating an account or completing a purchase. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; to preview your sign-up form.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try out your form on different devices using Chrome DevTools Device Mode.&lt;/li&gt;
&lt;li&gt;Now open the form on a phone or other real devices. What differences do you see?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-2-add-css-to-make-the-form-work-better&quot;&gt;Step 2: Add CSS to make the form work better &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#step-2-add-css-to-make-the-form-work-better&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Click &lt;strong&gt;View Source&lt;/strong&gt; to return to your source code.&lt;/p&gt;
&lt;p&gt;There&#39;s nothing wrong with the HTML so far, but you need to make sure your form works well for a
range of users on mobile and desktop.&lt;/p&gt;
&lt;p&gt;In this step you&#39;ll add CSS to make the form easier to use.&lt;/p&gt;
&lt;p&gt;Copy and paste all the following CSS into &lt;code&gt;css/main.css&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 400px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-up-form-codelab-2?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=css%2Fmain.css&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-up-form-codelab-2 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; to see your styled sign-up form. Then click &lt;strong&gt;View Source&lt;/strong&gt; to return to
&lt;code&gt;css/main.css&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Does this CSS work for a variety of browsers and screen sizes?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try adjusting &lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, and &lt;code&gt;font-size&lt;/code&gt; to suit your test devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CSS is mobile-first. &lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Media_queries&quot; rel=&quot;noopener&quot;&gt;Media queries&lt;/a&gt;
are used to apply CSS rules for viewports that are at least &lt;code&gt;400px&lt;/code&gt; wide, and then again for
viewports that are at least &lt;code&gt;500px&lt;/code&gt; wide. Are these &lt;a href=&quot;https://www.browserstack.com/guide/responsive-design-breakpoints&quot; rel=&quot;noopener&quot;&gt;breakpoints&lt;/a&gt;
adequate? How should you choose breakpoints for forms?&lt;/p&gt;
&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; &lt;strong&gt;Viewport&lt;/strong&gt; in this context means the display area available for your page. Mobile phones have a smaller viewport than laptops, and a small browser window on a desktop monitor has a smaller viewport than a maximized browser window. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;step-3-add-attributes-to-help-users-enter-data&quot;&gt;Step 3: Add attributes to help users enter data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#step-3-add-attributes-to-help-users-enter-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this step you add attributes to your input elements to enable the browser to store and autofill
form field values, and warn of fields with missing or invalid data.&lt;/p&gt;
&lt;p&gt;Update your &lt;code&gt;index.html&lt;/code&gt; file so the form code looks like this:&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;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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;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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sign up&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;h1&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;section&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;Full 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;name&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;autocomplete&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;br /&gt;           &lt;span class=&quot;token attr-name&quot;&gt;pattern&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;[\p{L}\.\- ]+&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&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;section&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;section&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;email&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;Email&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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;username&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&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;section&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;section&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;password&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;Password&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;new-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;br /&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;minlength&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;8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&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;section&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;button&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;sign-up&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;Sign up&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;button&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;form&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 &lt;code&gt;type&lt;/code&gt; values do a lot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;type=&amp;quot;password&amp;quot;&lt;/code&gt; obscures text as it&#39;s entered and enables the browser&#39;s
&lt;a href=&quot;https://passwords.google.com/&quot; rel=&quot;noopener&quot;&gt;password manager&lt;/a&gt; to suggest a strong password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt; provides basic validation and ensures mobile users get an appropriate keyboard. Try
it out!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; and then click the &lt;strong&gt;Email&lt;/strong&gt; label. What happens? Focus moves to the email
input because the label has a &lt;code&gt;for&lt;/code&gt; value that matches the email input&#39;s &lt;code&gt;id&lt;/code&gt;. The other labels and
inputs work the same way. Screenreaders also announce label text when the label (or the label&#39;s
associated input) gets focus. You can try that using the &lt;a href=&quot;https://chrome.google.com/webstore/detail/chromevox-classic-extensi/kgejglhpjiefppelpmljglcjbhoiplfn?hl=en&quot; rel=&quot;noopener&quot;&gt;ChromeVox extension&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Try submitting the form with an empty field. The browser won&#39;t submit the form, and it prompts to
complete missing data and sets focus. That&#39;s because you added the &lt;code&gt;require&lt;/code&gt; attribute to all the
inputs. Now try submitting with a password that has less than eight characters. The browser warns that
the password isn&#39;t long enough and sets focus on the password input because of the &lt;code&gt;minlength=&amp;quot;8&amp;quot;&lt;/code&gt;
attribute. The same works for &lt;code&gt;pattern&lt;/code&gt; (used for the name input) and other
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5/Constraint_validation&quot; rel=&quot;noopener&quot;&gt;validation constraints&lt;/a&gt;.
The browser does all this automatically, without needing any extra code.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;autocomplete&lt;/code&gt; value &lt;code&gt;name&lt;/code&gt; for the &lt;strong&gt;Full name&lt;/strong&gt; input makes sense, but what about the
other inputs?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt; for the &lt;strong&gt;Email&lt;/strong&gt; input means the browser&#39;s password manager will store
the email address as the &#39;name&#39; for this user (the username!) to go with the password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;autocomplete=&amp;quot;new-password&amp;quot;&lt;/code&gt; for &lt;strong&gt;Password&lt;/strong&gt; is a hint to the password manager that it should
offer to store this value as the password for the current site. You can then use
&lt;code&gt;autocomplete=&amp;quot;current-password&amp;quot;&lt;/code&gt; to enable autofill in a sign-in form (remember, this is
&lt;em&gt;sign-up&lt;/em&gt; form).&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; &lt;a href=&quot;https://web.dev/sign-in-form-best-practices&quot;&gt;Sign-in form best practices&lt;/a&gt; has more tips for improving form design, layout, and accessibility. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;step-4-help-users-enter-secure-passwords&quot;&gt;Step 4: Help users enter secure passwords &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#step-4-help-users-enter-secure-passwords&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the form as it is, did you notice anything wrong with the password input?&lt;/p&gt;
&lt;p&gt;There are two issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There&#39;s no way to know if there are constraints on the password value.&lt;/li&gt;
&lt;li&gt;You can&#39;t see the password to check if you got it right.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&#39;t make users guess!&lt;/p&gt;
&lt;p&gt;Update the password section of &lt;code&gt;index.html&lt;/code&gt; with the following code:&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;section&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;password&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;Password&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;button&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;toggle-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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;Show password as plain text. &lt;br /&gt;          Warning: this will display your password on the screen.&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;Show password&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;button&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;new-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;         &lt;span class=&quot;token attr-name&quot;&gt;minlength&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;8&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;password-constraints&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password-constraints&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;Eight or more characters.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&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;This adds new features to help users enter passwords:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A button (actually just text) to toggle password display. (The
button functionality will be added with JavaScript.)&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;aria-label&lt;/code&gt; attribute for the password-toggle button.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;aria-describedby&lt;/code&gt; attribute for the password input. Screenreaders
read the label text, the input type (password), and then the description.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To enable the password-toggle button and show users information about password constraints, copy all the JavaScript below and paste it into your own &lt;code&gt;js/main.js&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 400px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-up-form-codelab-4?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=js%2Fmain.js&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-up-form-codelab-4 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;(The CSS is already in place from step 2. Take a look, to see how the password-toggle button is
styled and positioned.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Would an &lt;a href=&quot;https://material.io/resources/icons/?icon=visibility&quot; rel=&quot;noopener&quot;&gt;icon&lt;/a&gt; work better than text to
toggle password display? Try &lt;a href=&quot;https://www.nngroup.com/articles/discount-usability-20-years/&quot; rel=&quot;noopener&quot;&gt;Discount Usability Testing&lt;/a&gt;
with a small group of friends or colleagues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To experience how screenreaders work with forms, install the &lt;a href=&quot;https://chrome.google.com/webstore/detail/chromevox-classic-extensi/kgejglhpjiefppelpmljglcjbhoiplfn?hl=en&quot; rel=&quot;noopener&quot;&gt;ChromeVox extension&lt;/a&gt; and navigate through the form.
Could you complete the form using ChromeVox only? If not, what would you change?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s how your form should look at this point:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 690px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-up-form-codelab-4?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-up-form-codelab-4 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going further &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-up-form-best-practices/#going-further&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This codelab doesn&#39;t cover several important features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Checking for compromised passwords. You should never allow passwords that have been compromised.
You can (and should) &lt;a href=&quot;https://web.dev/sign-up-form-best-practices/#no-compromised-passwords&quot;&gt;use a password-checking service to catch compromised passwords&lt;/a&gt;.
You can use an existing service or run one yourself on your own servers. Try it out! Add password
checking to your form.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Link to your Terms of Service and privacy policy documents: make it clear to users how you
safeguard their data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Style and branding: make sure these match the rest of your site. When entering names and addresses
and making payment, users need to feel comfortable, trusting that they&#39;re still in the right place.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/payment-and-address-form-best-practices#analytics-rum&quot;&gt;Analytics and Real User Monitoring&lt;/a&gt;:
enable the performance and usability of your form design to be tested and monitored for real users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>What are third-party origin trials?</title>
    <link href="https://web.dev/third-party-origin-trials/"/>
    <updated>2020-10-01T00:00:00Z</updated>
    <id>https://web.dev/third-party-origin-trials/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/origin-trials&quot;&gt;Origin trials&lt;/a&gt; are a way to test a new or experimental web platform
feature.&lt;/p&gt;
&lt;p&gt;Origin trials are usually only available on a first-party basis: they only work for a single
registered &lt;a href=&quot;https://web.dev/same-site-same-origin/#origin&quot;&gt;origin&lt;/a&gt;. If a developer wants to test an
experimental feature on other origins where their content is embedded, those origins all need to be
registered for the origin trial, each with a unique trial token. This is not a scalable approach for
testing scripts that are embedded across a number of sites.&lt;/p&gt;
&lt;p&gt;Third-party origin trials make it possible for providers of embedded content to try out a new
feature across multiple sites.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing how third-party origin trials enable a single registration token to be used across multiple origins&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3lDDKsr313oJfWuEMckG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Third-party origin trials don&#39;t make sense for all features. Chrome will only make the third-party
origin trial option available for features where embedding code on third-party sites is a common use
case.  &lt;a href=&quot;https://developers.chrome.com/origintrials/&quot; rel=&quot;noopener&quot;&gt;Getting started with Chrome&#39;s origin trials&lt;/a&gt;
provides more general information about how to participate in Chrome origin trials.&lt;/p&gt;
&lt;p&gt;If you participate in an origin trial as a third-party provider, it will be your responsibility to
notify and set expectations with any partners or customers whose sites you intend to include in the
origin trial. Experimental features may cause unexpected issues and browser vendors may not be able
to provide troubleshooting support.&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; Supporting third-party origin trials allows for broader participation, but also increases the potential for overuse or abuse of experimental features, so a &amp;quot;trusted tester&amp;quot; approach is more appropriate. The greater reach of third-party origin trials requires additional scrutiny and additional responsibility for web developers that participate as third-party providers. Requests to enable a third-party origin trial may be reviewed in order to avoid problematic third-party scripts affecting multiple sites. The Origin Trials Developer Guide explains the &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md#18-how-can-i-enable-an-experimental-feature-as-embedded-content-on-different-domains&quot;&gt;approval process&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Check &lt;a href=&quot;https://www.chromestatus.com/features/5691464711405568&quot; rel=&quot;noopener&quot;&gt;Chrome Platform Status&lt;/a&gt; for updates
on progress with third-party origin trials.&lt;/p&gt;
&lt;h2 id=&quot;how-to-register-for-a-third-party-origin-trial&quot;&gt;How to register for a third-party origin trial &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-origin-trials/#how-to-register-for-a-third-party-origin-trial&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Select a trial from the &lt;a href=&quot;https://developers.chrome.com/origintrials/#/trials/active&quot; rel=&quot;noopener&quot;&gt;list of active
trials&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the trial&#39;s registration page, enable the option to request a third-party token, if
available.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select one of the choices for restricting usage for a third-party token:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Standard Limit: This is the usual limit of
&lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md#3-what-happens-if-a-large-site-such-as-a-google-service-starts-depending-on-an-experimental-feature&quot; rel=&quot;noopener&quot;&gt;0.5% of Chrome page loads&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;User Subset: A small percentage of Chrome users will always be excluded from the trial,
even when a valid third-party token is provided. The exclusion percentage varies (or might
not apply) for each trial, but is typically less than 5%.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Register button to submit your request.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your third-party token will be issued immediately, unless further review of the request is
required. (Depending on the trial, token requests may require review.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If review is required, you&#39;ll be notified by email when the review is complete and your
third-party token is ready.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome origin trials registration page for the Conversion Measurement API, with third-party matching checkbox selected.&quot; decoding=&quot;async&quot; height=&quot;618&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RKAubZHAdOh7HIdQgDkQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Registration page for the Conversion Measurement trial.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;how-to-provide-feedback&quot;&gt;How to provide feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-origin-trials/#how-to-provide-feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;re registering for a third-party origin trial and have feedback to share on the process or
ideas on how we can improve it, please &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/issues/new&quot; rel=&quot;noopener&quot;&gt;create an
issue&lt;/a&gt; on the Origin Trials GitHub code
repo.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-origin-trials/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/origin-trials&quot;&gt;Getting started with Chrome&#39;s origin trials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md&quot; rel=&quot;noopener&quot;&gt;Origin Trials Guide for Web Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromestatus.com/features/5691464711405568&quot; rel=&quot;noopener&quot;&gt;Chrome Platform Status&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@_louisreed&quot; rel=&quot;noopener&quot;&gt;Louis Reed
&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/JeInkKlI2Po&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Sign-in form best practices</title>
    <link href="https://web.dev/sign-in-form-best-practices/"/>
    <updated>2020-06-29T00:00:00Z</updated>
    <id>https://web.dev/sign-in-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;alGcULGtiv8&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;If users ever need to log in to your site, then good sign-in form design is
critical. This is especially true for people on poor connections, on mobile, in
a hurry, or under stress. Poorly designed sign-in forms get high bounce rates.
Each bounce could mean a lost and disgruntled user—not just a missed sign-in
opportunity.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewbox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you would prefer to learn these best practices with a hands-on tutorial, check out the &lt;a href=&quot;https://web.dev/codelab-sign-in-form-best-practices/&quot;&gt;Sign-in form best practices codelab&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Here is an example of a simple sign-in form that demonstrates all of the best practices:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;checklist&quot;&gt;Checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#checklist&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#meaningful-html&quot;&gt;Use meaningful HTML elements&lt;/a&gt;: &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;,
&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#label&quot;&gt;Label each input with a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use element attributes to &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#element-attributes&quot;&gt;access built-in browser
features&lt;/a&gt;: &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;autocomplete&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Give input &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; attributes stable values that don&#39;t change
between page loads or website deployments.&lt;/li&gt;
&lt;li&gt;Put sign-in &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#form&quot;&gt;in its own &amp;lt;form&amp;gt; element&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#submission&quot;&gt;Ensure successful form submission&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#new-password&quot;&gt;&lt;code&gt;autocomplete=&amp;quot;new-password&amp;quot;&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#new-password&quot;&gt;&lt;code&gt;id=&amp;quot;new-password&amp;quot;&lt;/code&gt;&lt;/a&gt; for
the password input in a sign-up form, and for the new password in a reset-password form.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#current-password&quot;&gt;&lt;code&gt;autocomplete=&amp;quot;current-password&amp;quot;&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#current-password&quot;&gt;&lt;code&gt;id=&amp;quot;current-password&amp;quot;&lt;/code&gt;&lt;/a&gt;
for a sign-in password input.&lt;/li&gt;
&lt;li&gt;Provide &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#show-password&quot;&gt;Show password&lt;/a&gt; functionality.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#accessible-password-inputs&quot;&gt;Use &lt;code&gt;aria-label&lt;/code&gt; and &lt;code&gt;aria-describedby&lt;/code&gt;&lt;/a&gt; for
password inputs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#no-double-inputs&quot;&gt;Don&#39;t double-up inputs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Design forms so the &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#keyboard-obstruction&quot;&gt;mobile keyboard doesn&#39;t obscure inputs or
buttons&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ensure forms are usable on mobile: use &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#size-text-correctly&quot;&gt;legible text&lt;/a&gt;,
and make sure inputs and buttons are &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#tap-targets&quot;&gt;large enough to work as touch targets&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#general-guidelines&quot;&gt;Maintain branding and style&lt;/a&gt; on your sign-up and sign-in pages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#analytics&quot;&gt;Test in the field as well as the lab&lt;/a&gt;: build page analytics,
interaction analytics, and user-centric performance measurement into your
sign-up and sign-in flow.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#devices&quot;&gt;Test across browsers and devices&lt;/a&gt;: form behaviour varies
significantly across platforms.&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 article is about frontend best practices. It does not explain how to build backend services to authenticate users, store their credentials, or manage their accounts. &lt;a href=&quot;https://cloud.google.com/blog/products/gcp/12-best-practices-for-user-account&quot;&gt;12 best practices for user account, authorization and password management&lt;/a&gt; outlines core principles for running your own backend. If you have users in different parts of the world, you need to consider localizing your site&#39;s use of third-party identity services as well as its content.  There are also two relatively new APIs not covered in this article which can help you build a better sign-in experience: *  &lt;a href=&quot;https://web.dev/web-otp/&quot;&gt;&lt;strong&gt;WebOTP&lt;/strong&gt;&lt;/a&gt;: to deliver one-time passcodes or PIN numbers via SMS to mobile phones. This can allow users to select a phone number as an identifier (no need to enter an email address!) and also enables two-step verification for sign-in and one-time codes for payment confirmation. * &lt;a href=&quot;https://developer.chrome.com/blog/credential-management-api/&quot;&gt;&lt;strong&gt;Credential Management&lt;/strong&gt;&lt;/a&gt;: to enable developers to store and retrieve password credentials and federated credentials programmatically. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;meaningful-html&quot;&gt;Use meaningful HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#meaningful-html&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use elements built for the job: &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. These enable
built-in browser functionality, improve accessibility, and add meaning to your
markup.&lt;/p&gt;
&lt;h3 id=&quot;form&quot;&gt;Use &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#form&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You might be tempted to wrap inputs in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and handle input data
submission purely with JavaScript. It&#39;s generally better to use a plain old
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/form&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;&lt;/a&gt;
element. This makes your site accessible to screenreaders and other assistive
devices, enables a range of built-in browser features, makes it simpler to build
basic functional sign-in for older browsers, and can still work even if
JavaScript fails.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; A common mistake is to wrap a whole web page in a single form, but this is liable to cause problems for browser password managers and autofill. Use a different &amp;lt;form&amp;gt; for each UI component that needs a form. For example, if you have sign-in and search on the same page, you should use two form elements. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;label&quot;&gt;Use &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#label&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To label an input, use a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/label&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/a&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 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;email&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;Email&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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;…&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;Two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A tap or click on a label moves focus to its input. Associate a label with an
input by using the label&#39;s &lt;code&gt;for&lt;/code&gt; attribute with the input&#39;s &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Screenreaders announce label text when the label or the label&#39;s input gets
focus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&#39;t use placeholders as input labels. People are liable to forget what the
input was for once they&#39;ve started entering text, especially if they get
distracted (&amp;quot;Was I entering an email address, a phone number, or an account
ID?&amp;quot;). There are lots of other potential problems with placeholders: see &lt;a href=&quot;https://www.smashingmagazine.com/2018/06/placeholder-attribute/&quot; rel=&quot;noopener&quot;&gt;Don&#39;t
Use The Placeholder
Attribute&lt;/a&gt; and
&lt;a href=&quot;https://www.nngroup.com/articles/form-design-placeholders/&quot; rel=&quot;noopener&quot;&gt;Placeholders in Form Fields Are
Harmful&lt;/a&gt; if you&#39;re
unconvinced.&lt;/p&gt;
&lt;p&gt;It&#39;s probably best to put your labels above your inputs. This enables consistent
design across mobile and desktop and, according to &lt;a href=&quot;https://ai.googleblog.com/2014/07/simple-is-better-making-your-web-forms.html&quot; rel=&quot;noopener&quot;&gt;Google AI
research&lt;/a&gt;,
enables quicker scanning by users. You get full width labels and inputs, and you
don&#39;t need to adjust label and input width to fit the label text.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing form input label position on mobile: next to input and above input.&quot; decoding=&quot;async&quot; height=&quot;253&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/k0ioJa9CqnMI8vyAvQPS.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
  &lt;figcaption&gt;Label and input width is limited when both are on the same line.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Open the &lt;a href=&quot;https://label-position.glitch.me/&quot; rel=&quot;noopener&quot;&gt;label-position&lt;/a&gt; Glitch on a
mobile device to see for yourself.&lt;/p&gt;
&lt;h3 id=&quot;button&quot;&gt;Use &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#button&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/button&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/a&gt;
for buttons! Button elements provide accessible behaviour and built-in form
submission functionality, and they can easily be styled. There&#39;s no point in
using a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; or some other element pretending to be a button.&lt;/p&gt;
&lt;p&gt;Ensure that the submit button says what it does. Examples include &lt;strong&gt;Create account&lt;/strong&gt; or
&lt;strong&gt;Sign in&lt;/strong&gt;, not &lt;strong&gt;Submit&lt;/strong&gt; or &lt;strong&gt;Start&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;submission&quot;&gt;Ensure successful form submission &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#submission&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Help password managers understand that a form has been submitted. There are two
ways to do that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to a different page.&lt;/li&gt;
&lt;li&gt;Emulate navigation with &lt;code&gt;History.pushState()&lt;/code&gt; or &lt;code&gt;History.replaceState()&lt;/code&gt;,
and remove the password form.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With an &lt;code&gt;XMLHttpRequest&lt;/code&gt; or &lt;code&gt;fetch&lt;/code&gt; request, make sure that sign-in success is
reported in the response and handled by taking the form out of the DOM as well
as indicating success to the user.&lt;/p&gt;
&lt;p&gt;Consider disabling the &lt;strong&gt;Sign in&lt;/strong&gt; button once the user has tapped or clicked
it. &lt;a href=&quot;https://baymard.com/blog/users-double-click-online&quot; rel=&quot;noopener&quot;&gt;Many users click buttons multiple times&lt;/a&gt;
even on sites that are fast and responsive. That slows down interactions and
adds to server load.&lt;/p&gt;
&lt;p&gt;Conversely, don&#39;t disable form submission awaiting user input. For example,
don&#39;t disable the &lt;strong&gt;Sign in&lt;/strong&gt; button if users haven&#39;t entered their customer
PIN. Users may miss out something in the form, then try repeatedly tapping the
(disabled) &lt;strong&gt;Sign in&lt;/strong&gt; button and think it&#39;s not working. At the very least, if
you must disable form submission, explain to the user what&#39;s missing when they
click on the disabled button.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The default type for a button in a form is &lt;code&gt;submit&lt;/code&gt;. If you want to add another button in a form (for &lt;strong&gt;Show password&lt;/strong&gt;, for example) add &lt;code&gt;type=&amp;quot;button&amp;quot;&lt;/code&gt;. Otherwise clicking or tapping on the button will submit the form.  Pressing the &lt;code&gt;Enter&lt;/code&gt; key while any form field has focus simulates a click on the first &lt;code&gt;submit&lt;/code&gt; button in the form. If you include a button in your form before the &lt;strong&gt;Submit&lt;/strong&gt; button, and don&#39;t specify the type, that button will have the default type for buttons in a form (&lt;code&gt;submit&lt;/code&gt;) and receive the click event before the form is submitted. For an example of this, see our &lt;a href=&quot;https://enter-button.glitch.me/&quot;&gt;demo&lt;/a&gt;: fill in the form, then press &lt;code&gt;Enter&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;no-double-inputs&quot;&gt;Don&#39;t double up inputs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#no-double-inputs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some sites force users to enter emails or passwords twice. That might reduce
errors for a few users, but causes extra work for &lt;em&gt;all&lt;/em&gt; users, and &lt;a href=&quot;https://uxmovement.com/forms/why-the-confirm-password-field-must-die/&quot; rel=&quot;noopener&quot;&gt;increases
abandonment
rates&lt;/a&gt;.
Asking twice also makes no sense where browsers autofill email addresses or
suggest strong passwords. It&#39;s better to enable users to confirm their email
address (you&#39;ll need to do that anyway) and make it easy for them to reset their
password if necessary.&lt;/p&gt;
&lt;h2 id=&quot;element-attributes&quot;&gt;Make the most of element attributes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#element-attributes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where the magic really happens!
Browsers have multiple helpful built-in features that use input element attributes.&lt;/p&gt;
&lt;h2 id=&quot;show-password&quot;&gt;Keep passwords private—but enable users to see them if they want &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#show-password&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Passwords inputs should have &lt;code&gt;type=&amp;quot;password&amp;quot;&lt;/code&gt; to hide password text and help the
browser understand that the input is for passwords. (Note that browsers use
&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#autofill&quot;&gt;a variety of techniques&lt;/a&gt; to understand input roles and decide
whether or not to offer to save passwords.)&lt;/p&gt;
&lt;p&gt;You should add a &lt;strong&gt;Show password&lt;/strong&gt; icon or button to enable users to check the
text they&#39;ve entered—and don&#39;t forget to add a &lt;strong&gt;Forgot password&lt;/strong&gt; link. See
&lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#password-display&quot;&gt;Enable password display&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Google sign-in form showing Show password icon.&quot; decoding=&quot;async&quot; height=&quot;107&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/58suVe0HnSLaJvNjKY53.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;Password input from the Google sign-in form: with &lt;strong&gt;Show password&lt;/strong&gt; icon and &lt;strong&gt;Forgot password&lt;/strong&gt; link.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;mobile-keyboards&quot;&gt;Give mobile users the right keyboard &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#mobile-keyboards&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;&amp;lt;input type=&amp;quot;email&amp;quot;&amp;gt;&lt;/code&gt; to give mobile users an appropriate keyboard and
enable basic built-in email address validation by the browser… no JavaScript
required!&lt;/p&gt;
&lt;p&gt;If you need to use a telephone number instead of an email address, &lt;code&gt;&amp;lt;input type=&amp;quot;tel&amp;quot;&amp;gt;&lt;/code&gt; enables a telephone keypad on mobile. You can also use the
&lt;code&gt;inputmode&lt;/code&gt; attribute where necessary: &lt;code&gt;inputmode=&amp;quot;numeric&amp;quot;&lt;/code&gt; is ideal for PIN
numbers. &lt;a href=&quot;https://css-tricks.com/everything-you-ever-wanted-to-know-about-inputmode/&quot; rel=&quot;noopener&quot;&gt;Everything You Ever Wanted to Know About
inputmode&lt;/a&gt;
has more detail.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt; adds an up/down arrow to increment numbers, so don&#39;t use it for numbers that aren&#39;t meant to be incremented, such as IDs and account numbers. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;keyboard-obstruction&quot;&gt;Prevent mobile keyboard from obstructing the &lt;strong&gt;Sign in&lt;/strong&gt; button &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#keyboard-obstruction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, if you&#39;re not careful, mobile keyboards may cover your form or,
worse, partially obstruct the &lt;strong&gt;Sign in&lt;/strong&gt; button. Users may give up before
realizing what has happened.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Two screenshots of a sign-in form on an Android phone: one showing how the Submit button is obscured by the phone keyboard.&quot; decoding=&quot;async&quot; height=&quot;360&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/rLo5sW9LBpTcJU7KNnb7.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;The &lt;b&gt;Sign in&lt;/b&gt; button: now you see it, now you don&#39;t.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Where possible, avoid this by displaying only the email/phone and password inputs and &lt;strong&gt;Sign in&lt;/strong&gt; button at the top of your sign-in page. Put other content below.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a sign-in form on an Android phone: the Sign in button is not obscured by the phone keyboard.&quot; decoding=&quot;async&quot; height=&quot;342&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 200px) 200px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/0OebKiAP4sTgaXbcbvYx.png?auto=format&amp;w=400 400w&quot; width=&quot;200&quot; /&gt;
  &lt;figcaption&gt;The keyboard doesn&#39;t obstruct the &lt;b&gt;Sign in&lt;/b&gt; button.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;devices&quot;&gt;Test on a range of devices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#devices&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You&#39;ll need to test on a range of devices for your target audience, and adjust
accordingly. BrowserStack enables &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;free testing for open source
projects&lt;/a&gt; on a range of real devices
and browsers.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of a sign-in form on iPhone 7, 8 and 11. On iPhone 7 and 8 the Sign in button is obscured by the phone keyboard, but not on iPhone 11&quot; decoding=&quot;async&quot; height=&quot;522&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/jToMlWgjS3J2WKmjs1hx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The &lt;b&gt;Sign in&lt;/b&gt; button: obscured on iPhone 7 and 8, but not on iPhone 11.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;two-pages&quot;&gt;Consider using two pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#two-pages&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some sites (including Amazon and eBay) avoid the problem by asking for
email/phone and password on two pages. This approach also simplifies the
experience: the user is only tasked with one thing at a time.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a sign-in form on the Amazon website: email/phone and password on two separate &amp;#x27;pages&amp;#x27;.&quot; decoding=&quot;async&quot; height=&quot;385&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/CxpObjYZMs0MMFo66f4P.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;Two-stage sign-in: email or phone, then password.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Ideally, this should be implemented with a single &amp;lt;form&amp;gt;. Use JavaScript
to initially display only the email input, then hide it and show the password input.
If you must force the user to navigate to a new page between entering their email and
password, the form on the second page should have a hidden input element with the
email value, to help enable password managers to store the correct value. &lt;a href=&quot;https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands&quot; rel=&quot;noopener&quot;&gt;Password
Form Styles that Chromium Understands&lt;/a&gt;
provides a code example.&lt;/p&gt;
&lt;h3 id=&quot;autofill&quot;&gt;Help users to avoid re-entering data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#autofill&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can help browsers store data correctly and autofill inputs, so users don&#39;t
have to remember to enter email and password values. This is particularly important
on mobile, and crucial for email inputs, which get &lt;a href=&quot;https://www.formisimo.com/blog/conversion-rate-increases-57-with-form-analytics-case-study/&quot; rel=&quot;noopener&quot;&gt;high abandonment rates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two parts to this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;autocomplete&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, and &lt;code&gt;type&lt;/code&gt; attributes help browsers understand
the role of inputs in order to store data that can later be used for autofill.
To allow data to be stored for autofill, modern browsers also require inputs to
have a stable &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt; value (not randomly generated on each page load or
site deployment), and to be in a &amp;lt;form&amp;gt; with a &lt;code&gt;submit&lt;/code&gt; button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;autocomplete&lt;/code&gt; attribute helps browsers correctly autofill inputs using
stored data.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For email inputs use &lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt;, since &lt;code&gt;username&lt;/code&gt; is recognized
by password managers in modern browsers—even though you should use &lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt;
and you may want to use &lt;code&gt;id=&amp;quot;email&amp;quot;&lt;/code&gt; and &lt;code&gt;name=&amp;quot;email&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For password inputs, use the appropriate &lt;code&gt;autocomplete&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; values to help browsers
differentiate between new and current passwords.&lt;/p&gt;
&lt;h3 id=&quot;new-password&quot;&gt;Use &lt;code&gt;autocomplete=&amp;quot;new-password&amp;quot;&lt;/code&gt; and &lt;code&gt;id=&amp;quot;new-password&amp;quot;&lt;/code&gt; for a new password &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#new-password&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;autocomplete=&amp;quot;new-password&amp;quot;&lt;/code&gt; and &lt;code&gt;id=&amp;quot;new-password&amp;quot;&lt;/code&gt; for the password input in a sign-up
form, or the new password in a change-password form.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;current-password&quot;&gt;Use &lt;code&gt;autocomplete=&amp;quot;current-password&amp;quot;&lt;/code&gt; and &lt;code&gt;id=&amp;quot;current-password&amp;quot;&lt;/code&gt; for an existing password &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#current-password&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;autocomplete=&amp;quot;current-password&amp;quot;&lt;/code&gt; and &lt;code&gt;id=&amp;quot;current-password&amp;quot;&lt;/code&gt; for the password input in a
sign-in form, or the input for the user&#39;s old password in a change-password form. This tells the
browser that you want it to use the current password that it has stored for the site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a sign-up form:&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;input&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;new-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;new-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;…&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;For sign-in:&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;input&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;current-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;current-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;…&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; Browsers such as Chrome can use the browser&#39;s password manager to autofill fields in the sign-in process for returning users. For these features to work, the browser needs to be able to distinguish when a user changes their password.  Specifically the form for changing the user&#39;s password should be cleared or hidden from the page after the new password is set up. If the form for changing the user&#39;s password stays filled out on the page after the password change has occurred, the browser may not be able to record the update. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;password-managers&quot;&gt;Support password managers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#password-managers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Different browsers handle email autofill and password suggestion somewhat
differently, but the effects are much the same. On Safari 11 and above on desktop,
for example, the password manager is displayed, and then biometric
authentication (fingerprint or facial recognition) is used if available.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of three stages of sign-in process in Safari on desktop: password manager, biometric authentication, autofill.&quot; decoding=&quot;async&quot; height=&quot;234&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/UjBRRYaLbX9bh3LDFcAM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Sign-in with autocomplete—no text entry required!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Chrome on desktop displays email suggestions, shows the password manager, and autofills the password.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of four stages of sign-in process in Chrome on desktop: email completion, email suggestion, password manager, autofill on selection.&quot; decoding=&quot;async&quot; height=&quot;232&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/mDm1cstWZB9jJDzMmzgE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Autocomplete sign-in flow in Chrome 84.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Browser password and autofill systems are not simple. The algorithms for
guessing, storing and displaying values are not standardized, and vary from
platform to platform. For example, as pointed out by &lt;a href=&quot;https://hiddedevries.nl/en/blog/2018-01-13-making-password-managers-play-ball-with-your-login-form&quot; rel=&quot;noopener&quot;&gt;Hidde de
Vries&lt;/a&gt;:
&amp;quot;Firefox&#39;s password manager complements its heuristics with a
&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1119454&quot; rel=&quot;noopener&quot;&gt;recipe system&lt;/a&gt;.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloudfour.com/thinks/autofill-what-web-devs-should-know-but-dont&quot; rel=&quot;noopener&quot;&gt;Autofill: What web devs should know, but
don&#39;t&lt;/a&gt;
has a lot more information about using &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;autocomplete&lt;/code&gt;. The &lt;a href=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#inappropriate-for-the-control&quot; rel=&quot;noopener&quot;&gt;HTML
spec&lt;/a&gt;
lists all 59 possible values.&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 help password managers by using different &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; values in sign-up and sign-in forms, for the &lt;code&gt;form&lt;/code&gt; element itself, as well as any &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt; and &lt;code&gt;textarea&lt;/code&gt; elements. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;password-suggestions&quot;&gt;Enable the browser to suggest a strong password &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#password-suggestions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modern browsers use heuristics to decide when to show the password manager UI and
suggest a strong password.&lt;/p&gt;
&lt;p&gt;Here&#39;s how Safari does it on desktop.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of Firefox password manager on desktop.&quot; decoding=&quot;async&quot; height=&quot;229&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/B1DlZK0CllVjrOUbb5xB.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Password suggestion flow in Safari.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;(Strong unique password suggestion has been available in Safari since version 12.0.)&lt;/p&gt;
&lt;p&gt;Built-in browser password generators mean users and developers don&#39;t need
to work out what a &amp;quot;strong password&amp;quot; is. Since browsers can securely store
passwords and autofill them as necessary, there&#39;s no need for users to remember
or enter passwords. Encouraging users to take advantage of built-in browser
password generators also means they&#39;re more likely to use a unique, strong
password on your site, and less likely to reuse a password that could be
compromised elsewhere.&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 downside with this approach is that there&#39;s no way to share passwords across platforms. For example, a user may use Safari on their iPhone and Chrome on their Windows laptop. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;required-fields&quot;&gt;Help save users from accidentally missing inputs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#required-fields&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add the &lt;code&gt;required&lt;/code&gt; attribute to both email and password fields.
Modern browsers automatically prompt and set focus for missing data.
No JavaScript required!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of desktop Firefox and Chrome for Android showing &amp;#x27;Please fill out this field&amp;#x27; prompt for missing data.&quot; decoding=&quot;async&quot; height=&quot;392&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/n5Nr290upVmQGvlc263U.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;
    Prompt and focus for missing data on Firefox for desktop (version 76)
    and Chrome for Android (version 83).
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;mobile-design&quot;&gt;Design for fingers and thumbs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#mobile-design&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default browser size for just about everything relating to input elements
and buttons is too small, especially on mobile. This may seem obvious, but it&#39;s
a common problem with sign-in forms on many sites.&lt;/p&gt;
&lt;h3 id=&quot;tap-targets&quot;&gt;Make sure inputs and buttons are large enough &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#tap-targets&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The default size and padding for inputs and buttons is too small on desktop and
even worse on mobile.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of unstyled form in Chrome for desktop and Chrome for Android.&quot; decoding=&quot;async&quot; height=&quot;434&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lJNO6w2dOyp4cYKl5b3y.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;According to &lt;a href=&quot;https://support.google.com/accessibility/android/answer/7101858?hl=en-GB&quot; rel=&quot;noopener&quot;&gt;Android accessibility
guidance&lt;/a&gt;
the recommended target size for touchscreen objects is 7–10 mm. Apple interface
guidelines suggest 48x48 px, and the W3C suggest &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/target-size.html&quot; rel=&quot;noopener&quot;&gt;at least 44x44 CSS
pixels&lt;/a&gt;. On that
basis, add (at least) about 15 px of padding to input elements and buttons for
mobile, and around 10 px on desktop. Try this out with a real mobile device and
a real finger or thumb. You should comfortably be able to tap each of your
inputs and buttons.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/seo/http-status-code/&quot; rel=&quot;noopener&quot;&gt;Tap targets are not sized appropriately&lt;/a&gt;
Lighthouse audit can help you automate the process of detecting input elements
that are too small.&lt;/p&gt;
&lt;h4 id=&quot;design-for-thumbs&quot;&gt;Design for thumbs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#design-for-thumbs&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Search for &lt;a href=&quot;https://www.google.com/search?q=touch+target&quot; rel=&quot;noopener&quot;&gt;touch target&lt;/a&gt; and
you&#39;ll see lots of pictures of forefingers. However, in the real world, many
people use their thumbs to interact with phones. Thumbs are bigger than
forefingers, and control is less precise. All the more reason for adequately
sized touch targets.&lt;/p&gt;
&lt;h3 id=&quot;size-text-correctly&quot;&gt;Make text big enough &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#size-text-correctly&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As with size and padding, the default browser font size for input elements and
buttons is too small, particularly on mobile.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of unstyled form in Chrome on desktop and on Android.&quot; decoding=&quot;async&quot; height=&quot;494&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/EeIsqWhLbot15p4SYpo2.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Default styling on desktop and mobile: input text is too small to be legible for many users.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Browsers on different platforms size fonts differently, so it&#39;s difficult to
specify a particular font size that works well everywhere. A quick survey of
popular websites shows sizes of 13–16 pixels on desktop: matching that physical size
is a good minimum for text on mobile.&lt;/p&gt;
&lt;p&gt;This means you need to use a larger pixel size on mobile: &lt;code&gt;16px&lt;/code&gt; on Chrome for
desktop is quite legible, but even with good vision it&#39;s difficult to read &lt;code&gt;16px&lt;/code&gt;
text on Chrome for Android. You can set different font pixel sizes for different
viewport sizes using &lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ux/responsive#apply_media_queries_based_on_viewport_size&quot; rel=&quot;noopener&quot;&gt;media
queries&lt;/a&gt;.
&lt;code&gt;20px&lt;/code&gt; is about right on mobile—but you should test this out with friends or
colleagues who have low vision.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/seo/font-size/&quot; rel=&quot;noopener&quot;&gt;Document doesn&#39;t use legible font sizes&lt;/a&gt;
Lighthouse audit can help you automate the process of detecting text that&#39;s too
small.&lt;/p&gt;
&lt;h3 id=&quot;size-margins-correctly&quot;&gt;Provide enough space between inputs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#size-margins-correctly&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add enough margin to make inputs work well as touch targets. In other words, aim
for about a finger width of margin.&lt;/p&gt;
&lt;h3 id=&quot;visible-inputs&quot;&gt;Make sure your inputs are clearly visible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#visible-inputs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The default border styling for inputs makes them hard to see. They&#39;re almost
invisible on some platforms such as Chrome for Android.&lt;/p&gt;
&lt;p&gt;As well as padding, add a border: on a white background, a good general rule is
to use &lt;code&gt;#ccc&lt;/code&gt; or darker.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of styled form in Chrome on Android.&quot; decoding=&quot;async&quot; height=&quot;525&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 250px) 250px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/OgDJ5V2N7imHXSBkN4pr.png?auto=format&amp;w=500 500w&quot; width=&quot;250&quot; /&gt;
  &lt;figcaption&gt;Legible text, visible input borders, adequate padding and margins.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;built-in-validation&quot;&gt;Use built-in browser features to warn of invalid input values &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#built-in-validation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browsers have built-in features to do basic form validation for inputs with a
&lt;code&gt;type&lt;/code&gt; attribute. Browsers warn when you submit a form with an invalid value,
and set focus on the problematic input.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a sign-in form in Chrome on desktop showing browser prompt and focus for an invalid email value.&quot; decoding=&quot;async&quot; height=&quot;290&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Phf9m5J66lIX9x5brzOL.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;Basic built-in validation by the browser.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can use the &lt;code&gt;:invalid&lt;/code&gt; CSS selector to highlight invalid data. Use
&lt;code&gt;:not(:placeholder-shown)&lt;/code&gt; to avoid selecting inputs with no content.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;input[type=email]:not(:placeholder-shown):invalid&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;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;outline-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&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;Try out different ways of highlighting inputs with invalid values.&lt;/p&gt;
&lt;h2 id=&quot;javascript&quot;&gt;Use JavaScript where necessary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;password-display&quot;&gt;Toggle password display &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#password-display&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You should add a &lt;strong&gt;Show password&lt;/strong&gt; icon or button to enable users to check the
text they&#39;ve entered. &lt;a href=&quot;https://www.nngroup.com/articles/stop-password-masking/&quot; rel=&quot;noopener&quot;&gt;Usability
suffers&lt;/a&gt; when users
can&#39;t see the text they&#39;ve entered. Currently there&#39;s no built-in way to do
this, though &lt;a href=&quot;https://twitter.com/sw12/status/1251191795377156099&quot; rel=&quot;noopener&quot;&gt;there are plans for
implementation&lt;/a&gt;. You&#39;ll
need to use JavaScript instead.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;https://web.dev/sign-in-form-best-practices/show-password-google.png&quot; alt=&quot;Google sign-in form showing Show password icon.&quot; width=&quot;350&quot; /&gt;
  &lt;figcaption&gt;Google sign-in form: with &lt;strong&gt;Show password&lt;/strong&gt; icon and &lt;strong&gt;Forgot password&lt;/strong&gt; link.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The following code uses a text button to add &lt;strong&gt;Show password&lt;/strong&gt; functionality.&lt;/p&gt;
&lt;p&gt;HTML:&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;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;password&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;Password&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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;button&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;toggle-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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;Show password as plain text. Warning: this will display your password on the screen.&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;Show password&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;current-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here&#39;s the CSS to make the button look like plain text:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;button#toggle-password&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;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Media query isn&#39;t shown here. */&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; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--mobile-font-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 property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Display at the top right of the container */&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And the JavaScript for showing the password:&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; passwordInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;password&#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; togglePasswordButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;toggle-password&#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;togglePasswordButton&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; togglePassword&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;togglePassword&lt;/span&gt;&lt;span class=&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;passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;password&#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;    passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Hide password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&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-label&#39;&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;&#39;Hide password.&#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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Show password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&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-label&#39;&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;&#39;Show password as plain text. &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;Warning: this will display your password on the screen.&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here&#39;s the end result:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of sign-in form with Show password text &amp;#x27;button&amp;#x27;, in Safari on Mac and on iPhone 7.&quot; decoding=&quot;async&quot; height=&quot;468&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/x4NP9JMf1KI8PapQ9JFh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Sign-in form with &lt;strong&gt;Show password&lt;/strong&gt; text &#39;button&#39;, in Safari on Mac and iPhone 7.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;accessible-password-inputs&quot;&gt;Make password inputs accessible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#accessible-password-inputs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;aria-describedby&lt;/code&gt; to outline password rules by giving it the ID of the
element that describes the constraints. Screenreaders provide the label text, the
input type (password), and then the description.&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;input&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;password&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;password-constraints&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&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;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password-constraints&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;Eight or more characters with a mix of letters, numbers and symbols.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When you add &lt;strong&gt;Show password&lt;/strong&gt; functionality, make sure to include
an &lt;code&gt;aria-label&lt;/code&gt; to warn that the password will be displayed. Otherwise users may
inadvertently reveal passwords.&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;button&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;toggle-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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;Show password as plain text.&lt;br /&gt;                    Warning: this will display your password on the screen.&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;highlight-line&quot;&gt;  Show password&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can see both ARIA features in action in the following Glitch:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://webaim.org/techniques/forms/&quot; rel=&quot;noopener&quot;&gt;Creating Accessible Forms&lt;/a&gt; has more tips to help make forms accessible.&lt;/p&gt;
&lt;h3 id=&quot;validation&quot;&gt;Validate in realtime and before submission &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#validation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HTML form elements and attributes have built-in features for basic validation,
but you should also use JavaScript to do more robust validation while users are
entering data and when they attempt to submit the form.&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; Client-side validation helps users enter data and can avoid unnecessary server load, but you must always validate and sanitize data on your backend. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://glitch.com/edit/#!/sign-in-form-codelab-5&quot; rel=&quot;noopener&quot;&gt;Step 5&lt;/a&gt; of the sign-in form
codelab uses the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/forms.html#constraints&quot; rel=&quot;noopener&quot;&gt;Constraint Validation
API&lt;/a&gt; (which is
&lt;a href=&quot;https://caniuse.com/#feat=constraint-validation&quot; rel=&quot;noopener&quot;&gt;widely supported&lt;/a&gt;) to add
custom validation using built-in browser UI to set focus and display prompts.&lt;/p&gt;
&lt;p&gt;Find out more: &lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ux/input/forms#use_javascript_for_more_complex_real-time_validation&quot; rel=&quot;noopener&quot;&gt;Use JavaScript for more complex real-time
validation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;analytics&quot;&gt;Analytics and RUM &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#analytics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&amp;quot;What you cannot measure, you cannot improve&amp;quot; is particularly true for sign-up
and sign-in forms. You need to set goals, measure success, improve your site—and
repeat.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nngroup.com/articles/discount-usability-20-years/&quot; rel=&quot;noopener&quot;&gt;Discount usability
testing&lt;/a&gt; can be
helpful for trying out changes, but you&#39;ll need real-world data to really
understand how your users experience your sign-up and sign-in forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Page analytics&lt;/strong&gt;: sign-up and sign-in page views, bounce rates,
and exits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interaction analytics&lt;/strong&gt;: &lt;a href=&quot;https://support.google.com/analytics/answer/6180923?hl=en&quot; rel=&quot;noopener&quot;&gt;goal
funnels&lt;/a&gt; (where do
users abandon your sign-in or sign-in flow?) and
&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/gtagjs/events&quot; rel=&quot;noopener&quot;&gt;events&lt;/a&gt;
(what actions do users take when interacting with your forms?)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Website performance&lt;/strong&gt;: &lt;a href=&quot;https://web.dev/user-centric-performance-metrics&quot;&gt;user-centric
metrics&lt;/a&gt; (are your sign-up and sign-in
forms slow for some reason and, if so, what is the cause?).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You may also want to consider implementing A/B testing in order to try out
different approaches to sign-up and sign-in, and staged rollouts to validate the
changes on a subset of users before releasing changes to all users.&lt;/p&gt;
&lt;h2 id=&quot;general-guidelines&quot;&gt;General guidelines &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#general-guidelines&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Well designed UI and UX can reduce sign-in form abandonment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t make users hunt for sign-in! Put a link to the sign-in form at the top
of the page, using well-understood wording such as &lt;strong&gt;Sign In&lt;/strong&gt;, &lt;strong&gt;Create Account&lt;/strong&gt;
or &lt;strong&gt;Register&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Keep it focused! Sign-up forms are not the place to distract people with
offers and other site features.&lt;/li&gt;
&lt;li&gt;Minimize sign-up complexity. Collect other user data (such as addresses or
credit card details) only when users see a clear benefit from providing that
data.&lt;/li&gt;
&lt;li&gt;Before users start on your sign-up form, make it clear what the value
proposition is. How do they benefit from signing in? Give users concrete
incentives to complete sign-up.&lt;/li&gt;
&lt;li&gt;If possible allow users to identify themselves with a mobile phone number
instead of an email address, since some users may not use email.&lt;/li&gt;
&lt;li&gt;Make it easy for users to reset their password, and make the &lt;strong&gt;Forgot your
password?&lt;/strong&gt; link obvious.&lt;/li&gt;
&lt;li&gt;Link to your terms of service and privacy policy documents: make it clear to
users from the start how you safeguard their data.&lt;/li&gt;
&lt;li&gt;Include the logo and name of your company or organization on your signup and
sign-in pages, and make sure that language, fonts and styles match the rest of
your site. Some forms don&#39;t feel like they belong to the same site as other
content, especially if they have a significantly different URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources&quot;&gt;Keep learning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sign-in-form-best-practices/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/forms/&quot;&gt;Create Amazing Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2018/08/best-practices-for-mobile-form-design/&quot; rel=&quot;noopener&quot;&gt;Best Practices For Mobile Form Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/more-capable-form-controls&quot;&gt;More capable form controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webaim.org/techniques/forms/&quot; rel=&quot;noopener&quot;&gt;Creating Accessible Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/credential-management-api/&quot; rel=&quot;noopener&quot;&gt;Streamlining the Sign-in Flow Using Credential Management API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web-otp/&quot;&gt;Verify phone numbers on the web with the WebOTP API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/photos/_XFObcM_7KU&quot; rel=&quot;noopener&quot;&gt;Meghan Schiereck&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Use cross-platform browser features to build a sign-in form</title>
    <link href="https://web.dev/codelab-sign-in-form-best-practices/"/>
    <updated>2020-06-29T00:00:00Z</updated>
    <id>https://web.dev/codelab-sign-in-form-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This codelab teaches you how to build a sign-in form that&#39;s secure, accessible, and easy to use.&lt;/p&gt;
&lt;h2 id=&quot;1-use-meaningful-html&quot;&gt;1. Use meaningful HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#1-use-meaningful-html&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use these elements built for the job:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you&#39;ll see, these elements enable built-in browser functionality, improve
accessibility, and add meaning to your markup.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click Remix to edit to make the project editable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&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;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sign in&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;h1&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Email&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 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;section&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;section&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 punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Password&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 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;section&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sign in&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;button&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;form&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&#39;s how your &lt;code&gt;index.html&lt;/code&gt; file should look at this point:&lt;/p&gt;
  &lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form-codelab-1?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form-codelab-1 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; to preview your sign-in form.
The HTML that you added is valid and correct, but the default browser styling
makes it looks terrible and hard to use, especially on mobile devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;View Source&lt;/strong&gt; to return to your source code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;2-design-for-fingers-and-thumbs&quot;&gt;2. Design for fingers and thumbs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#2-design-for-fingers-and-thumbs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adjust padding, margins, and font sizes to ensure that your inputs work well on mobile.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Copy the following CSS and paste it into your &lt;code&gt;style.css&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
  &lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form-codelab-2?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=style.css&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form-codelab-2 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;View App&lt;/strong&gt; to see your freshly styled sign-in form.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;View Source&lt;/strong&gt; to return to your &lt;code&gt;style.css&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&#39;s quite a lot of code! The main things to be aware of are the changes to sizes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;margin&lt;/code&gt; are added to inputs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-size&lt;/code&gt; is different for mobile and desktop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;:invalid&lt;/code&gt; selector is used to indicate when an input has an invalid value.
This doesn&#39;t work yet.&lt;/p&gt;
&lt;p&gt;The CSS layout is mobile-first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The default CSS is for viewports less than 450 pixels wide.&lt;/li&gt;
&lt;li&gt;The media query section sets overrides for viewports that are at least 450 pixels wide.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When building your own form like this, it&#39;s very important at this point in the process to
test your code on real devices on desktop and mobile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is label and input text readable, especially for people with low vision?&lt;/li&gt;
&lt;li&gt;Are the inputs and &lt;strong&gt;Sign in&lt;/strong&gt; button large enough to use as touch targets for thumbs?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-add-input-attributes-to-enable-built-in-browser-features&quot;&gt;3. Add input attributes to enable built-in browser features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#3-add-input-attributes-to-enable-built-in-browser-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enable the browser to store and autofill input values, and provide access to
built-in password-management features.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add attributes to your form HTML so it looks like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&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;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&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;post&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Sign in&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;email&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;Email&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;username&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autofocus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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 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;password&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;Password&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;current-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&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;sign-in&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;Sign in&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;View your app again and then click &lt;strong&gt;Email&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Notice how focus moves to the email input. This is because the label is associated with the input through the &lt;code&gt;for=&amp;quot;email&amp;quot;&lt;/code&gt; attribute. Screenreaders also
announce the label text when the label or the label&#39;s associated input gets focus.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Focus the email input on a mobile device.&lt;/p&gt;
&lt;p&gt;Notice how the keyboard is optimized for typing an email address. For example, the &lt;code&gt;@&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt; characters might be shown on the primary keyboard, and the
operating system might show stored emails above the keyboard. All of this happens because the &lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt; attribute is applied to an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;The default email keyboard on iOS.&quot; decoding=&quot;async&quot; height=&quot;1504&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/dUtDcvYy1RMzEYBi7Ce0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;/figure&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
&lt;p&gt;Type some text into the password input.&lt;/p&gt;
&lt;p&gt;The text is hidden by default because the &lt;code&gt;type=&amp;quot;password&amp;quot;&lt;/code&gt; attribute has been applied to the element.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;autocomplete&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, and &lt;code&gt;type&lt;/code&gt; attributes help browsers understand
the role of inputs in order to store data that can be used later for autofill.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;
&lt;p&gt;Focus the email input on a desktop device and type some text.&lt;/p&gt;
&lt;p&gt;You can see the URL of your app when you click &lt;strong&gt;Fullscreen&lt;/strong&gt; &lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;The Fullscreen icon&quot; /&gt;. If you stored any email addresses in your
browser, you probably see a dialog that lets you select from those stored emails. This happens because the &lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt; attribute applied to the
email input.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt; and &lt;code&gt;autocomplete=&amp;quot;current-password&amp;quot;&lt;/code&gt; help browsers use
stored values to autofill the inputs.&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; For email inputs, use &lt;code&gt;autocomplete=&amp;quot;username&amp;quot;&lt;/code&gt; because &lt;code&gt;username&lt;/code&gt; is recognized by password managers in modern browsers, even though you should use &lt;code&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt;, and you may want to use &lt;code&gt;id=&amp;quot;email&amp;quot;&lt;/code&gt; and &lt;code&gt;name=&amp;quot;email&amp;quot;&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Different browsers use &lt;a href=&quot;https://web.dev/sign-in-form-best-practices/#password-managers:~:text=Browser%20password%20and%20autofill%20systems%20are%20not%20simple&quot;&gt;different techniques&lt;/a&gt;
to work out the role of form inputs and provide autofill for a range of
different websites.&lt;/p&gt;
&lt;p&gt;Add and remove attributes to try this yourself.&lt;/p&gt;
&lt;p&gt;It&#39;s extremely important to test behavior across platforms. You should enter values
and submit the form in different browsers on different devices. It&#39;s easy to
test on a range of platforms with BrowserStack, which is &lt;a href=&quot;https://www.browserstack.com/open-source&quot; rel=&quot;noopener&quot;&gt;free for open source
projects&lt;/a&gt;. Try it!&lt;/p&gt;
&lt;p&gt;Here&#39;s how your &lt;code&gt;index.html&lt;/code&gt; file should look at this point:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form-codelab-3?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form-codelab-3 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;4-add-ui-to-toggle-password-display&quot;&gt;4. Add UI to toggle password display &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#4-add-ui-to-toggle-password-display&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Usability experts &lt;a href=&quot;https://www.nngroup.com/articles/stop-password-masking/&quot; rel=&quot;noopener&quot;&gt;strongly recommend&lt;/a&gt; the addition of an icon or button that lets users see the text that they enter in the &lt;strong&gt;Password&lt;/strong&gt; field. There&#39;s &lt;a href=&quot;https://twitter.com/sw12/status/1251191795377156099&quot; rel=&quot;noopener&quot;&gt;no built-in way to do this&lt;/a&gt;, so you need to implement it yourself with JavaScript.&lt;/p&gt;
&lt;p&gt;The code to add this functionality is straightforward. This example uses text, not an icon.&lt;/p&gt;
&lt;p&gt;Update the &lt;a href=&quot;https://glitch.com/edit/#!/sign-in-form-codelab-4?path=index.html:22:2&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://glitch.com/edit/#!/sign-in-form-codelab-4?path=style.css:34:0&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;style.css&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://glitch.com/edit/#!/sign-in-form-codelab-4?path=script.js&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;script.js&lt;/code&gt;&lt;/a&gt; files as follows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a toggle to the password section in the &lt;code&gt;index.html&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;password&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;Password&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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;button&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;toggle-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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;Show password as plain text. Warning: this will display your password on the screen.&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;Show password&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;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;current-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Add the following CSS to the bottom of the &lt;code&gt;style.css&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;button#toggle-password&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;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&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; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -4px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -2px&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 makes the &lt;strong&gt;Show password&lt;/strong&gt; button look like plain text and displays it in the top-right corner of the password section.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Add the following JavaScript to the &lt;code&gt;script.js&lt;/code&gt; file to toggle password display and set the appropriate &lt;code&gt;aria-label&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; passwordInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;password&#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; togglePasswordButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;toggle-password&#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;togglePasswordButton&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; togglePassword&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;togglePassword&lt;/span&gt;&lt;span class=&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;passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;password&#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;    passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Hide password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&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-label&#39;&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;&#39;Hide password.&#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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Show password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    togglePasswordButton&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-label&#39;&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;&#39;Show password as plain text. &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;Warning: this will display your password on the screen.&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
&lt;p&gt;Try the show password logic now.&lt;/p&gt;
&lt;p&gt;a. View your app.
b. Enter some text in the password field.
c. Click &lt;strong&gt;Show password&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repeat the fourth step on multiple browsers on different operating systems.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Think about UX design: will users notice &lt;strong&gt;Show password&lt;/strong&gt; and understand it? Is there a better way to provide this functionality? This is a good moment to try &lt;a href=&quot;https://www.nngroup.com/articles/discount-usability-20-years/&quot; rel=&quot;noopener&quot;&gt;discount usability testing&lt;/a&gt; with a small group of friends or colleagues.&lt;/p&gt;
&lt;p&gt;To understand how this functionality works for screenreaders, install the &lt;a href=&quot;https://chrome.google.com/webstore/detail/chromevox-classic-extensi/kgejglhpjiefppelpmljglcjbhoiplfn?hl=en&quot; rel=&quot;noopener&quot;&gt;ChromeVox Classic Extension&lt;/a&gt; and navigate through the form. Do the &lt;code&gt;aria-label&lt;/code&gt; values work as intended?&lt;/p&gt;
&lt;p&gt;Some websites, such as &lt;a href=&quot;https://mail.google.com/&quot; rel=&quot;noopener&quot;&gt;Gmail&lt;/a&gt;, use icons, not
text, to toggle password display. When you&#39;re done with this codelab, implement this with SVG images.
&lt;a href=&quot;https://material.io/resources/icons/?icon=visibility&quot; rel=&quot;noopener&quot;&gt;Material Design&lt;/a&gt; offers high-quality icons that you can download for free.&lt;/p&gt;
&lt;p&gt;Here&#39;s how your code should look at this point:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form-codelab-4?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=style.css&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form-codelab-4 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;5-add-form-validation&quot;&gt;5. Add form validation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#5-add-form-validation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can help users enter their data correctly when you let them validate their data before form submission and show them what they need to change.&lt;/p&gt;
&lt;p&gt;HTML form elements and attributes have built-in features for basic validation,
but you should also use JavaScript to do more robust validation while users enter data and when they attempt to submit the form.&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; Client-side validation helps users enter data and can avoid unnecessary server load, but you must always validate and sanitize data on your backend. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This step uses the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/forms.html#constraints&quot; rel=&quot;noopener&quot;&gt;Constraint Validation API&lt;/a&gt;
(which is &lt;a href=&quot;https://caniuse.com/#search=constraint%20validation&quot; rel=&quot;noopener&quot;&gt;widely supported&lt;/a&gt;)
to add custom validation with built-in browser UI that sets focus and displays prompts.&lt;/p&gt;
&lt;p&gt;Tell users the constraints for passwords and any other inputs. Don&#39;t make them guess!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the password section of the &lt;code&gt;index.html&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&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;section&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;password&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;Password&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;button&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;toggle-password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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;Show password as plain text. Warning: this will display your password on the screen.&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;Show password&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;button&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&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;current-password&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;password-constraints&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password-constraints&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;At least eight characters, with at least one lowercase and one uppercase letter.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&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;This adds two new features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Information about password constraints&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;aria-describedby&lt;/code&gt; attribute for the password input (Screenreaders read the label text, the input type (password), and then the description.)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Add the following CSS to the bottom of the &lt;code&gt;style.css&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div#password-constraints&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;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px 0 0 0&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; 16px&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;ol start=&quot;3&quot;&gt;
&lt;li&gt;Add the following JavaScript to &lt;code&gt;script.js&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;passwordInput&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;input&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resetCustomValidity&lt;span class=&quot;token punctuation&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resetCustomValidity&lt;/span&gt;&lt;span class=&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;  passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCustomValidity&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// A production site would use more stringent password testing. &lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validatePassword&lt;/span&gt;&lt;span class=&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; message&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;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 regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;{8,}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;At least eight characters. &#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 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 regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token char-class&quot;&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token range&quot;&gt;A&lt;span class=&quot;token range-punctuation operator&quot;&gt;-&lt;/span&gt;Z&lt;/span&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    message &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;At least one uppercase letter. &#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 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 regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token char-class&quot;&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token range&quot;&gt;a&lt;span class=&quot;token range-punctuation operator&quot;&gt;-&lt;/span&gt;z&lt;/span&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token char-set class-name&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    message &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;At least one lowercase letter.&#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;  passwordInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCustomValidity&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; form &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;form&#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; signinButton &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;button#sign-in&#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;form&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;submit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleFormSubmission&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                       &lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleFormSubmission&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;  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 function&quot;&gt;validatePassword&lt;/span&gt;&lt;span class=&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;  form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reportValidity&lt;/span&gt;&lt;span class=&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;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkValidity&lt;/span&gt;&lt;span class=&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 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;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 comment&quot;&gt;// On a production site do form submission.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Logging in!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    signinButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
&lt;p&gt;Try it!&lt;/p&gt;
&lt;p&gt;All recent browsers have built-in features for form validation and
support validation with JavaScript.&lt;/p&gt;
&lt;p&gt;a. Enter an invalid email address and click &lt;strong&gt;Sign in&lt;/strong&gt;. The browser displays a warning—no JavaScript required!&lt;/p&gt;
&lt;p&gt;b. Enter a valid email address, but then click &lt;strong&gt;Sign in&lt;/strong&gt; without a password value. The browser warns that you missed a required value and sets focus on the
password input.&lt;/p&gt;
&lt;p&gt;c. Enter an invalid password and click &lt;strong&gt;Sign in&lt;/strong&gt;. Now you see different messages depending on what&#39;s wrong.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try different ways to help users enter email addresses and passwords. &lt;a href=&quot;https://aerotwist.com/blog/better-password-form-fields/&quot; rel=&quot;noopener&quot;&gt;Better password form fields&lt;/a&gt;
offers some clever suggestions.&lt;/p&gt;
&lt;p&gt;Here&#39;s how your code should look at this point:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/sign-in-form-codelab-5?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=style.css&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;sign-in-form-codelab-5 on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;go-further&quot;&gt;Go further &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-sign-in-form-best-practices/#go-further&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;They&#39;re not shown in this codelab, but you still need these four crucial sign-in form features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add &lt;strong&gt;Forgot your password?&lt;/strong&gt;, a button that makes it easy for users to reset their passwords.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Link to your terms of service and privacy policy documents so that your users know how you safeguard their data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider style and branding, and ensure that these additional features match the rest of your website.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add &lt;a href=&quot;https://web.dev/sign-in-form-best-practices#analytics&quot;&gt;Analytics and RUM&lt;/a&gt; so that you can test and monitor the performance and usability of your form design.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Getting started with Trust Tokens</title>
    <link href="https://web.dev/trust-tokens/"/>
    <updated>2020-06-22T00:00:00Z</updated>
    <id>https://web.dev/trust-tokens/</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;  This API &lt;a href=&quot;https://developer.chrome.com/blog/rename-trust-tokens/&quot;&gt;has been renamed&lt;/a&gt; Private State Tokens.  The developer.chrome.com article &lt;a href=&quot;https://developer.chrome.com/docs/privacy-sandbox/trust-tokens/&quot;&gt;Private State Tokens&lt;/a&gt; provides implementation status updates, and explains how to engage and share feedback.  &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;⚠️ Warning: you may need to update your app!&lt;/strong&gt;  &lt;strong&gt;TrustTokenV3&lt;/strong&gt; is a collection of backwards-incompatible changes to Chromium&#39;s Trust Tokens implementation. The changes arrived in Chrome 92, which reached Chrome Stable towards the end of July 2021.  If you haven&#39;t already, you will need to update existing applications &lt;a href=&quot;https://www.chromestatus.com/feature/5078049450098688&quot;&gt;testing the API&lt;/a&gt;.  Find out more: &lt;a href=&quot;https://bit.ly/what-is-trusttokenv3&quot;&gt;What&#39;s TrustTokenV3?&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;bXB1Iwq6Eq4&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Trust tokens enable an origin to issue cryptographic tokens to a user it trusts. The tokens are
stored by the user&#39;s browser. The browser can then use the tokens in other contexts to evaluate the
user&#39;s authenticity.&lt;/p&gt;
&lt;p&gt;The Trust Token API enables trust of a user in one context to be conveyed to another context without
identifying the user or linking the two identities.&lt;/p&gt;
&lt;p&gt;You can try out the API with our &lt;a href=&quot;https://trust-token-demo.glitch.me/&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt;, and
&lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-89/#trust-token&quot; rel=&quot;noopener&quot;&gt;inspect tokens&lt;/a&gt; in the
Chrome DevTools &lt;strong&gt;Network&lt;/strong&gt; and &lt;strong&gt;Application&lt;/strong&gt; tabs.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing Trust Tokens in the Chrome DevTools Network tab.&quot; decoding=&quot;async&quot; height=&quot;584&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/krrI292OLd6awb4dxkN0.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Trust Tokens in the Chrome DevTools &lt;b&gt;Network&lt;/b&gt; tab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing Trust Tokens in the Chrome DevTools Application tab.&quot; decoding=&quot;async&quot; height=&quot;584&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cwR9JdoVo1M4VDovP2oM.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Trust Tokens in the Chrome DevTools &lt;b&gt;Application&lt;/b&gt; tab.&lt;/figcaption&gt;
&lt;/figure&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 Privacy Sandbox is a series of proposals to satisfy third-party use cases without third-party cookies or other tracking mechanisms. See &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox&quot;&gt;Digging into the Privacy Sandbox&lt;/a&gt; for an overview of all the proposals.  &lt;strong&gt;This proposal needs your feedback!&lt;/strong&gt; If you have comments, please &lt;a href=&quot;https://github.com/WICG/trust-token-api/issues/new&quot;&gt;create an issue&lt;/a&gt; on the &lt;a href=&quot;https://github.com/WICG/trust-token-api&quot;&gt;Trust Token explainer&lt;/a&gt; repository. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;why-do-we-need-trust-tokens&quot;&gt;Why do we need Trust Tokens? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#why-do-we-need-trust-tokens&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The web needs ways to establish trust signals which show that a user is who they say they are, and
not a bot pretending to be a human, or a malicious third-party defrauding a real person or service.
Fraud protection is particularly important for advertisers, ad providers, and CDNs.&lt;/p&gt;
&lt;p&gt;Unfortunately, many existing mechanisms to gauge and propagate trustworthiness—to work out if an
interaction with a site is from a real human, for example—take advantage of techniques that can also
be used for fingerprinting.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Fingerprinting&lt;/strong&gt; enables sites to identify and track individual users by getting data about their device, operating system, and browser setup (such as language preferences, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorID/userAgent&quot;&gt;user agent&lt;/a&gt;, and available fonts) or changes in device state. This may be done on the server by checking request headers or on the client with JavaScript.  Fingerprinting uses mechanisms that users aren&#39;t aware of and can&#39;t control. Sites such as &lt;a href=&quot;https://panopticlick.eff.org/&quot;&gt;Panopticlick&lt;/a&gt; and &lt;a href=&quot;https://amiunique.org/&quot;&gt;amiunique.org&lt;/a&gt; show how fingerprint data can be combined to identify you as an individual. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The API must preserve privacy, enabling trust to be propagated across sites without individual user
tracking.&lt;/p&gt;
&lt;h2 id=&quot;whats-in-the-trust-tokens-proposal&quot;&gt;What&#39;s in the Trust Tokens proposal? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#whats-in-the-trust-tokens-proposal&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The web relies on building trust signals to detect fraud and spamming. One way to do this is by
tracking browsing with global, cross-site per-user identifiers. For a privacy-preserving API, that&#39;s
not acceptable.&lt;/p&gt;
&lt;p&gt;From the proposal
&lt;a href=&quot;https://github.com/WICG/trust-token-api#overview&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;explainer&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This API proposes a new per-origin storage area for &quot;Privacy Pass&quot; style
cryptographic tokens, which are accessible in third party contexts. These
tokens are non-personalized and cannot be used to track users, but are
cryptographically signed so they cannot be forged.&lt;/p&gt;
&lt;p&gt;When an origin is in a context where they trust the user, they can issue
the browser a batch of tokens, which can be &quot;spent&quot; at a later time in a
context where the user would otherwise be unknown or less trusted.
Crucially, the tokens are indistinguishable from one another, preventing
websites from tracking users through them.&lt;/p&gt;
&lt;p&gt;We further propose an extension mechanism for the browser to sign outgoing
  requests with keys bound to a particular token redemption.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;sample-api-usage&quot;&gt;Sample API usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#sample-api-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following is adapted from
&lt;a href=&quot;https://github.com/WICG/trust-token-api#sample-api-usage&quot; rel=&quot;noopener&quot;&gt;sample code in the API explainer&lt;/a&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; The code in this post uses updated syntax available since Chrome 88. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Imagine that a user visits a news website (&lt;code&gt;publisher.example&lt;/code&gt;) which embeds advertising from a
third party ad network (&lt;code&gt;foo.example&lt;/code&gt;). The user has previously used a social media site that issues
trust tokens (&lt;code&gt;issuer.example&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The sequence below shows how trust tokens work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; The user visits &lt;code&gt;issuer.example&lt;/code&gt; and performs actions that lead the site to believe they
are a real human, such as account activity, or passing a CAPTCHA challenge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;code&gt;issuer.example&lt;/code&gt; verifies the user is a human, and runs the following JavaScript to
issue a trust token to the user&#39;s 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 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;https://issuer.example/trust-token&#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;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;token-request&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://issuer.example&#39;&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 function&quot;&gt;then&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; The user&#39;s browser stores the trust token, associating it with &lt;code&gt;issuer.example&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Some time later, the user visits &lt;code&gt;publisher.example&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; &lt;code&gt;publisher.example&lt;/code&gt; wants to know if the user is a real human. &lt;code&gt;publisher.example&lt;/code&gt; trusts
&lt;code&gt;issuer.example&lt;/code&gt;, so they check if the user&#39;s browser has valid tokens from that origin:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasTrustToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://issuer.example&#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;&lt;strong&gt;6.&lt;/strong&gt; If this returns a promise that resolves to &lt;code&gt;true&lt;/code&gt;, that means the user has tokens from
&lt;code&gt;issuer.example&lt;/code&gt;, so &lt;code&gt;publisher.example&lt;/code&gt; can attempt to redeem a token:&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;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://issuer.example/trust-token&#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;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;token-redemption&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://issuer.example&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;refreshPolicy&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;none&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; refresh&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;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 operator&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 code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The redeemer &lt;code&gt;publisher.example&lt;/code&gt; requests a redemption.&lt;/li&gt;
&lt;li&gt;If the redemption is successful, the issuer &lt;code&gt;issuer.example&lt;/code&gt; returns a redemption record which
indicates that at some point they issued a valid token to this browser.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Once the promise returned by &lt;code&gt;fetch()&lt;/code&gt; has resolved, the redemption record can be used
in subsequent resource requests:&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;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://foo.example/get-content&#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;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;send-redemption-record&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;issuers&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;https://issuer.example&#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 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With this code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Redemption records are included as a request header &lt;code&gt;Sec-Redemption-Record&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.example&lt;/code&gt; receives the redemption record and can parse the record to determine whether
&lt;code&gt;issuer.example&lt;/code&gt; thought this user was a human.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.example&lt;/code&gt; responds accordingly.&lt;/li&gt;
&lt;/ol&gt;
&lt;details&gt;
&lt;summary&gt;
  How can a website work out whether to trust you?
&lt;/summary&gt;
You might have shopping history with an e-commerce site, check-ins on a location platform, or account
history at a bank. Issuers might also look at other factors such as how long you&#39;ve had an account,
or other interactions (such as CAPTCHAs or form submission) that increase the issuer&#39;s trust in the
likelihood that you&#39;re a real human.
&lt;/details&gt;
&lt;h3 id=&quot;trust-token-issuance&quot;&gt;Trust token issuance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#trust-token-issuance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If the user is deemed to be trustworthy by a trust token issuer such as &lt;code&gt;issuer.example&lt;/code&gt;, the issuer
can fetch trust tokens for the user by making a &lt;code&gt;fetch()&lt;/code&gt; request with a &lt;code&gt;trustToken&lt;/code&gt; parameter:&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;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;issuer.example/trust-token&#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;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;token-request&#39;&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 function&quot;&gt;then&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This invokes an extension of the &lt;a href=&quot;https://privacypass.github.io/&quot; rel=&quot;noopener&quot;&gt;Privacy Pass&lt;/a&gt; issuance
protocol using a &lt;a href=&quot;https://eprint.iacr.org/2020/072.pdf&quot; rel=&quot;noopener&quot;&gt;new cryptographic primitive&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate a set of pseudo-random numbers known as &lt;em&gt;nonces&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blind the nonces (encode them so the issuer can&#39;t view their contents) and attach them to the
request in a &lt;code&gt;Sec-Trust-Token&lt;/code&gt; header.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Send a POST request to the endpoint provided.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The endpoint responds with &lt;a href=&quot;https://en.wikipedia.org/wiki/Blind_signature&quot; rel=&quot;noopener&quot;&gt;blinded tokens&lt;/a&gt;
(signatures on the blind nonces), then the tokens are unblinded and stored internally together with
the associated nonces by the browser as trust tokens.&lt;/p&gt;
&lt;h3 id=&quot;trust-token-redemption&quot;&gt;Trust token redemption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#trust-token-redemption&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A publisher site (such as &lt;code&gt;publisher.example&lt;/code&gt; in the example above) can check if there are trust
tokens available for the user:&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; userHasTokens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasTrustToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;issuer.example/trust-token&#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 there are tokens available, the publisher site can redeem them to get a redemption record:&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;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;issuer.example/trust-token&#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 operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;token-redemption&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;refreshPolicy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&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 operator&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;then&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The publisher can include redemption records in requests that require a trust token—such as
posting a comment, liking a page, or voting in a poll—by using a &lt;code&gt;fetch()&lt;/code&gt; call like
the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token 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;https://foo.example/post-comment&#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 operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;trustToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;send-redemption-record&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;issuers&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;issuer.example/trust-token&#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 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 operator&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;then&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Redemption records are included as a &lt;code&gt;Sec-Redemption-Record&lt;/code&gt; request header.&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; Trust tokens are only accessible through options to Fetch, XHR, and the HTML &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element: they cannot be accessed directly. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;privacy-considerations&quot;&gt;Privacy considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#privacy-considerations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tokens are designed to be &#39;unlinkable&#39;. An issuer can learn aggregate information about which sites
its users visit, but can&#39;t link issuance with redemption: when a user redeems a token, the issuer
can&#39;t tell the token apart from other tokens it has created. However, trust tokens currently do not
exist in a vacuum: there are other ways an issuer could currently—in theory—join a user&#39;s identity
across sites, such as third-party cookies and covert tracking techniques. It is important for sites
to understand this ecosystem transition as they plan their support. This is a general aspect of the
transition for many Privacy Sandbox APIs, so not discussed further here.&lt;/p&gt;
&lt;h3 id=&quot;security-considerations&quot;&gt;Security considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#security-considerations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Trust token exhaustion:&lt;/strong&gt; a malicious site could deliberately deplete a user&#39;s supply of tokens
from a particular issuer. There are several mitigations against this kind of attack, such as
enabling issuers to provide many tokens at once, so users have an adequate supply of ensuring
browsers only ever redeem one token per top-level page view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Double-spend prevention:&lt;/strong&gt; malware might attempt to access all of a user&#39;s trust tokens. However,
tokens will run out over time, since every redemption is sent to the same token issuer, which can
verify that each token is used only once. To mitigate risk, issuers could also sign fewer tokens.&lt;/p&gt;
&lt;h3 id=&quot;request-mechanisms&quot;&gt;Request mechanisms &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#request-mechanisms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It might be possible to allow for sending redemption records outside of &lt;code&gt;fetch()&lt;/code&gt;, for example with
navigation requests. Sites might also be able to include issuer data in HTTP response headers to
enable token redemption in parallel with page loading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To reiterate: this proposal needs your feedback!&lt;/strong&gt; If you have comments, please
&lt;a href=&quot;https://github.com/WICG/trust-token-api/issues/new&quot; rel=&quot;noopener&quot;&gt;create an issue&lt;/a&gt; on the
Trust Token &lt;a href=&quot;https://github.com/WICG/trust-token-api&quot; rel=&quot;noopener&quot;&gt;explainer repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/trust-tokens/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://trust-token-demo.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Trust Tokens demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/origin-trials/&quot; rel=&quot;noopener&quot;&gt;Getting started with Chrome&#39;s origin trials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/&quot;&gt;Digging in to the Privacy Sandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/trust-token-api&quot; rel=&quot;noopener&quot;&gt;Trust Token API Explainer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/updates/trust-token&quot; rel=&quot;noopener&quot;&gt;Chromium Projects: Trust Token API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://groups.google.com/a/chromium.org/g/blink-dev/c/X9sF2uLe9rA/m/1xV5KEn2DgAJ&quot; rel=&quot;noopener&quot;&gt;Intent to Implement: Trust Token API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromestatus.com/feature/5078049450098688&quot; rel=&quot;noopener&quot;&gt;Chrome Platform Status&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://privacypass.github.io/&quot; rel=&quot;noopener&quot;&gt;Privacy Pass&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eprint.iacr.org/2020/072.pdf&quot; rel=&quot;noopener&quot;&gt;Extensions of Privacy Pass&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Thanks to all those who helped write and review this post.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/photos/b4D7FKAghoE&quot; rel=&quot;noopener&quot;&gt;ZSun Fu&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?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>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Digging into the Privacy Sandbox</title>
    <link href="https://web.dev/digging-into-the-privacy-sandbox/"/>
    <updated>2020-04-08T00:00:00Z</updated>
    <id>https://web.dev/digging-into-the-privacy-sandbox/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;WnCKlNE52tc&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This post outlines APIs and concepts from the &lt;a href=&quot;https://www.chromium.org/Home/chromium-privacy/privacy-sandbox&quot; rel=&quot;noopener&quot;&gt;Privacy Sandbox&lt;/a&gt; proposals.&lt;/li&gt;
&lt;li&gt;The proposal authors are inviting feedback from the community, particularly from those in the advertising space (publishers, advertisers, and ad tech companies), to suggest missing use cases and share information about how to support your business use cases.&lt;/li&gt;
&lt;li&gt;You can comment on the proposals by filing issues on &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#proposals&quot;&gt;the repositories linked to below&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;There&#39;s a &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary&quot;&gt;glossary&lt;/a&gt; for the proposals at the end of this post.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;background&quot;&gt;The current state of privacy on the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Websites use services from other companies to provide analytics, serve video and do lots of other useful stuff. Composability is one of the web&#39;s &lt;a href=&quot;https://youtu.be/WnCKlNE52tc?t=930&quot; rel=&quot;noopener&quot;&gt;superpowers&lt;/a&gt;. Most notably, ads are included in web pages via third-party JavaScript and iframes. Ad views, clicks and conversions are tracked via third-party cookies and scripts.&lt;/p&gt;
&lt;p&gt;However, when you visit a website you may not be aware of the third parties involved and what they&#39;re doing with your data. Even publishers and web developers may not understand the entire third-party supply chain.&lt;/p&gt;
&lt;p&gt;Ad selection, conversion measurement, and other use cases currently rely on establishing stable cross-site user identity. Historically this has been done with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Cookies#Third-party_cookies&quot; rel=&quot;noopener&quot;&gt;third-party cookies&lt;/a&gt;, but browsers have begun to restrict access to these cookies. There has also been an &lt;a href=&quot;https://github.com/bslassey/privacy-budget/issues/6&quot; rel=&quot;noopener&quot;&gt;increase in the use of other mechanisms&lt;/a&gt; for cross-site user tracking, such as covert browser storage, device fingerprinting, and requests for personal information such as email addresses.&lt;/p&gt;
&lt;p&gt;This is a dilemma for the web. How can legitimate third-party use cases be supported without enabling users to be tracked across sites?&lt;/p&gt;
&lt;p&gt;In particular, how can websites fund content by enabling third parties to show ads and measure ad performance—but not allow individual users to be profiled? How can advertisers and site owners evaluate a user&#39;s authenticity without resorting to dark patterns such as device fingerprinting?&lt;/p&gt;
&lt;p&gt;The way things work at the moment can be problematic for the entire web ecosystem, not just users. For publishers and advertisers, tracking identity and using a variety of non-standard third-party solutions can add to technical debt, code complexity, and data risk. Users, developers, publishers, and advertisers should be confident that the web is protecting user privacy choices.&lt;/p&gt;
&lt;p&gt;Advertising is a core web business model for the internet, but advertising has to work for everyone. Which brings us to the Privacy Sandbox&#39;s mission: to create a thriving web ecosystem that is respectful of users and private by default.&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introducing the Privacy Sandbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.blog.google/products/chrome/building-a-more-private-web/&quot; rel=&quot;noopener&quot;&gt;Privacy Sandbox&lt;/a&gt; introduces a set of privacy-preserving APIs to support business models that fund the open web in the absence of tracking mechanisms like third-party cookies.&lt;/p&gt;
&lt;p&gt;The Privacy Sandbox APIs require web browsers to take on a new role. Rather than working with limited tools and protections, the APIs enable the user&#39;s browser to act on the user&#39;s behalf—locally, on their device—to protect the user&#39;s identifying information as they navigate the web. The APIs enable use cases such as ad selection and conversion measurement, without revealing individual private and personal information. In engineering terms a &lt;a href=&quot;https://web.dev/browser-sandbox&quot;&gt;sandbox&lt;/a&gt; is a protected environment; a key principle of the Privacy Sandbox is that a user&#39;s personal information should be protected and not shared in a way that lets the user be identified across sites.&lt;/p&gt;
&lt;p&gt;This is a shift in direction for browsers. The Privacy Sandbox&#39;s vision of the future has browsers providing specific tools to satisfy specific use cases, while preserving user privacy. &lt;a href=&quot;https://github.com/michaelkleber/privacy-model&quot; rel=&quot;noopener&quot;&gt;A Potential Privacy Model for the Web&lt;/a&gt; sets out core principles behind the APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To establish the range of web activity across which the user&#39;s browser can let websites treat a person as having a single identity.&lt;/li&gt;
&lt;li&gt;To identify the ways in which information can move across identity boundaries without compromising that separation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;proposals&quot;&gt;The Privacy Sandbox proposals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#proposals&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to successfully transition away from third-party cookies the Privacy Sandbox initiative needs your support. The proposal &lt;a href=&quot;https://blog.chromium.org/2019/08/potential-uses-for-privacy-sandbox.html&quot; rel=&quot;noopener&quot;&gt;explainers&lt;/a&gt; need feedback from developers as well as publishers, advertisers, and ad technology companies, to suggest missing use cases and share information about how to accomplish their goals in a privacy-safe way.&lt;/p&gt;
&lt;p&gt;You can comment on the proposal explainers by filing issues against each repository:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/michaelkleber/privacy-model&quot; rel=&quot;noopener&quot;&gt;Privacy Model for the Web&lt;/a&gt;&lt;br /&gt;
Establish the range of web activity across which the user&#39;s browser can let websites treat a person as having a single identity. Identify the ways in which information can move across identity boundaries without compromising that separation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/privacy-budget&quot; rel=&quot;noopener&quot;&gt;Privacy Budget&lt;/a&gt;&lt;br /&gt;
Limit the total amount of potentially identifiable data that sites can access. Update APIs to reduce the amount of potentially identifiable data revealed. Make access to potentially identifiable data measurable.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/ip-blindness&quot; rel=&quot;noopener&quot;&gt;Gnatcatcher&lt;/a&gt;&lt;br /&gt;
Limit the ability to identify individual users by accessing their IP address.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dvorak42/trust-token-api&quot; rel=&quot;noopener&quot;&gt;Trust Token API&lt;/a&gt;&lt;br /&gt;
Enable an origin that trusts a user to issue them with cryptographic tokens which are stored by the user&#39;s browser so they can be used in other contexts to evaluate the user&#39;s authenticity.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/krgovind/first-party-sets/&quot; rel=&quot;noopener&quot;&gt;First-Party Sets&lt;/a&gt;&lt;br /&gt;
Allow related domain names owned by the same entity to declare themselves as belonging to the same first party.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/csharrison/aggregate-reporting-api&quot; rel=&quot;noopener&quot;&gt;Aggregated Reporting&lt;/a&gt;&lt;br /&gt;
Provide privacy preserving mechanisms to support a variety of use cases such as view-through-conversion, brand, lift, and reach measurement.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/conversion-measurement-api&quot; rel=&quot;noopener&quot;&gt;Attribution Reporting&lt;/a&gt;&lt;br /&gt;
Provide privacy preserving measurement of clicks and views with event-level and aggregate reports.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jkarlin/topics&quot; rel=&quot;noopener&quot;&gt;Topics API&lt;/a&gt;&lt;br /&gt;
Enable interest-based advertising, without having to resort to tracking the sites a user visits. The design of the API was informed by community feedback from our earlier FLoC trials, and supersedes the &lt;a href=&quot;https://github.com/WICG/floc&quot; rel=&quot;noopener&quot;&gt;FLoC proposal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/turtledove/blob/main/FLEDGE.md&quot; rel=&quot;noopener&quot;&gt;FLEDGE&lt;/a&gt;&lt;br /&gt;
Provides a solution for remarketing use cases, designed so it cannot be used by third parties to track user browsing behaviour across sites.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can dive into the API proposal explainers right away, and over the coming months we&#39;ll be publishing posts about each proposal individually.&lt;/p&gt;
&lt;p&gt;We&#39;ll also be adding to our playlist of five-minute videos that give a simple explanation of each API.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/videoseries?list=PLNYkxOF6rcICntazGfSVKSj5EwuR9w5Nv&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot;max-width: 100% !important&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;use-cases-and-goals&quot;&gt;Use cases and goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#use-cases-and-goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;measure-conversion&quot;&gt;Measure conversion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#measure-conversion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Enable advertisers to measure ad performance.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/csharrison/conversion-measurement-api&quot; rel=&quot;noopener&quot;&gt;Attribution Reporting&lt;/a&gt; API enables the measurement of two events that are linked together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An event on a publisher&#39;s website, such as a user viewing or clicking an ad.&lt;/li&gt;
&lt;li&gt;A subsequent conversion on an advertiser site.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This API supports &lt;strong&gt;click-through&lt;/strong&gt; and &lt;strong&gt;view-through&lt;/strong&gt; measurement.&lt;/p&gt;
&lt;p&gt;Other features in this API include cross-device attribution reporting and app-to-web attribution reporting.&lt;/p&gt;
&lt;p&gt;The API also offers two types of &lt;strong&gt;attribution reports&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Event-level reports&lt;/strong&gt; associate a particular ad click or view (on the ad side) with data on the conversion side. To preserve user privacy, by preventing the joining of user identity across sites, conversion-side data is very limited, and the data is &#39;noised&#39; (meaning that for a small percentage of cases, random data is sent). As an extra privacy protection, reports are not sent immediately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Aggregate reports&lt;/strong&gt; are not tied to a specific event on the ad side. These reports provide richer, higher-fidelity conversion data than event-level reports. A combination of privacy techniques across cryptography, distribution of trust, and differential privacy help reduce the risk of identity joining across sites.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both report types can be used simultaneously: they&#39;re complementary.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting-introduction/#status&quot; rel=&quot;noopener&quot;&gt;Introduction to Attribution Reporting&lt;/a&gt; explains more about the status of these features and how to try this API.&lt;/p&gt;
&lt;h3 id=&quot;select-ads&quot;&gt;Select ads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#select-ads&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Enable advertisers to display ads relevant to users.&lt;/p&gt;
&lt;p&gt;Relevant ads are &lt;a href=&quot;https://services.google.com/fh/files/misc/disabling_third-party_cookies_publisher_revenue.pdf&quot; rel=&quot;noopener&quot;&gt;more favorable to users and more profitable for publishers&lt;/a&gt; (the people running ad-supported websites). Third party ad selection tools make ad space more valuable to advertisers (the people who purchase ad space on websites) which in turn increases revenue for ad-supported websites and enables content to get created and published.&lt;/p&gt;
&lt;p&gt;There are many ways to make ads relevant to the user, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;First-party-data&lt;/strong&gt;: Show ads relevant to topics a person has told a website they have an interest in, or content a person has looked at previously on the current website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contextual&lt;/strong&gt;: Choose where to display ads based on site content. For example, &#39;Put this ad next to articles about knitting.&#39;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remarketing&lt;/strong&gt;: Advertise to people who&#39;ve already visited your site, while they are not on your site. For example, &#39;Show this ad for discount wool to people who visited your store and left knitting items in their shopping cart—while they&#39;re visiting craft sites.&#39;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interest-based&lt;/strong&gt;: Select ads based on a user&#39;s browsing history. For example, &#39;Show this ad to users whose browsing behaviour indicates they might be interested in knitting&#39;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First-party-data and contextual ad selection can be achieved without knowing anything about the user other than their activity within a site. These techniques don&#39;t require cross-site tracking.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#remarketing&quot;&gt;Remarketing&lt;/a&gt; is usually done by using cookies or some other way to recognize people across websites: adding users to lists and then selecting specific ads to show them.&lt;/p&gt;
&lt;p&gt;Interest-based ad selection currently uses cookies to track user behaviour across as many sites as possible. Many people are concerned about the privacy implications of ad selection. The Privacy Sandbox proposes two alternatives, for remarketing and for interest-based selection:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/WICG/turtledove/blob/main/FLEDGE.md&quot; rel=&quot;noopener&quot;&gt;FLEDGE&lt;/a&gt;: for &lt;strong&gt;remarketing use cases&lt;/strong&gt;.&lt;br /&gt;
The API is designed so it cannot be used by third parties to track user browsing behaviour: the user&#39;s browser, not the advertiser or ad tech platform, stores advertiser-defined interest groups that the user&#39;s browser is associated with. The user&#39;s browser combines interest group data with ad buyer/seller data and business logic to conduct an &amp;quot;auction&amp;quot; locally on the user&#39;s device to select an ad, rather than sharing data with a third party.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jkarlin/topics&quot; rel=&quot;noopener&quot;&gt;Topics API&lt;/a&gt;: for &lt;strong&gt;interest-based audiences&lt;/strong&gt;.&lt;br /&gt;
Enable interest-based advertising, without having to resort to tracking the sites a user visits. The API proposes using machine learning to infer topics from hostnames, and a JavaScript API that returns coarse-grained topics a user might currently be interested in, based on the hostnames of sites recently visited.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;combat-fingerprinting&quot;&gt;Combat fingerprinting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#combat-fingerprinting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Reduce the amount of potentially identifiable data revealed by APIs and make access to potentially identifiable data controllable by users, and measurable.&lt;/p&gt;
&lt;p&gt;Browsers have taken steps to &lt;a href=&quot;https://blog.chromium.org/2020/01/building-more-private-web-path-towards.html&quot; rel=&quot;noopener&quot;&gt;deprecate third-party cookies&lt;/a&gt;, but techniques to identify and track the behaviour of individual users, known as fingerprinting, have continued to evolve. Fingerprinting uses mechanisms that users aren&#39;t aware of and can&#39;t control.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/bslassey/privacy-budget&quot; rel=&quot;noopener&quot;&gt;Privacy Budget&lt;/a&gt; proposal aims to limit the potential for fingerprinting by identifying how much fingerprint data is exposed by JavaScript APIs or other &#39;surfaces&#39; (such as HTTP request headers) and setting a limit on how much of this data can be accessed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fingerprinting surfaces such as the &lt;a href=&quot;https://github.com/WICG/ua-client-hints&quot; rel=&quot;noopener&quot;&gt;User-Agent&lt;/a&gt; header will be reduced in scope, and the data made available by alternative mechanisms such as &lt;a href=&quot;https://wicg.github.io/ua-client-hints/&quot; rel=&quot;noopener&quot;&gt;Client Hints&lt;/a&gt; will be subject to Privacy Budget limits. Other surfaces, such as the &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1018180&quot; rel=&quot;noopener&quot;&gt;device orientation&lt;/a&gt; and &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=661792&quot; rel=&quot;noopener&quot;&gt;battery-level&lt;/a&gt; APIs, will be updated to keep the information exposed to a minimum.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ip-address-security&quot;&gt;IP address security &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#ip-address-security&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Control access to IP addresses to reduce covert fingerprinting, and allow sites to opt out of seeing IP addresses in order to not consume &lt;a href=&quot;https://github.com/bslassey/privacy-budget&quot; rel=&quot;noopener&quot;&gt;privacy budget&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A user&#39;s IP address is the public &#39;address&#39; of their computer on the internet, which in most cases is dynamically assigned by the network through which they connect to the internet. However, even dynamic IP addresses may remain stable over a significant period of time. Not surprisingly, this means that IP addresses are a significant source of fingerprint data.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/bslassey/ip-blindness&quot; rel=&quot;noopener&quot;&gt;Gnatcatcher&lt;/a&gt; proposal is an attempt to provide a
privacy-preserving approach that avoids consuming privacy budget, while ensuring that sites
requiring access to IP addresses for legitimate purposes such as abuse prevention can do so, subject
to certification and auditing.&lt;/p&gt;
&lt;p&gt;There are two parts to the proposal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/ip-blindness/blob/master/willful_ip_blindness.md&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Willful IP Blindness&lt;/strong&gt;&lt;/a&gt;
provides a way for websites to let browsers know they are not connecting IP addresses with users.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/ip-blindness/blob/master/near_path_nat.md&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Near-path NAT&lt;/strong&gt;&lt;/a&gt; allows
groups of users to send their traffic through the same privatizing server, effectively hiding their
IP addresses from a site host.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;combat-spam,-fraud-and-denial-of-service-attacks&quot;&gt;Combat spam, fraud and denial-of-service attacks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#combat-spam,-fraud-and-denial-of-service-attacks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Verify user authenticity without fingerprinting.&lt;/p&gt;
&lt;p&gt;Anti-fraud protection is crucial for keeping users safe, and to ensure that advertisers and site owners can get accurate ad performance measurements. Advertisers and site owners must be able to distinguish between malicious bots and authentic users. If advertisers can&#39;t reliably tell which ad clicks are from real humans, they spend less, so site publishers get less revenue. Many third party services currently use techniques such as &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-fingerprinting&quot;&gt;device fingerprinting&lt;/a&gt; to combat fraud.&lt;/p&gt;
&lt;p&gt;Unfortunately, the techniques used to identify legitimate users and block spammers, fraudsters, and bots work in ways similar to &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-fingerprinting&quot;&gt;fingerprinting&lt;/a&gt; techniques that damage privacy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/dvorak42/trust-token-api&quot; rel=&quot;noopener&quot;&gt;Trust Tokens API&lt;/a&gt; proposes an alternative approach, allowing authenticity established for a user in one context, such as a social media site, to be conveyed to another context, such as an ad running on a news site—without identifying the user or linking the two identities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;enable-domains-to-belong-to-the-same-first-party&quot;&gt;Enable domains to belong to the same first party &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#enable-domains-to-belong-to-the-same-first-party&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Enable entities to declare that related domain names are owned by the same first party.&lt;/p&gt;
&lt;p&gt;Many organizations own sites across multiple domains. This can become a problem if restrictions are imposed on tracking user identity across sites that are seen as &#39;third-party&#39; but actually belong to the same organization.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/krgovind/first-party-sets/&quot; rel=&quot;noopener&quot;&gt;First Party Sets&lt;/a&gt; aims to make the web&#39;s concept of first and third parties more closely aligned with the real world&#39;s by enabling multiple domains to declare themselves as belonging to the same first party.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;explainers&quot;&gt;Privacy Sandbox proposal explainers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#explainers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Privacy Sandbox initiative needs your support. The API proposal &lt;a href=&quot;https://blog.chromium.org/2019/08/potential-uses-for-privacy-sandbox.html&quot; rel=&quot;noopener&quot;&gt;explainers&lt;/a&gt; need feedback, in particular to suggest missing use cases and more-private ways to accomplish their goals.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/privacy-budget&quot; rel=&quot;noopener&quot;&gt;Privacy Budget&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dvorak42/trust-token-api&quot; rel=&quot;noopener&quot;&gt;Trust Token API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bslassey/ip-blindness&quot; rel=&quot;noopener&quot;&gt;Gnatcatcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/csharrison/aggregate-reporting-api&quot; rel=&quot;noopener&quot;&gt;Aggregated Reporting API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/csharrison/conversion-measurement-api&quot; rel=&quot;noopener&quot;&gt;Conversion measurement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jkarlin/topics&quot; rel=&quot;noopener&quot;&gt;Topics API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/turtledove/blob/main/FLEDGE.md&quot; rel=&quot;noopener&quot;&gt;FLEDGE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/michaelkleber/privacy-model&quot; rel=&quot;noopener&quot;&gt;A Potential Privacy Model for the Web&lt;/a&gt; sets out the core principles underlying the APIs.&lt;/p&gt;
&lt;h3 id=&quot;the-privacy-sandbox&quot;&gt;The Privacy Sandbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#the-privacy-sandbox&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Technical overview: &lt;a href=&quot;https://www.chromium.org/Home/chromium-privacy/privacy-sandbox&quot; rel=&quot;noopener&quot;&gt;The Privacy Sandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Non-technical overview: &lt;a href=&quot;https://www.privacysandbox.com/&quot; rel=&quot;noopener&quot;&gt;privacysandbox.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Project principles: &lt;a href=&quot;https://www.blog.google/products/chrome/building-a-more-private-web/&quot; rel=&quot;noopener&quot;&gt;Building a more private web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.chromium.org/2019/10/developers-get-ready-for-new.html&quot; rel=&quot;noopener&quot;&gt;The future of third-party cookies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;discussion-and-participation&quot;&gt;Discussion and participation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#discussion-and-participation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/privacy-sandbox-participate/&quot; rel=&quot;noopener&quot;&gt;How to participate in the Privacy Sandbox initiative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/privacy-sandbox-update-2021-jan/&quot; rel=&quot;noopener&quot;&gt;Progress update on the Privacy Sandbox initiative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/privacy-sandbox-dev-support&quot; rel=&quot;noopener&quot;&gt;Privacy Sandbox Developer Support&lt;/a&gt;: join discussions or ask questions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-cases-policies-requirements&quot;&gt;Use cases, policies, and requirements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#use-cases-policies-requirements&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/web-advertising/blob/master/support_for_advertising_use_cases.md&quot; rel=&quot;noopener&quot;&gt;Advertising Use Cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.mozilla.org/Security/Anti_tracking_policy&quot; rel=&quot;noopener&quot;&gt;Mozilla anti-tracking policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webkit.org/tracking-prevention-policy/&quot; rel=&quot;noopener&quot;&gt;WebKit tracking prevention policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webkit.org/blog/8943/privacy-preserving-ad-click-attribution-for-the-web/&quot; rel=&quot;noopener&quot;&gt;Privacy Preserving Ad Click Attribution For the Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://brave.com/brave-fingerprinting-and-privacy-budgets/&quot; rel=&quot;noopener&quot;&gt;Brave, Fingerprinting, and Privacy Budgets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;glossary&quot;&gt;Appendix: Glossary of terms used in the proposal explainers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;glossary-ctr&quot;&gt;Click-through rate (CTR) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-ctr&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The ratio of users who click on an ad, having seen it. (See also &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-impression&quot;&gt;impression&lt;/a&gt;.)&lt;/p&gt;
&lt;h3 id=&quot;glossary-ctc&quot;&gt;Click-through-conversion (CTC) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-ctc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A conversion attributed to an ad that was &#39;clicked&#39;.&lt;/p&gt;
&lt;h3 id=&quot;conversion&quot;&gt;Conversion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#conversion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The completion of an action on an advertiser&#39;s website by a user who has previously interacted with an ad from that advertiser. For example, purchase of a product or sign-up for a newsletter after clicking an ad that links to the advertiser&#39;s site.&lt;/p&gt;
&lt;h3 id=&quot;differential-privacy&quot;&gt;Differential privacy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#differential-privacy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Share information about a dataset to reveal patterns of behaviour without revealing private information about individuals or whether they belong to the dataset.&lt;/p&gt;
&lt;h3 id=&quot;domain&quot;&gt;Domain &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#domain&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-tld&quot;&gt;Top-Level Domain&lt;/a&gt; and &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-etld&quot;&gt;eTLD&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;glossary-etld&quot;&gt;eTLD, eTLD+1 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-etld&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&#39;Effective&#39; top level domains are defined by the &lt;a href=&quot;https://publicsuffix.org/list/&quot; rel=&quot;noopener&quot;&gt;Public Suffix List&lt;/a&gt;. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;co.uk&lt;br /&gt;appspot.com&lt;br /&gt;glitch.me&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Effective TLDs are what enable foo.appspot.com to be a different site from bar.appspot.com. The effective top-level domain (&lt;strong&gt;eTLD&lt;/strong&gt;) in this case is appspot.com, and the whole &lt;strong&gt;site&lt;/strong&gt; name (foo.appspot.com, bar.appspot.com) is known as the &lt;strong&gt;eTLD+1&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;See also &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-tld&quot;&gt;Top-Level Domain&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;entropy&quot;&gt;Entropy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#entropy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A measure of how much an item of data reveals individual identity.&lt;/p&gt;
&lt;p&gt;Data entropy is measured in bits. The more that data reveals identity, the higher its entropy value.&lt;/p&gt;
&lt;p&gt;Data can be combined to identify an individual, but it can be difficult to work out whether new data adds to entropy. For example, knowing a person is from Australia doesn&#39;t reduce entropy if you already know the person is from Kangaroo Island.&lt;/p&gt;
&lt;h3 id=&quot;glossary-fingerprinting&quot;&gt;Fingerprinting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-fingerprinting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Techniques to identify and track the behaviour of individual users. Fingerprinting uses mechanisms that users aren&#39;t aware of and can&#39;t control. Sites such as &lt;a href=&quot;https://panopticlick.eff.org/&quot; rel=&quot;noopener&quot;&gt;Panopticlick&lt;/a&gt; and &lt;a href=&quot;https://amiunique.org/&quot; rel=&quot;noopener&quot;&gt;amiunique.org&lt;/a&gt; show how fingerprint data can be combined to identify you as an individual.&lt;/p&gt;
&lt;h3 id=&quot;glossary-fingerprinting-surface&quot;&gt;Fingerprinting surface &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-fingerprinting-surface&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Something that can be used (probably in combination with other surfaces) to identify a particular user or device. For example, the &lt;code&gt;navigator.userAgent()&lt;/code&gt; JavaScript method and the &lt;code&gt;User-Agent&lt;/code&gt; HTTP request header provide access to a fingerprinting surface (the user agent string).&lt;/p&gt;
&lt;h3 id=&quot;glossary-first-party&quot;&gt;First-party &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-first-party&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Resources from the site you&#39;re visiting. For example, the page you&#39;re reading is on the site web.dev and includes resources from that site. See also &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-third-party&quot;&gt;Third-party&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;glossary-impression&quot;&gt;Impression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-impression&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;View of an ad. (See also &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-ctr&quot;&gt;click-through rate&lt;/a&gt;.)&lt;/p&gt;
&lt;h3 id=&quot;k-anonymity&quot;&gt;k-anonymity &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#k-anonymity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A measure of anonymity within a data set. If you have &lt;em&gt;k&lt;/em&gt; anonymity, you can&#39;t be distinguished from &lt;em&gt;k-1&lt;/em&gt; other individuals in the data set. In other words, &lt;em&gt;k&lt;/em&gt; individuals have the same information (including you).&lt;/p&gt;
&lt;h3 id=&quot;nonce&quot;&gt;Nonce &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#nonce&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Arbitrary number used once only in cryptographic communication.&lt;/p&gt;
&lt;h3 id=&quot;origin&quot;&gt;Origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#origin&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The origin of a request, including the server name but no path information. For example: &lt;code&gt;https://web.dev&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;glossary-passive-surface&quot;&gt;Passive surface &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-passive-surface&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some fingerprinting surfaces, such as user agent strings, IP addresses and accept-language headers, are available to every website whether the site asks for them or not. That means passive surfaces can easily consume a site&#39;s privacy budget.&lt;/p&gt;
&lt;p&gt;The Privacy Sandbox initiative proposes replacing passive surfaces with active ways to get specific information, for example using Client Hints a single time to get the user&#39;s language rather than having an accept-language header for every response to every server.&lt;/p&gt;
&lt;h3 id=&quot;publisher&quot;&gt;Publisher &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#publisher&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Privacy Sandbox proposal explainers are mostly about ads, so the kinds of publishers referred to are ones that put ads on their websites.&lt;/p&gt;
&lt;h3 id=&quot;reach&quot;&gt;Reach &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#reach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The total number of people who see an ad.&lt;/p&gt;
&lt;h3 id=&quot;remarketing&quot;&gt;Remarketing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#remarketing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Advertising to people who&#39;ve already visited your site. For example, an online store could show ads for a toy sale to people who previously viewed toys on their site.&lt;/p&gt;
&lt;h3 id=&quot;site&quot;&gt;Site &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#site&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-tld&quot;&gt;Top-Level Domain&lt;/a&gt; and &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-etld&quot;&gt;eTLD&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;surface&quot;&gt;Surface &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#surface&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-fingerprinting-surface&quot;&gt;Fingerprinting surface&lt;/a&gt; and &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-passive-surface&quot;&gt;Passive surface&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;glossary-third-party&quot;&gt;Third-party &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-third-party&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Resources served from a domain that&#39;s different from the website you&#39;re visiting. For example, a website foo.com might use analytics code from google-analytics.com (via JavaScript), fonts from use.typekit.net (via a link element) and a video from vimeo.com (in an iframe). See also &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-first-party&quot;&gt;First-party&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;glossary-tld&quot;&gt;Top-level domain (TLD) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-tld&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Top-level domains such as .com and .org are listed in the &lt;a href=&quot;https://www.iana.org/domains/root/db&quot; rel=&quot;noopener&quot;&gt;Root Zone Database&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that some &#39;sites&#39; are actually just subdomains. For example, translate.google.com and maps.google.com are just subdomains of google.com (which is the &lt;a href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#glossary-etld&quot;&gt;eTLD + 1&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&quot;well-known&quot;&gt;.well-known &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/digging-into-the-privacy-sandbox/#well-known&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It can be useful to access policy or other information about a host &lt;em&gt;before&lt;/em&gt; making a request. For example, robots.txt tells web crawlers which pages to visit and which pages to ignore. IETF &lt;a href=&quot;https://tools.ietf.org/html/rfc8615&quot; rel=&quot;noopener&quot;&gt;RFC8615&lt;/a&gt; outlines a standardized way to make site-wide metadata accessible in standard locations in a /.well-known/ subdirectory. You can see a list of these at &lt;a href=&quot;https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml&quot; rel=&quot;noopener&quot;&gt;iana.org/assignments/well-known-uris/well-known-uris.xhtml&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thanks to all those who helped with writing and reviewing this post.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/@bamin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Pierre Bamin&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?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>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Feedback from the summer 2019 image optimization survey</title>
    <link href="https://web.dev/image-optimization-survey-2019/"/>
    <updated>2019-11-22T00:00:00Z</updated>
    <id>https://web.dev/image-optimization-survey-2019/</id>
    <content type="html" mode="escaped">&lt;p&gt;This post lists the freeform feedback that Google Web DevRel received in its Summer 2019
image optimization techniques survey.
Responses were solicited through &lt;a href=&quot;https://developers.google.com/web&quot; rel=&quot;noopener&quot;&gt;Web Fundamentals&lt;/a&gt; and
&lt;a href=&quot;https://twitter.com/chromiumdev&quot; rel=&quot;noopener&quot;&gt;@ChromiumDev&lt;/a&gt;. The motivation for the survey was to find out why
most sites don&#39;t follow image optimization best practices even though they seem like a relatively
easy way to improve performance. The response data isn&#39;t listed here because there were flaws
in the survey methodology.&lt;/p&gt;
&lt;h2 id=&quot;audience&quot;&gt;Audience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#audience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If you&#39;re a web developer, you might find this post useful for discovering new image optimization
techniques, or details on how other web developers have solved a problem that you&#39;re facing, as
well as the costs, benefits, and limitations of each technique.&lt;/li&gt;
&lt;li&gt;If you&#39;re an image service or image CDN provider, this post might help you find new market
opportunities.&lt;/li&gt;
&lt;li&gt;If you&#39;re a framework, build tool, or CMS developer, this post might give you ideas on new
features to implement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;comments&quot;&gt;Comments &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#comments&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;webp&quot;&gt;WebP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#webp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Why&#39;s this important? Image formats such as &lt;a href=&quot;https://web.dev/serve-images-webp&quot;&gt;WebP&lt;/a&gt; can result in smaller files and better quality than older formats such as JPEG and PNG. There are several techniques for using modern formats with fallback for older browsers. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;I do like WebP but it isn&#39;t yet fully ready. Moreover, our WordPress doesn&#39;t support WebP. One of the most popular photo editing apps, Photoshop, also doesn&#39;t support WebP out of the box. So we can&#39;t rely on 3rd party apps or services for image compression.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Make WebP usable on Safari.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I would love to use WebP if I could export them from Photoshop/Figma/Sketch and all browsers supported it.&amp;quot; [Note: Sketch does support WebP]&lt;/li&gt;
&lt;li&gt;&amp;quot;Next gen formatting solution would be great.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Stop pushing WebP so hard when browser support is poor, and consider the need for PNG instead of JPEG for screenshots.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Google Docs doesn&#39;t support WebP.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We would use WebP exclusively, but are concerned about browser compatibility.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;First fix browser compatibility and update legacy browsers or add legacy fixes, then people will be more inclined to adopt to new image types like WebP…&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Encourage plugin/theme developers to consider providing support to WebP and other next-gen image types, so that non-developers don&#39;t need to fiddle with it.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;svg-and-vector-images&quot;&gt;SVG and vector images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#svg-and-vector-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;If possible I&#39;m using (animated) SVG. gatsby-image fixed a lot of this. But when you dig into what they&#39;ve done, it&#39;s completely unrealistic that a normal website should have to build out something like that to get images to work right. The browser should take on more of this responsibility.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Would it be possible to document how to create SVG animations with &lt;a href=&quot;https://airbnb.io/lottie/#/&quot; rel=&quot;noopener&quot;&gt;lottie.js&lt;/a&gt;?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We try to use big resolution JPEG pictures with low sizes in our website most of the time to avoid loading times. We also ensure to use SVGs when necessary to provide quality for responsive design.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We try to use optimized vector graphics for all but pics if possible.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;other-image-formats&quot;&gt;Other image formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#other-image-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;We [need to] better educate people to stop using GIF.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;lazy-loading&quot;&gt;Lazy loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Why&#39;s this important? &lt;a href=&quot;https://web.dev/use-lazysizes-to-lazyload-images&quot;&gt;Requesting image files just in time&lt;/a&gt;, rather than getting all the images for a page as soon as it loads, can improve performance and reduce data cost. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Please keep the user in mind when considering features such as lazy load, because for many it&#39;s annoying.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Make the lazy load attribute work with background-image please.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Frameworks should do better asset processing out of the box.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We have converted from lazy loading a long time ago.  User reports of millions of images and sites &amp;quot;NOT LOADING&amp;quot;.  That was understanding our team summarized it as.  It&#39;s hard for a non-technical users to describe issues.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I&#39;m keen to get a better understanding of using Intersection Observer API for lazy loading rather than using traditional techniques.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;This well works for me: &lt;a href=&quot;https://pwafire.org/developer/codelabs/progressive-loading&quot; rel=&quot;noopener&quot;&gt;pwafire.org/developer/codelabs/progressive-loading&lt;/a&gt;.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;background-images&quot;&gt;Background images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#background-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;I usually load images as backgrounds in CSS.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag is problematic and difficult to control fine-grained details about, especially with user-submitted content. We use &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and background-image styling much more often as it allows us to use background-size, background-position, and prevent right-click saving of the image.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;transparency&quot;&gt;Transparency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#transparency&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;It&#39;s 2019. How are JPGs still without alpha transparency?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I only really use PNGs for photographs when I need a transparent background.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;low-quality-image-placeholders-lqips&quot;&gt;Low Quality Image Placeholders (LQIPs) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#low-quality-image-placeholders-lqips&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;We use LQIPS and it&#39;s a great technique to keep visitors engaged without loading high quality images really early.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;We actually had a recent performance issue with images. As a user scrolls down on our site, we show the next 60 cards which include a thumbnail. Due to the 6 connection limit on the same domain, the thumbnails were being blocked as well as the next AJAX request to get the next 60 cards if a user continues to scroll down.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We would love to use HTTP/2 but most of our customers use IE11! We are therefore exploring domain sharding / loading AJAX JSON data requests off a different domain.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;sizing&quot;&gt;Sizing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#sizing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Why&#39;s this important? The &lt;a href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt; attribute provides alternative image sources. You specify width or pixel density so the browser can choose the smallest image without needing to download images to calculate dimensions. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Sorry for intrinsicsize; leveraging height/width seems better to me.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Looking for a way to generate less sizes, right now it&#39;s ~12.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Dynamic resizing of images is really hard and impossible without JS.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;A tool like &lt;a href=&quot;http://responsivebreakpoints.com/&quot; rel=&quot;noopener&quot;&gt;responsivebreakpoints.com&lt;/a&gt; is good for web.dev :).&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;high-quality-and-high-resolution-images&quot;&gt;High quality and high-resolution images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#high-quality-and-high-resolution-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;How to download compress images without losing DPI quality?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We&#39;re a document management company. Our apps handle MILLIONS of hi-res scanned images, usually TIFF or PDF.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;It&#39;s a hassle. Hi-res img files are necessary for print format; must be optimized for web. It&#39;s a hassle to downsize images for web but it&#39;s a show-stopper if authors only supply lightweight files for images destined for print publication. We wind up giving mixed messages about requirements for submission of manuscripts with artwork. We then wind up with complex workflows for processing those materials.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;browser-capability&quot;&gt;Browser capability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#browser-capability&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Auto responsive src crop from browser as [built-in] feature would be very useful as it is time consuming to crop all images to 4 sizes and writing all the markup. If we can upload one large photo and writing a simple picture tag that browsers will automatically create the multiple src attributes that would be a winning feature.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Personally I&#39;m having a hard time avoiding page reflows when image with is set by CSS for responsive images (max-width: 100%; height auto or height: width: 100%; height auto), especially in combination with art direction from adaptive images/picture tag. Best way to avoid seems to use the &amp;quot;negative padding hack&amp;quot; for a fixed image ratio and then position the image inside this ratio box. Better browser support/responsive image handling would be a really great help!&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Please disable GIF &amp;quot;autoplay&amp;quot; by fetching just the first frame.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cdns-and-image-services&quot;&gt;CDNs and image services &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#cdns-and-image-services&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Why&#39;s this important? &lt;strong&gt;Image services&lt;/strong&gt; make it easier to optimize your images. You usually just upload one high-resolution version of each image and then use a web service API to optimize or transform the image as needed. &lt;strong&gt;Content Delivery Networks (CDNs)&lt;/strong&gt; optimize the distribution and delivery of images and other website assets and sometimes provide optimization services, such as automatically delivering WebP images to supporting browsers instead of PNG or JPEG, without changing the file extension. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Google should provide a free CDN like Cloudflare.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Maybe more tooling to set up dynamic scaling and CDNs with different providers would be nice.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;A single oversized overcompressed image is a very decent solution with no extra production cost. You need around 1000 pixels wide images for mobile (500px render width) and that is also the size you need for large/desktop non-retina displays. I think images resize CDNs are a very bad solution, although I have used it in the past. The CMS should handle the resizing and of that is too complex to set up, a single solution is a good compromise (for now).&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;CloudFlare auto-scales our images to best match the user&#39;s display. So we can save on loading time because images are loaded in relative to the user&#39;s display. For example, if a user is on a phone, it won&#39;t load in a desktop-sized background.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Cloudflare does this in the background without us having to do anything except check a box in our settings panel.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Just to reiterate, the only reason I can successfully use srcset, etc. is due to the ease of Cloudinary. But Cloudinary gets expensive, &lt;em&gt;really&lt;/em&gt; fast. this feels like a major hole in the development experience.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;We need a way to easily auto crop images in a smart way so they can work with different aspect ratios in different contexts.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I also use images from Other providers like Unsplash where there is very less control of resolution, quality and compression.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cms,-platform,-and-framework&quot;&gt;CMS, platform, and framework &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#cms,-platform,-and-framework&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;I still struggle to find out what is the best way to use images, when I am building a site using a CMS. Authors tend to configure images with different dimensions and expect images not to shrink or scale. I am not sure if it is ok to set max-width or max-height on images&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Been using gatsby-image for the last few projects and have never looked back.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Images are often the hard part as they are put into CMS by end user, they may use any size, format, sometimes original image in ideal image format and dimensions are not available.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Images are difficult to maintain since our system is self-serve adding controls is difficult unless things happen automatically without affecting resolution. Also for us images don&#39;t look correct in mobile vs desktop&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I help people to optimize their sites (WordPress). The biggest problems I&#39;ve seen for images are: Need to depend on a CDN or plugins to create WebP. srcset/picture has to be coded properly by theme developers. Most of the lazy loading plugins loads slowly giving bad UX. Background images are hard to lazy load.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;costbenefit&quot;&gt;Cost/benefit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#costbenefit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;The new practices are effective but increase the development time of the sites.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;The lack of adherence to the new standards such as srcset and WebP has been slow to be adopted by many Fortune 500 companies. Seeing this, many companies have resisted the change as an unnecessary development cost for current websites. The performance gains are not widely discussed or reported by the end user (UX). If anything, it makes it somewhat harder to save images from the web.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Costly to create and manage multiple sizes, versions.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;They take up a lot of space on our server.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;seo&quot;&gt;SEO &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#seo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;It&#39;s difficult to balance between acceptable image quality and file size. On one hand, I want fast loading for the SEO benefit, but on the other hand, poor quality images will detract from the UI/UX.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-role-of-images-on-the-web&quot;&gt;The role of images on the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#the-role-of-images-on-the-web&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;There are too many on the web. Stop using useless images that don&#39;t enhance the written content.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Do you still remember the time when the web didn&#39;t have images and we shared selfies as ASCII-art?&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tooling,-guidance,-standards-and-best-practice-frustrations-and-requests&quot;&gt;Tooling, guidance, standards and best practice: frustrations and requests &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-optimization-survey-2019/#tooling,-guidance,-standards-and-best-practice-frustrations-and-requests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[One participant wrote a &lt;a href=&quot;https://stokito.wordpress.com/2019/07/19/how-to-make-web-images-better/&quot; rel=&quot;noopener&quot;&gt;blog post&lt;/a&gt; in response to this survey]&lt;/li&gt;
&lt;li&gt;&amp;quot;The requirements seems to constantly change. As a web developer it is extremely frustrating because it is time consuming to save out the images in the first place. We optimize the best we can, we check the site and then months later Google has decided that the images could be even more compressed or need to be in a different format. This prevents us from providing the best possible solution to our client that lasts and instead creates a costly endeavor for them and us. Some of our small business clients simply don&#39;t have the budget for us to keep fixing images and re-saving them out to adhere to the requirements. We don&#39;t have the budget to do this work within their management packages. Writing the code to call different image sizes for different devices is also time consuming. It would be great to come up with a system of saving out images that would be consistent for a longer period of time.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Yes, I think you got &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/budgets&quot; rel=&quot;noopener&quot;&gt;Keep Request Counts Low And File Sizes Small&lt;/a&gt; all wrong in Lighthouse. If a site serves over HTTP1.x then sure, but if a site serves over HTTP2 then the number of requests is less important or not even an issue if originating from the same hostname. I have a lite website, but I load 30 small WebP files of approx 35 requests total, over HTTP2 on the same hostname. Lighthouse is flagging this as an &amp;quot;Keep Request Counts Low And File Sizes Small&amp;quot; issue whereas it is superfast and because of the HTTP2 on the same hostname the number of requests are not a problem. And yes, the files are already small (most between 1 KB and 2 KB or less). I could load a sprite but then more CSS computing needs to be done. So please update the &amp;quot;Keep Request Counts Low And File Sizes Small&amp;quot; report in Lighthouse to take HTTP2 over same hostname into account.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;It has been a struggle for people to remember to compress their images.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Cross browser behavior remains unpredictable so the simplest solutions are often the safest.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Top tips for web performance</title>
    <link href="https://web.dev/use-srcset-to-automatically-choose-the-right-image/"/>
    <updated>2019-06-24T00:00:00Z</updated>
    <id>https://web.dev/use-srcset-to-automatically-choose-the-right-image/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;SyVKRnusyqM&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;According to &lt;a href=&quot;https://httparchive.org/reports/state-of-images&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt;, a
typical mobile web page weighs over 2.6 MB, and more than two thirds of that
weight is images. That&#39;s a great opportunity for optimization!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;320&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8A7JasX5JOADmB1XkjMC.svg&quot; width=&quot;700&quot; /&gt;
  &lt;figcaption&gt;
    &lt;a href=&quot;https://mobile.httparchive.org/&quot;&gt;Average mobile page bytes by content type&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t save images larger than their display size.&lt;/li&gt;
&lt;li&gt;Save multiple sizes for each image and use the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-srcset&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;
attribute to enable the browser to choose the smallest.
The &lt;code&gt;w&lt;/code&gt; value tells the browser the width of each version:&lt;/li&gt;
&lt;/ul&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;img&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;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&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;small.jpg 500w,&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;             medium.jpg 1000w,&lt;/span&gt;&lt;br /&gt;             large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;save-images-with-the-right-size&quot;&gt;Save images with the right size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#save-images-with-the-right-size&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can make your website faster and less data hungry by using images with
dimensions that match the display size. In other words, give images the right
width and height when you save them.&lt;/p&gt;
&lt;p&gt;Take a look at the images below.&lt;/p&gt;
&lt;p&gt;They appear nearly identical, but the file size of one is more than 10 times
larger than the other.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Little Puss and Lias: two ten week old tabby kittens.&quot; decoding=&quot;async&quot; height=&quot;534&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/IHpM8DG6qiNlRcbfxnt8.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;Saved width 1000 px, file size 184 KB&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Little Puss and Lias: two ten week old tabby kittens.&quot; decoding=&quot;async&quot; height=&quot;200&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/XThwdsYxfx6KHkMxgbYI.jpg?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
    &lt;figcaption&gt;Saved width 300 px, file size 16 KB&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The first image is much larger in file size because it&#39;s saved with dimensions
much larger than the display size. Both images are displayed with a fixed
width of 300 pixels, so it makes sense to use an image saved at the same
size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For fixed widths, use images saved with the same dimensions as the
display size.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;but-what-if-display-size-varies&quot;&gt;But… what if display size varies? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#but-what-if-display-size-varies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In a multi-device world, images aren&#39;t always displayed at a single fixed size.&lt;/p&gt;
&lt;p&gt;Image elements might have a percentage width, or be part of responsive layouts
where image display sizes change to fit the screen size.&lt;/p&gt;
&lt;p&gt;…and what about pixel-hungry devices like Retina displays?&lt;/p&gt;
&lt;h2 id=&quot;help-the-browser-choose-the-right-image-size&quot;&gt;Help the browser choose the right image size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#help-the-browser-choose-the-right-image-size&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Wouldn&#39;t it be great if you could make each image available at different sizes, then
let the browser choose the best size for the device and display size?
Unfortunately there&#39;s a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Catch-22_(logic)&quot; rel=&quot;noopener&quot;&gt;catch-22&lt;/a&gt; when it comes to
working out which image is best. The browser should use the smallest possible
image, but it can&#39;t know the width of an image without downloading it to check.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;srcset&lt;/code&gt; comes in handy. You save images at different sizes, then
tell the browser the width of each version:&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;img&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;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&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 attr-name&quot;&gt;srcset&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;small.jpg 500w, medium.jpg 1000w, large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;w&lt;/code&gt; values show the width of each image in pixels. For example,
&lt;code&gt;small.jpg 500w&lt;/code&gt; tells the browser that &lt;a href=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&quot;&gt;small.jpg&lt;/a&gt; is 500
pixels wide. This enables the browser to choose the smallest possible image,
depending on the screen type and the viewport size—without having to
download images to check their size.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;srcset&lt;/code&gt; gives the browser information about the saved width of each image file.  It does &lt;em&gt;not&lt;/em&gt; specify the size to display the image—you still need CSS for that! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can see &lt;code&gt;srcset&lt;/code&gt; in action for the image below. If you&#39;re on a laptop or
desktop computer, change your browser window size and reopen this page.
Then use the Network panel of your browser tools to check which image was used.
(You&#39;ll need to do that in an Incognito or Private window, otherwise the
original image file will be cached.)&lt;/p&gt;
&lt;img alt=&quot;Lias and Little Puss: two ten week old grey tabby kittens&quot; decoding=&quot;async&quot; height=&quot;334&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/scLPehGom3IZLxPJiPPF.jpg?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
&lt;h2 id=&quot;how-can-i-create-multiple-image-sizes&quot;&gt;How can I create multiple image sizes? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#how-can-i-create-multiple-image-sizes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll need to make multiple sizes available for every image you want to use
with &lt;code&gt;srcset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For one-off images such as hero images you can manually save different sizes. If
you have lots of images, such as product photos, you&#39;ll need to automate.
For that there are two approaches.&lt;/p&gt;
&lt;h3 id=&quot;incorporate-image-processing-in-your-build-process&quot;&gt;Incorporate image processing in your build process &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#incorporate-image-processing-in-your-build-process&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As part of your build process, you can add steps to create different sized
versions of your images. See &lt;a href=&quot;https://web.dev/use-imagemin-to-compress-images&quot;&gt;&amp;quot;Use Imagemin to compress images&amp;quot;&lt;/a&gt;
to learn more.&lt;/p&gt;
&lt;h3 id=&quot;use-an-image-service&quot;&gt;Use an image service &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#use-an-image-service&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image creation and delivery can be automated using a commercial service like
&lt;a href=&quot;https://cloudinary.com/&quot; rel=&quot;noopener&quot;&gt;Cloudinary&lt;/a&gt;, or an open source equivalent such as
&lt;a href=&quot;https://github.com/thumbor/thumbor&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; that you install and run yourself.&lt;/p&gt;
&lt;p&gt;You upload your high resolution images, and the image service automatically
creates and delivers different image formats and sizes depending on the URL
parameters. For an example, open &lt;a href=&quot;https://res.cloudinary.com/webdotdev/f_auto/w_500/IMG_20190113_113201.jpg&quot; rel=&quot;noopener&quot;&gt;this sample image on Cloudinary&lt;/a&gt; and try changing the &lt;code&gt;w&lt;/code&gt; value or the file extension in the URL bar.&lt;/p&gt;
&lt;p&gt;Image services also have more advanced features such as the ability to automate
&amp;quot;smart cropping&amp;quot; for different image sizes and automatically deliver &lt;a href=&quot;https://developers.google.com/speed/webp/&quot; rel=&quot;noopener&quot;&gt;WebP&lt;/a&gt; images
to browsers that support the format, instead of JPEGs—without changing the file
extension.&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 check the format delivered using your browser tools.  For the image URL above, a WebP file is automatically delivered to browsers that support WebP, without changing the &lt;code&gt;.jpg&lt;/code&gt; file extension. &lt;/div&gt;&lt;/aside&gt;
&lt;img alt=&quot;Chrome DevTools showing WebP content-type header for file served by Cloudinary&quot; decoding=&quot;async&quot; height=&quot;146&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Y0ra5DLlntYoLV46uU1f.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;what-if-the-image-doesnt-look-right-at-different-sizes&quot;&gt;What if the image doesn&#39;t look right at different sizes? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#what-if-the-image-doesnt-look-right-at-different-sizes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In that case, you&#39;ll need to use the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element for &amp;quot;art direction&amp;quot;:
providing a different image or image crop at different sizes. To learn more
take a look at the &lt;a href=&quot;https://web.dev/codelab-art-direction&quot;&gt;&amp;quot;Art direction&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;h2 id=&quot;what-about-pixel-density&quot;&gt;What about pixel density? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#what-about-pixel-density&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;High-end devices have smaller (more dense) physical pixels. For example, a
high-end phone might have two or three times as many pixels in each row of
pixels as a cheaper device.&lt;/p&gt;
&lt;p&gt;That can affect the size you need to save your images. We won&#39;t go into the gory
details here, but you can find out more from the
&lt;a href=&quot;https://web.dev/codelab-density-descriptors&quot;&gt;&amp;quot;Use density descriptors&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;h2 id=&quot;what-about-the-display-size-of-the-image&quot;&gt;What about the display size of the image? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#what-about-the-display-size-of-the-image&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use &lt;code&gt;sizes&lt;/code&gt; to make &lt;code&gt;srcset&lt;/code&gt; work even better.&lt;/p&gt;
&lt;p&gt;Without it, the browser uses the full width of the viewport when choosing an
image from a &lt;code&gt;srcset&lt;/code&gt;. The &lt;code&gt;sizes&lt;/code&gt; attribute tells the browser the width that an
image element will be displayed, so the browser can choose the smallest possible
image file—before it makes any layout calculations.&lt;/p&gt;
&lt;p&gt;In the example below, &lt;code&gt;sizes=&amp;quot;50vw&amp;quot;&lt;/code&gt; tells the browser that this image will be
displayed at 50% of the viewport width.&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;img&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;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&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;small.jpg 500w, medium.jpg 1000w, large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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 attr-name&quot;&gt;sizes&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;50vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can see this in action at
&lt;a href=&quot;https://simpl.info/sizeswvalues/&quot; rel=&quot;noopener&quot;&gt;simpl.info/sizes&lt;/a&gt; and the &lt;a href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths&quot;&gt;&amp;quot;Specifying multiple slot widths&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;sizes&lt;/code&gt; gives the browser information about the display width of an image element.  As with &lt;code&gt;srcset&lt;/code&gt; it does NOT specify the size to display the image—you need CSS for that. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-about-browser-support&quot;&gt;What about browser support? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#what-about-browser-support&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; are &lt;a href=&quot;https://caniuse.com/#feat=srcset&quot; rel=&quot;noopener&quot;&gt;supported by over 90% of
browsers globally&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If a browser does not support &lt;code&gt;srcset&lt;/code&gt; or &lt;code&gt;sizes&lt;/code&gt; it will fall back to just using the &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;This makes &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; great progressive enhancements!&lt;/p&gt;
&lt;h2 id=&quot;learn-more&quot;&gt;Learn more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/#learn-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at the &lt;a href=&quot;https://web.dev/fast#optimize-your-images&quot;&gt;&amp;quot;Optimize your images&amp;quot;&lt;/a&gt; section
of web.dev for a deeper dive into image optimization. For a more guided
experience, consider trying the free &lt;a href=&quot;https://udacity.com/course/responsive-images--ud882&quot; rel=&quot;noopener&quot;&gt;&amp;quot;Responsive
Images&amp;quot;&lt;/a&gt; course offered by
Udacity.&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; This post accompanies &lt;a href=&quot;https://www.youtube.com/playlist?list=PLNYkxOF6rcICVl6Vb-AFlw81bQLuv6a_P&quot;&gt;&lt;strong&gt;Top tips for web performance&lt;/strong&gt;&lt;/a&gt;: a fortnightly video series showing simple techniques to improve site speed. &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Prework</title>
    <link href="https://web.dev/performance-audit-prework/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit-prework/</id>
    <content type="html" mode="escaped">&lt;p&gt;Before gathering performance metrics for a site audit, there are several checks you can do to
identify easy fixes and areas for focus.&lt;/p&gt;
&lt;h2 id=&quot;validity-check-architecture-and-code&quot;&gt;Validity check: architecture and code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-prework/#validity-check-architecture-and-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://wiki.c2.com/?TechnicalDebt&quot; rel=&quot;noopener&quot;&gt;Pay down technical debt!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wherever possible fix simple bugs and remove unneeded assets and code &lt;strong&gt;before&lt;/strong&gt;
measuring performance — but make sure to keep a before-and-after record of problems and fixes. These
improvements can still be a part of your audit work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Site architecture and assets&lt;/strong&gt;&lt;br /&gt; Can anything easily be removed from the code repo and from the
site, such as unused legacy pages, content or other assets? Check for orphaned pages, redundant
templates, unused images and &lt;a href=&quot;https://web.dev/web/updates/2017/04/devtools-release-notes#coverage&quot;&gt;unused code and libraries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime errors&lt;/strong&gt;&lt;br /&gt; Check for errors reported in the browser console. There shouldn&#39;t be any :).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linting&lt;/strong&gt;&lt;br /&gt; Are there errors in your HTML, CSS or JavaScript code? Building linting into your
workflow can help maintain code quality and avoid regressions. We recommend
&lt;a href=&quot;http://htmlhint.com/&quot; rel=&quot;noopener&quot;&gt;HTMLHint&lt;/a&gt;, &lt;a href=&quot;https://stylelint.io/&quot; rel=&quot;noopener&quot;&gt;StyleLint&lt;/a&gt; and
&lt;a href=&quot;http://eslint.org/&quot; rel=&quot;noopener&quot;&gt;ESLint&lt;/a&gt;, which can be used as code editor plugins, or run from
the command line within workflow processes and continuous integration tools such as
&lt;a href=&quot;https://travis-ci.org/&quot; rel=&quot;noopener&quot;&gt;Travis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broken links and images&lt;/strong&gt;&lt;br /&gt; There are many tools to test for broken links at build time and
runtime, including Chrome Extensions
(&lt;a href=&quot;https://chrome.google.com/webstore/detail/check-my-links/ojkcdipcgfaekbeaelaapakgnjflfglf&quot; rel=&quot;noopener&quot;&gt;this one&lt;/a&gt;
is good) and Node tools such as
&lt;a href=&quot;https://github.com/stevenvachon/broken-link-checker&quot; rel=&quot;noopener&quot;&gt;Broken Link Checker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plugins&lt;/strong&gt;&lt;br /&gt; Plugins such as Flash and Silverlight can be a security risk, support for them
&lt;a href=&quot;https://blog.chromium.org/2014/11/the-final-countdown-for-npapi.html&quot; rel=&quot;noopener&quot;&gt;has been deprecated&lt;/a&gt;,
and they don&#39;t work on mobile. &lt;a href=&quot;https://web.dev/web/tools/lighthouse/audits/plugins&quot;&gt;Use Lighthouse to check for
plugins&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;test-with-a-variety-of-devices-and-contexts&quot;&gt;Test with a variety of devices and contexts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-prework/#test-with-a-variety-of-devices-and-contexts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nothing beats getting real people to test your site with real devices, multiple browsers and
different connectivity contexts.&lt;/p&gt;
&lt;p&gt;Some of these checks are relatively subjective, but they can identify problems that affect perceived
performance. Broken links, for example, waste time and feel &#39;unresponsive&#39;. Illegible text is slow
to read.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-device testing&lt;/strong&gt;&lt;br /&gt; Try different viewport and window sizes. Use at least one mobile and
one desktop device. If possible, try your site on a low-spec mobile device with a small screen. Is
the text readable? Are any images broken? Can you zoom? Are touch targets large enough? Is it slow?
Are any features unresponsive? Screenshot or video the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-platform testing&lt;/strong&gt;&lt;br /&gt; What platforms do you target? You need to test on the browsers and
operating systems your users use now and in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Connectivity&lt;/strong&gt;&lt;br /&gt; &lt;a href=&quot;https://web.dev/web/fundamentals/performance/poor-connectivity/#testing&quot;&gt;Test on multiple target
network types&lt;/a&gt;: connected, wifi and
cellular. You can use browser tools to &lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance/network-conditions&quot;&gt;emulate a variety of network
conditions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Devices&lt;/strong&gt;&lt;br /&gt; Make sure to try out your site on the same devices as your users. The following
photo shows the same page on two different phones.&lt;/p&gt;
&lt;figure&gt;   
&lt;img alt=&quot;Blog post page running on a high spec and a low spec phone&quot; decoding=&quot;async&quot; height=&quot;678&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 760px) 760px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yZXt6rdAoMpFp8ncmbvW.jpeg?auto=format&amp;w=1520 1520w&quot; width=&quot;760&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;On the larger screen, text is small but readable. On the smaller screen the browser renders the
layout correctly, but the text is unreadable, even when zoomed in. The display is blurry and has a
&#39;color cast&#39; — white doesn&#39;t look white — making content less legible.&lt;/p&gt;
&lt;p&gt;Simple findings such as this can be far more important than obscure performance data!&lt;/p&gt;
&lt;h2 id=&quot;try-out-ui-and-ux&quot;&gt;Try out UI and UX &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-prework/#try-out-ui-and-ux&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Accessibility, usability and readability&lt;/strong&gt;&lt;br /&gt; To ensure that your site&#39;s content and
functionality are accessible to everyone, you need to understand the diversity of your users.
&lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; and other tools test for specific
accessibility problems, but nothing beats real-world testing. Try reading, navigating and entering
data in a variety of scenarios: for example, outdoors in sunlight or on a train. Ask a range of
friends, family and colleagues to try out your site. Try consuming content via a screen reader such
as &lt;a href=&quot;https://www.youtube.com/watch?v=5R-6WvAihms&amp;amp;list=PLNYkxOF6rcICWx0C9LVWWVqvHlYJyqw7g&amp;amp;index=6&quot; rel=&quot;noopener&quot;&gt;VoiceOver&lt;/a&gt;
on Mac or
&lt;a href=&quot;https://www.youtube.com/watch?v=Jao3s_CwdRU&amp;amp;list=PLNYkxOF6rcICWx0C9LVWWVqvHlYJyqw7g&amp;amp;index=4&quot; rel=&quot;noopener&quot;&gt;NVDA&lt;/a&gt;
on Windows.&lt;/p&gt;
&lt;p&gt;You can find out more about implementing and reviewing accessibility in the &lt;a href=&quot;https://web.dev/web/fundamentals/accessibility/&quot;&gt;Udacity course on Accessibility&lt;/a&gt; and the Web
Fundamentals article &lt;a href=&quot;https://web.dev/web/fundamentals/accessibility/how-to-review&quot;&gt;How To Do an Accessibility Review&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Keep a record of your accessibility audit. Chances are that you&#39;ll be able to make simple
improvements that are good for all your users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fundamental UI and UX problems&lt;/strong&gt;&lt;br /&gt; Interactions that don&#39;t work how they should, overflowing
elements on smaller windows and viewports, too-small tap targets, unreadable content, janky
scrolling… Open multiple pages on the site, try out navigation and all core functionality. Keep a
record.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Images, audio and video&lt;/strong&gt;&lt;br /&gt; Test for overflowing content, &lt;a href=&quot;https://chrome.google.com/webstore/detail/image-checker/bacnicogfgpigmmenfiplfiofpkocpii&quot; rel=&quot;noopener&quot;&gt;incorrect aspect ratio&lt;/a&gt;,
poor cropping, and quality problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Subjective UI tests&lt;/strong&gt;&lt;br /&gt;
These may not all be relevant, but simple changes can make refactoring easier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is &#39;What can I do here? immediately clear when you open the site?&lt;/li&gt;
&lt;li&gt;Are you drawn to consume content and follow links?&lt;/li&gt;
&lt;li&gt;Are there visual hierarchies or pathways — or does everything have the same visual weight?&lt;/li&gt;
&lt;li&gt;Is the layout cluttered?&lt;/li&gt;
&lt;li&gt;Are there too many fonts?&lt;/li&gt;
&lt;li&gt;Are there images or other content that could be removed?&lt;/li&gt;
&lt;li&gt;Content design is as important as interface design. Is the text and image content on your site
appropriate for mobile and desktop contexts? Can anything be eliminated?
&lt;a href=&quot;https://web.dev/web/fundamentals/design-and-ui/responsive/content&quot;&gt;Write for mobile&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Auditing Performance</title>
    <link href="https://web.dev/performance-audit/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-and-what&quot;&gt;Why and what? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#why-and-what&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ve probably heard about all the good things that Progressive Web App techniques can do for your
site. You might feel tempted to jump straight in and add PWA features. That&#39;s possible, but you&#39;ll
be much better off if you get &#39;PWA-ready&#39; first.&lt;/p&gt;
&lt;p&gt;No amount of PWA magic will fix problems such as blocking JavaScript or bloated images. PWAs need a
solid foundation.&lt;/p&gt;
&lt;p&gt;So how do you check the health of your website? The first step is to do a site audit: an objective
review of what works well and where (and how) there could be improvement.&lt;/p&gt;
&lt;p&gt;Auditing your site or app will help you build a resilient, performant experience — and highlight
quick wins that can be implemented with minimal sign-off. An audit also gives you a baseline for
data-driven development. Did a change make things better? How does your site compare with
competitors? You get metrics to prioritize effort, and concrete evidence to brag about once you&#39;ve
made improvements.&lt;/p&gt;
&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes… &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#if-you-only-have-5-minutes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; on your homepage and &lt;a href=&quot;https://web.dev/web/tools/lighthouse#gists&quot;&gt;save the
report data&lt;/a&gt;. You get a quantified
baseline and a todo list for improvements to performance, accessibility, security and SEO.&lt;/p&gt;
&lt;h2 id=&quot;if-you-only-have-30-minutes&quot;&gt;If you only have 30 minutes… &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#if-you-only-have-30-minutes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; is probably still the best place to start, but with a little
more time you can also record results from other tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/security&quot;&gt;Chrome DevTools Security panel&lt;/a&gt;: HTTPS usage.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance/&quot;&gt;Chrome DevTools Network Panel&lt;/a&gt;: load timings;
resource sizes and number of requests for HTML, CSS, JavaScript, images, fonts and other files.&lt;/li&gt;
&lt;li&gt;Chrome Task Manager: if your site constantly uses significant CPU or more memory than other apps
then you may need to fix memory leaks, task running or resource loading problems.
Make sure to test your site on devices representative of your users.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.webpagetest.org/easy&quot; rel=&quot;noopener&quot;&gt;WebPagetest&lt;/a&gt;: performance for different locations
and connection types, caching, time to first byte, CDN usage.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/speed/pagespeed/insights/&quot; rel=&quot;noopener&quot;&gt;Pagespeed Insights&lt;/a&gt;: load performance, data cost and
resource usage, including Chrome User Experience report data highlighting real-world performance
statistics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thinkwithgoogle.com/feature/mobile/&quot; rel=&quot;noopener&quot;&gt;Speed Scorecard and Impact Calculator&lt;/a&gt;: compare site speed
against peers and estimate the potential revenue opportunity of improving site speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure to test your website as a
first-time user sees it. Open the site an Incognito (Private) Window, or use browser tools to
disable caching and clear storage. This ensures that every asset is retrieved from the
network and not from a local cache, so you get an accurate picture of first-load performance.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPagetest&lt;/a&gt; is a free service that runs performance tests from multiple locations globally using real browsers with real connections, to provide detailed metrics and optimization advice.  &lt;ul&gt; &lt;li&gt;Leverage &lt;a href=&quot;https://web.dev/performance-audit/(https://www.webpagetest.org/easy&quot;&gt;webpagetest.org/easy&lt;/a&gt; to quickly simulate differences between network connection types on mobile&lt;/li&gt; &lt;li&gt;Generate a Lighthouse audit and report with every WebPagetest trace.&lt;/li&gt; &lt;li&gt; Measure site on first visit as well as for repeat views — for example: to see how much service worker caching helps.&lt;/li&gt; &lt;li&gt;Visually compare multiple sites and get filmstrip and waterfall comparisons.&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Nothing beats real world testing — try out your site with the same devices and connectivity as your
users and keep a record of your subjective experience.&lt;/p&gt;
&lt;h2 id=&quot;if-you-find-the-range-of-tools-bewildering&quot;&gt;If you find the range of tools bewildering… &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#if-you-find-the-range-of-tools-bewildering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at our guide: &lt;a href=&quot;https://web.dev/web/fundamentals/performance/speed-tools/&quot;&gt;How To Think About Speed Tools&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If nothing else, simply use &lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; to check for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS: &lt;a href=&quot;https://web.dev/web/fundamentals/security/encrypt-in-transit/why-https&quot;&gt;every site should deliver all assets over HTTPS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Server settings: your web server or CDN should
&lt;a href=&quot;https://web.dev/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer&quot;&gt;use compression correctly&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/web/fundamentals/performance/http2/&quot;&gt;use HTTP/2&lt;/a&gt;,
and &lt;a href=&quot;https://web.dev/web/fundamentals/performance/optimizing-content-efficiency/http-caching&quot;&gt;include appropriate headers&lt;/a&gt;
to enable your browser to cache resources.&lt;/li&gt;
&lt;li&gt;Script elements that can be moved to the bottom of the page and/or given an
&lt;a href=&quot;http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/&quot; rel=&quot;noopener&quot;&gt;async or defer&lt;/a&gt;
attribute.&lt;/li&gt;
&lt;li&gt;JavaScript and libraries that can be removed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://umaar.com/dev-tips/121-css-coverage/&quot; rel=&quot;noopener&quot;&gt;Unused CSS&lt;/a&gt;
and &lt;a href=&quot;https://web.dev/web/updates/2017/04/devtools-release-notes&quot;&gt;unused JavaScript&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Images that can be saved with higher compression or smaller pixel dimensions.&lt;/li&gt;
&lt;li&gt;Image files that would be smaller saved using a different format, for example
photos saved as PNGs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;audience,-stakeholders,-context&quot;&gt;Audience, stakeholders, context &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#audience,-stakeholders,-context&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Priorities for refactoring depend on your audience, content and functionality.
Who visits your site? Why and how do they use it? What&#39;s your
&lt;a href=&quot;https://www.performancebudget.io/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;performance budget&lt;/a&gt;? If you&#39;re not sure of the
answer to these questions, try the requirements gathering exercises from our PWA training
resources: &lt;a href=&quot;https://web.dev/web/ilt/pwa/your-audience-your-content&quot;&gt;Your audience, your content&lt;/a&gt;
and &lt;a href=&quot;https://web.dev/web/ilt/pwa/design-for-all-your-users&quot;&gt;Design for all your users&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Who are your stakeholders, and what are their priorities? This will affect the way you structure,
present and share your audit data.&lt;/p&gt;
&lt;p&gt;If you can&#39;t audit your whole site, check page analytics to get an idea of where to focus. High
bounce rates, low time-on-page and unexpected exit pages can be a good indicator of where to begin.
Likewise business metrics such as hosting costs, ad clicks and conversions. Get an overview from
stakeholders of what data matters to them.&lt;/p&gt;
&lt;h2 id=&quot;test,-record,-fix,-repeat&quot;&gt;Test, record, fix, repeat &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#test,-record,-fix,-repeat&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Record the state of your site &lt;strong&gt;before&lt;/strong&gt; making any changes, to uncover problems and
set a starting point for improvements or regressions. That gives you data to justify and reward
development effort.&lt;/p&gt;
&lt;p&gt;Make sure to test multiple page types within your site — not just the home page. For single page
apps, test different components, routes and UX flows, and not just the first load experience.&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot; class=&quot;hide-from-toc&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Use tools to measure performance</title>
    <link href="https://web.dev/performance-audit-tools/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit-tools/</id>
    <content type="html" mode="escaped">&lt;p&gt;There are several core objectives for building a performant, resilient site with low data cost.&lt;/p&gt;
&lt;p&gt;For each objective, you need an audit.&lt;/p&gt;
&lt;table data-alignment=&quot;top&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What to test for?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ensure privacy, security and data integrity, and enable powerful API usage&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https&quot;&gt;Why HTTPS Matters&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;HTTPS implemented for all site pages/routes and assets.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Improve load performance&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/&quot;&gt;53% of users
abandon sites&lt;/a&gt; that take longer than three seconds to load&lt;/td&gt;
&lt;td&gt;JavaScript and CSS that could be loaded asynchronously or deferred. Set goals for time to
  interactive and payload size: for example TTI on 3G. &lt;a href=&quot;https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/&quot;&gt;Set a performance budget&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduce page weight&lt;/td&gt;
&lt;td&gt;• Reduce data cost for users with capped data plans
• Reduce web app storage requirements — particularly important for users on low-spec devices
• Reduce hosting and serving costs
• Improve serving performance, reliability and resilience&lt;/td&gt;
&lt;td&gt;Set a page weight budget: for example, first load under 400 kB. Check for heavy JavaScript.
  Check file sizes to find bloated images, media, HTML, CSS and JavaScript. Find images that could
  be lazy loaded, and check for unused code with
  &lt;a href=&quot;https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage&quot;&gt;coverage tools&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduce resource requests&lt;/td&gt;
&lt;td&gt;• Reduce
  &lt;a href=&quot;https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/&quot;&gt;latency issues&lt;/a&gt;
• Reduce serving costs
• Improve serving performance, reliability and resilience&lt;/td&gt;
&lt;td&gt;Look for excessive or unnecessary requests for any type of resource. For example: files that are
loaded repeatedly, JavaScript that is loaded in multiple versions, CSS that is never used, images
that are never viewed (or could be lazy loaded). &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimize memory usage &lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://timkadlec.com/2013/11/why-we-need-responsive-images-part-deux/&quot;&gt;Memory can
become the new bottleneck&lt;/a&gt;, especially on mobile devices&lt;/td&gt;
&lt;td&gt;Use the Chrome Task Manager to compare your site against others for memory usage when loading
the home page and using other site features. &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduce CPU load&lt;/td&gt;
&lt;td&gt;Mobile devices have limited CPU, especially low-spec devices&lt;/td&gt;
&lt;td&gt;Check for heavy JavaScript. Find unused JavaScript and CSS with
  &lt;a href=&quot;https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage&quot;&gt;coverage tools&lt;/a&gt;.
  Check for &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/dom-size&quot;&gt;excessive
  DOM size&lt;/a&gt; and scripts that run unnecessarily on first load. Look for JavaScript loaded in
multiple versions, or libraries that could be avoided with minor refactoring. &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There is a wide range of tools and techniques for auditing websites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System tools&lt;/li&gt;
&lt;li&gt;Built-in browser tools&lt;/li&gt;
&lt;li&gt;Browser extensions&lt;/li&gt;
&lt;li&gt;Online test applications&lt;/li&gt;
&lt;li&gt;Emulation tools&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Metrics provided by servers and business systems&lt;/li&gt;
&lt;li&gt;Screen and video recording&lt;/li&gt;
&lt;li&gt;Manual tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below you&#39;ll learn which approach is relevant for each type of audit.&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;Images constitute by far the &lt;a href=&quot;http://httparchive.org/interesting.php#bytesperpage&quot;&gt;most weight&lt;/a&gt; and &lt;a href=&quot;http://httparchive.org/trends.php#bytesImg&amp;amp;reqImg&quot;&gt;most requests&lt;/a&gt; for most web pages.&lt;/strong&gt;  &lt;a href=&quot;https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/&quot;&gt;Latency gets worse as connectivity gets worse&lt;/a&gt; so excessive image file requests are an increasing problem as the web goes mobile. Images also consume power: more image requests, more radio usage, more flat batteries. &lt;a href=&quot;http://httparchive.org/trends.php#bytesImg&amp;amp;reqImg&quot;&gt;Even just to render images takes power&lt;/a&gt; – and this is proportional to the size and quantity of images.  Likewise for memory: small increases in pixel dimensions result in big increases in memory usage. With images on mobile — especially on low-spec devices — &lt;a href=&quot;https://timkadlec.com/2013/11/why-we-need-responsive-images-part-deux/&quot;&gt;memory can become the new bottleneck&lt;/a&gt;. Bloated images are also problematic for users on capped data plans.  &lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ui/responsive/content#viewport&quot;&gt;Remove redundant images&lt;/a&gt;! If you can&#39;t get rid of them, optimize: increase compression as much as possible, reduce pixel dimensions, and use the format that gives you the smallest file sizes. Optimizing &#39;hero images&#39; such as banners and backgrounds is an easy, one-off win. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;record-resource-requests-number,-size,-type-and-timing&quot;&gt;Record resource requests: number, size, type and timing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#record-resource-requests-number,-size,-type-and-timing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A good place to start when auditing a site is to check pages with your browser&#39;s network tools.
If you&#39;re not sure how to do this, work through the Chrome DevTools network panel
&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance/&quot;&gt;Get Started Guide&lt;/a&gt;.
Similar tools are available for
&lt;a href=&quot;https://developer.mozilla.org/docs/Tools/Network_Monitor&quot; rel=&quot;noopener&quot;&gt;Firefox&lt;/a&gt;,
&lt;a href=&quot;https://developer.apple.com/library/content/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/Instruments/Instruments.html#//apple_ref/doc/uid/TP40007874-CH4-SW1&quot; rel=&quot;noopener&quot;&gt;Safari&lt;/a&gt;,
&lt;a href=&quot;https://msdn.microsoft.com/library/gg130952(v=vs.85).aspx&quot; rel=&quot;noopener&quot;&gt;Internet Explorer&lt;/a&gt; and
&lt;a href=&quot;https://docs.microsoft.com/microsoft-edge/f12-devtools-guide/network&quot; rel=&quot;noopener&quot;&gt;Edge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Remember to keep a record of results before you make changes. For network requests, that can be as
simple as a screenshot — you can also
&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/evaluate-performance/timeline-tool#save_and_load_recordings&quot;&gt;save profile data&lt;/a&gt; as a JSON file. There&#39;s more information below about &lt;a href=&quot;https://web.dev/performance-audit-tools/#save-the-results&quot;&gt;how to save and share test results&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before you begin auditing network usage, make sure to
&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance/#emulate&quot;&gt;disable the browser cache&lt;/a&gt;
to ensure you get accurate statistics for first-load performance. If you already do caching via a
service worker,
&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/progressive-web-apps&quot;&gt;clear Cache API storage&lt;/a&gt;.
You may want to use an Incognito (Private) window, so that you don&#39;t have to worry about disabling
the browser cache or removing previously cached entries.&lt;/p&gt;
&lt;p&gt;Here are some core features and metrics you should check with browser tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load performance: &lt;a href=&quot;https://web.dev/web/tools/lighthouse/#devtools&quot;&gt;Lighthouse&lt;/a&gt;
provides a summary of load metrics. Addy Osmani has written a great summary of &lt;a href=&quot;https://medium.com/@addyosmani/progressive-web-apps-with-react-js-part-2-page-load-performance-33b932d97cf2&quot; rel=&quot;noopener&quot;&gt;key user moments&lt;/a&gt;
for page load.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/evaluate-performance/timeline-tool&quot;&gt;Timeline events&lt;/a&gt;
for loading and parsing resources, and memory usage. If you want to go deeper, run memory and
JavaScript &lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/evaluate-performance/timeline-tool#profile-js&quot;&gt;profiling&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Total page weight and number of files.&lt;/li&gt;
&lt;li&gt;Number and weight of JavaScript files.&lt;/li&gt;
&lt;li&gt;Any particularly large individual JavaScript files (over, say, 100KB).&lt;/li&gt;
&lt;li&gt;Unused JavaScript. You can check using the Chrome
&lt;a href=&quot;https://web.dev/web/updates/2017/04/devtools-release-notes&quot;&gt;coverage tool&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Total number and weight of image files.&lt;/li&gt;
&lt;li&gt;Any particularly large individual image files.&lt;/li&gt;
&lt;li&gt;Image formats: are there
&lt;a href=&quot;https://web.dev/web/fundamentals/performance/optimizing-content-efficiency/image-optimization&quot;&gt;PNGs that could be JPEGs or SVGs&lt;/a&gt;? Is WebP used with fallbacks?&lt;/li&gt;
&lt;li&gt;Whether responsive image techniques (such as
&lt;a href=&quot;https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/&quot; rel=&quot;noopener&quot;&gt;srcset&lt;/a&gt;)
are used.&lt;/li&gt;
&lt;li&gt;HTML file size.&lt;/li&gt;
&lt;li&gt;Total number and weight of CSS files.&lt;/li&gt;
&lt;li&gt;Unused CSS. (In Chrome, use the
&lt;a href=&quot;https://umaar.com/dev-tips/121-css-coverage/&quot; rel=&quot;noopener&quot;&gt;coverage panel&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;Check for problematic usage of other assets such as Web Fonts (including icon fonts).&lt;/li&gt;
&lt;li&gt;Check the DevTools timeline for anything that blocks page load.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;re working from fast wifi or a fast cellular connection, test with &lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance/network-conditions&quot;&gt;low bandwidth and high
latency emulation&lt;/a&gt;.
Remember to test on mobile as well as desktop — some sites use UA sniffing to deliver different
assets and layouts for different devices. You may need to test on actual hardware using
&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/remote-debugging/&quot;&gt;remote debugging&lt;/a&gt;,
not just with device simulation.&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;You can often use browser tools to spot problems simply by checking network responses and ordering by size.&lt;/strong&gt;  For example: the 349KB PNG here looked like it could be a problem:  &lt;figure&gt; &lt;img alt=&quot;Chrome DevTools Network panel showing a large file&quot; decoding=&quot;async&quot; height=&quot;242&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 479px) 479px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ajIej5qg2qPKZGnwgbyk.png?auto=format&amp;w=958 958w&quot; width=&quot;479&quot; /&gt; &lt;/figure&gt;  Sure enough, it turned out the image was 1600px wide, whereas the maximum display width of the element was only 400px. Decompressed, the image needed over 4MB of memory, which is a lot on a mobile phone.  Resaving the image as an 800px wide JPEG (to cope with 400px display width on 2x screens) and optimizing with &lt;a href=&quot;https://imageoptim.com/&quot;&gt;ImageOptim&lt;/a&gt; resulted in a 17KB file: compare the &lt;a href=&quot;https://drive.google.com/open?id=0B9xlQg9Xpugsb0VpQldsN3YwSEE&quot;&gt;original PNG&lt;/a&gt; with the &lt;a href=&quot;https://drive.google.com/open?id=0B9xlQg9XpugsTlBVNlQ1bUdQa0U&quot;&gt;optimized JPEG&lt;/a&gt;.&lt;p&gt;&lt;/p&gt;  That&#39;s a 95% improvement!  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;check-memory-and-cpu-load&quot;&gt;Check memory and CPU load &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#check-memory-and-cpu-load&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you make changes, keep a record of memory and CPU usage.&lt;/p&gt;
&lt;p&gt;In Chrome you can access the Task Manager from the Window menu. This is a simple way to check a
web page&#39;s requirements.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome Task Manager showing memory and CPU usage for   the four open browser tabs&quot; decoding=&quot;async&quot; height=&quot;278&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 655px) 655px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BwWPMsK66mrAbfewiEpA.png?auto=format&amp;w=1310 1310w&quot; width=&quot;655&quot; /&gt;
  &lt;figcaption&gt;Chrome&#39;s Task Manager — watch out for memory and CPU hogs!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;test-first-and-subsequent-load-performance&quot;&gt;Test first and subsequent load performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#test-first-and-subsequent-load-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt;,
&lt;a href=&quot;https://www.webpagetest.org/easy&quot; rel=&quot;noopener&quot;&gt;WebPagetest&lt;/a&gt; and
&lt;a href=&quot;https://developers.google.com/speed/pagespeed/insights/&quot; rel=&quot;noopener&quot;&gt;Pagespeed Insights&lt;/a&gt; are useful for analyzing speed, data cost and resource usage. WebPagetest will also check static-content
caching, time to first byte, and if your site makes effective use of CDNs.&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;It&#39;s simple to enable static-content caching so browsers can cache assets the first time they&#39;re requested, by configuring your server to include appropriate headers.&lt;/strong&gt;  If a browser can cache resources, it won&#39;t need to retrieve them from the network on subsequent visits. This improves load speed, cuts data cost and reduces network and server load — even for browsers that don&#39;t support caching via a service worker. &lt;a href=&quot;https://jakearchibald.com/2016/caching-best-practices/&quot;&gt;Even if you&#39;re using the Cache API&lt;/a&gt; it&#39;s important to enable browser caching.  To find out more, take a look at &lt;a href=&quot;https://developers.google.com/speed/docs/insights/LeverageBrowserCaching&quot;&gt;PageSpeed Tools&lt;/a&gt; and the resources on &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching&quot;&gt;Web Fundamentals&lt;/a&gt; (in particular, the &#39;Invalidating and updating cached responses&#39; section).  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;save-the-results&quot;&gt;Save the results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#save-the-results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WebPagetest&lt;/strong&gt;: &lt;a href=&quot;https://www.webpagetest.org/result/170428_NW_cc5afd75a2041f7e09984f33e4a4ae14/&quot; rel=&quot;noopener&quot;&gt;test results each have their own URL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pagespeed Insights&lt;/strong&gt;: the &lt;a href=&quot;https://developers.google.com/speed/pagespeed/insights&quot; rel=&quot;noopener&quot;&gt;online&lt;/a&gt;
Pagespeed Insights tool &lt;a href=&quot;https://webmasters.googleblog.com/2018/01/real-world-data-in-pagespeed-insights.html?m=1&quot; rel=&quot;noopener&quot;&gt;now includes Chrome User Experience report data&lt;/a&gt;
highlighting real-world performance stats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lighthouse&lt;/strong&gt;: save reports from the Chrome DevTools Audit panel by clicking on the
download button:&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome Lighthouse button for downloading reports&quot; decoding=&quot;async&quot; height=&quot;225&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fcXsYtUzSNLnYZgZAwQN.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;test-for-core-progressive-web-app-requirements&quot;&gt;Test for core Progressive Web App requirements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#test-for-core-progressive-web-app-requirements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; helps you test security,
functionality, accessibility, performance and search engine performance. In particular, Lighthouse
checks if your site successfully implements PWA features such as service workers and a Web App manifest.&lt;/p&gt;
&lt;p&gt;Lighthouse also tests whether your site can provide an acceptable offline experience.&lt;/p&gt;
&lt;p&gt;You can download a Lighthouse report as JSON or, if you&#39;re using the
&lt;a href=&quot;https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk&quot; rel=&quot;noopener&quot;&gt;Lighthouse Chrome Extension&lt;/a&gt;,
share the report as a GitHub Gist: click on the share button, select Open in Viewer, then click on
the share button again in the new window and Save as Gist.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing how to export a Chrome Lighthouse   report as a gist&quot; decoding=&quot;async&quot; height=&quot;346&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/pKYiZ9JL0bI1FfqUkC2u.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Export a report to a gist from the Lighthouse Chrome Extension — click the share button&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;use-analytics,-event-tracking-and-business-metrics-to-track-real-world-performance&quot;&gt;Use analytics, event tracking and business metrics to track real-world performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#use-analytics,-event-tracking-and-business-metrics-to-track-real-world-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you can, keep a record of analytics data before you implement changes: bounce rates, time on
page, exit pages: whatever&#39;s relevant to your business requirements.&lt;/p&gt;
&lt;p&gt;If possible, record business and technical metrics that might be affected, so you can compare
results after making changes. For example: an e-commerce site might track orders-per-minute or
record stats for stress and endurance testing. Back-end storage costs, CPU requirements, serving
costs and resilience are likely to improve if you cut page weight and resource requests.&lt;/p&gt;
&lt;p&gt;If analytics aren&#39;t implemented, now is the time! Business metrics and analytics are the final
arbiter of whether or not your site is working. If appropriate, incorporate
&lt;a href=&quot;https://web.dev/analytics/devguides/collection/analyticsjs/events&quot;&gt;event tracking&lt;/a&gt;
for user actions such as button clicks and video plays. You may also want to implement
&lt;a href=&quot;https://support.google.com/analytics/answer/2520139?ref_topic=1649581&quot; rel=&quot;noopener&quot;&gt;goal flow analysis&lt;/a&gt;:
the paths by which your users navigate towards &#39;conversions&#39;.&lt;/p&gt;
&lt;p&gt;You can keep an eye on Google Analytics
&lt;a href=&quot;https://support.google.com/analytics/answer/1205784&quot; rel=&quot;noopener&quot;&gt;Site Speed&lt;/a&gt; to check how
performance metrics correlate with business metrics. For example: &#39;how fast did the homepage load?&#39;
compared to &#39;did entry via the home page result in a sale?&#39;&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing Google Analytics Site Speed&quot; decoding=&quot;async&quot; height=&quot;417&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jw1eTOeLv1S7jqrgQxlU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Google Analytics uses data from the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigation_timing_API&quot; rel=&quot;noopener&quot;&gt;Navigation Timing API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You may want to record data using one of the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceObserver&quot; rel=&quot;noopener&quot;&gt;JavaScript performance APIs&lt;/a&gt;
or your own metrics, for 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; subscribeBtn &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;#subscribe&#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;    subscribeBtn&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 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 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 comment&quot;&gt;// Event listener logic goes here...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;     &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&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; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeStamp&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;lag &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;event&#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;eventCategory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Performance Metric&#39;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token literal-property property&quot;&gt;eventAction&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;input-latency&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token literal-property property&quot;&gt;eventLabel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#subscribe:click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token literal-property property&quot;&gt;eventValue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lag&lt;span class=&quot;token punctuation&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;nonInteraction&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;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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can also use ReportingObserver to check for browser deprecation and intervention warnings.
This is one of &lt;a href=&quot;https://web.dev/web/updates/2018/07/reportingobserver&quot;&gt;many APIs for getting real-world, live measurements from actual users&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;real-world-experience-screen-and-video-recording&quot;&gt;Real-world experience: screen and video recording &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#real-world-experience-screen-and-video-recording&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make a video recording of page load on mobile and desktop. This works even better at high frame
rates and if you add a timer display.&lt;/p&gt;
&lt;p&gt;You may also want to save screencasts. There are many screencast recording apps for Android, iOS,
and desktop platforms (and
&lt;a href=&quot;https://paul.kinlan.me/android-screen-recording/&quot; rel=&quot;noopener&quot;&gt;scripts to do the same&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Video-recording page load works much like the
&lt;a href=&quot;http://www.webpagetest.org/video/compare.php?tests=170427_61_14ZR-r:1-c:0&quot; rel=&quot;noopener&quot;&gt;filmstrip view&lt;/a&gt; in
WebPagetest or
&lt;a href=&quot;https://web.dev/web/updates/2015/07/devtools-digest-film-strip-and-a-new-home-for-throttling&quot;&gt;Capture Screenshots&lt;/a&gt;
in Chrome DevTools. You get a real-world record of page component load speed: what&#39;s fast and what&#39;s
slow. Save video recordings and screencasts to compare against later improvements.&lt;/p&gt;
&lt;p&gt;A side-by-side before-and-after comparison can be a great way to demonstrate improvements!&lt;/p&gt;
&lt;h2 id=&quot;what-else&quot;&gt;What else? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-tools/#what-else&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If relevant, get a &lt;a href=&quot;http://www.webbloatscore.com/&quot; rel=&quot;noopener&quot;&gt;Web Bloat Score&lt;/a&gt;. This is a fun
test, but it can also be a compelling way to demonstrate code bloat — or to show you&#39;ve
made improvements.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://whatdoesmysitecost.com/test/170427_KK_6aecf8c8a21c22e9f59b2b65e8371569#gniCost&quot; rel=&quot;noopener&quot;&gt;What Does My Site Cost?&lt;/a&gt;,
shown below, gives a rough guide to the financial cost of loading your site in different regions.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot from whatdoesmysitecost.com&quot; decoding=&quot;async&quot; height=&quot;586&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GzHcuWgCpknwNSMlj3A7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Many other standalone and online tools are available: take a look at
&lt;a href=&quot;http://perf.rocks/tools&quot; rel=&quot;noopener&quot;&gt;perf.rocks/tools&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Next steps</title>
    <link href="https://web.dev/performance-audit-next/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit-next/</id>
    <content type="html" mode="escaped">&lt;p&gt;Having completed a site audit, you will have accurate review data in a form that makes it easy for
developers and other stakeholders to prioritize and justify changes.&lt;/p&gt;
&lt;p&gt;Next, you may want to revisit the sections on this site that provide in-depth advice on how to
improve &lt;a href=&quot;https://web.dev/web/fundamentals/performance/get-started/&quot;&gt;load performance&lt;/a&gt; and
&lt;a href=&quot;https://web.dev/web/fundamentals/performance/rendering/&quot;&gt;rendering performance&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-next/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://perf.rocks/articles&quot; rel=&quot;noopener&quot;&gt;perf.rocks&lt;/a&gt;: resources to help you build fast sites&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/fundamentals/performance&quot;&gt;Web Fundamentals Performance guide&lt;/a&gt;:
why performance matters and how to improve it&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/fundamentals/performance/speed-tools/&quot;&gt;How To Think About Speed Tools&lt;/a&gt;:
overview of guidance and tools for developers and marketers from Google&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stevesouders.com/hpws/&quot; rel=&quot;noopener&quot;&gt;High Performance Web Sites&lt;/a&gt; by Steve Souders: more
than 10 years old, but still valuable&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hpbn.co/&quot; rel=&quot;noopener&quot;&gt;High Performance Browser Networking&lt;/a&gt; by Ilya Grigorik:
comprehensive reference including introductory guides to TCP, UDP, TLS and other topics&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets&quot; rel=&quot;noopener&quot;&gt;Can You Afford It?: Real-world Web Performance Budgets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Check site security</title>
    <link href="https://web.dev/performance-audit-security/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit-security/</id>
    <content type="html" mode="escaped">&lt;p&gt;You won&#39;t be able to build a PWA without HTTPS.&lt;/p&gt;
&lt;p&gt;Serving your site over HTTPS is fundamental for security, and many APIs won&#39;t work without it. If
you need to justify implementation costs, find out &lt;a href=&quot;https://web.dev/web/fundamentals/security/encrypt-in-transit/why-https&quot;&gt;why HTTPS matters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If a site uses HTTP for any assets, users will be warned in the URL bar. Chrome displays a warning
like the following.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome &amp;#x27;not secure&amp;#x27; warning&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OYTYjIbwrPLxQG4riSL5.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;From Chrome 68, the address bar warns if not all assets use HTTPS&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;HTTPS should be implemented everywhere — not just, for example, on login or checkout pages. Any
insecure page or asset can be a vector for attack, making your site a liability for your users and
your business.&lt;/p&gt;
&lt;p&gt;Site security is easy to check with &lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/security&quot;&gt;Chrome DevTools Security panel&lt;/a&gt;. Keep a record of any
problems.&lt;/p&gt;
&lt;p&gt;The site in the following example is not secure, since some assets are served over HTTP.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome DevTools Security panel&quot; decoding=&quot;async&quot; height=&quot;553&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/S19zXkzMqSQxUNJ3FfEW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;Chrome DevTools Security panel&lt;/em&gt;&lt;/figcaption&gt; 
&lt;/figure&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Share the results</title>
    <link href="https://web.dev/performance-audit-share/"/>
    <updated>2018-08-16T00:00:00Z</updated>
    <id>https://web.dev/performance-audit-share/</id>
    <content type="html" mode="escaped">&lt;p&gt;Once you&#39;ve audited a site, make sure to package the results in a digestible form.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider producing different reports for different stakeholders.&lt;/li&gt;
&lt;li&gt;Focus on business needs and show how technical metrics support these.&lt;/li&gt;
&lt;li&gt;Start with a summary.&lt;/li&gt;
&lt;li&gt;Structure data by topic (such as load performance and page weight) rather than simply listing tool output data.&lt;/li&gt;
&lt;li&gt;Order results by priority.&lt;/li&gt;
&lt;li&gt;Leave out results if they&#39;re not relevant or interesting.&lt;/li&gt;
&lt;li&gt;Where possible present numerical data as charts or graphs.&lt;/li&gt;
&lt;li&gt;Avoid a wall of data — site reviews should not be boring.
&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;note&quot;&gt;
  &lt;p&gt;&lt;strong&gt;Be sensitive to the people on the receiving end of your audit.&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Those working on a site may be well aware of problems. There may be complex, non-technical
  reasons why problems haven&#39;t been fixed.&lt;/p&gt;
  &lt;p&gt;It&#39;s much more helpful to describe poor performance in terms of opportunities and solutions,
  rather than simply listing a catalog of failures.&lt;/p&gt;
  &lt;p&gt;Wherever possible, talk to site developers and other stakeholders before presenting your
  findings more widely.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;provide-context&quot;&gt;Provide context &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-share/#provide-context&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When sharing review results you may want to include contextual data to justify effort and motivate
developers or other stakeholders to implement improvements you suggest — such as the following from
&lt;a href=&quot;https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;DoubleClick&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;53% of users abandon sites that take longer than three seconds to load.&lt;/li&gt;
&lt;li&gt;Mobile sites load in 5 seconds earn up to 2x more mobile ad revenue.&lt;/li&gt;
&lt;li&gt;The average load time for mobile sites is 19 seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a comprehensive list of business reasons for improving performance on
&lt;a href=&quot;http://www.neotys.com/blog/how-to-talk-about-performance-testing-with-ceos-and-business-managers/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;neotys.com&lt;/a&gt;.
More information about how to improve site performance is available from
&lt;a href=&quot;http://perf.rocks/articles&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;perf.rocks&lt;/a&gt; and
&lt;a href=&quot;https://web.dev/web/fundamentals/performance/&quot;&gt;Web Fundamentals&lt;/a&gt;, along with case
studies and success stories.&lt;/p&gt;
&lt;p&gt;If you don&#39;t have a &lt;a href=&quot;https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets&quot; rel=&quot;noopener&quot;&gt;performance budget&lt;/a&gt;, now is the time! &lt;a href=&quot;https://www.performancebudget.io/&quot; rel=&quot;noopener&quot;&gt;Calculate
a budget&lt;/a&gt; and show how your site weighs up.&lt;/p&gt;
&lt;h2 id=&quot;demonstrate-potential&quot;&gt;Demonstrate potential &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-audit-share/#demonstrate-potential&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome DevTools &lt;a href=&quot;https://t.co/0a56PgKlPv&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;Local Overrides&lt;/a&gt; allow you to override
website assets with local versions. This can be a great way to show how simple changes can make a
big difference.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a version of the CSS used by a site&#39;s homepage, with redundant rules removed.&lt;/li&gt;
&lt;li&gt;Change HTML to defer JavaScript loading.&lt;/li&gt;
&lt;li&gt;Replace image files with optimized versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can even share changed files with developers working on a site, to enable them to demonstrate
potential improvements directly to their colleagues. Local Overrides also makes it simple to create
side-by-side screencasts showing performance differences between optimized and unoptimized versions.
This approach can be much more compelling than a lengthy todo list! Find out how to use Local
Overrides &lt;a href=&quot;https://glebbahmutov.com/blog/local-overrides/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Multi-device content</title>
    <link href="https://web.dev/multi-device-content/"/>
    <updated>2016-05-10T00:00:00Z</updated>
    <id>https://web.dev/multi-device-content/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;how-people-read-on-the-web&quot;&gt;How people read on the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#how-people-read-on-the-web&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;http://www.usability.gov/how-to-and-tools/methods/writing-for-the-web.html&quot; rel=&quot;noopener&quot;&gt;US government writing guide&lt;/a&gt; summarizes what people want from writing on the web:&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; When writing for the web, using plain language allows users to find what they need, understand what they have found, and then use it to meet their needs.  It should also be actionable, findable, and shareable. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Research shows that &lt;a href=&quot;https://www.nngroup.com/articles/concise-scannable-and-objective-how-to-write-for-the-web/&quot; rel=&quot;noopener&quot;&gt;people don&#39;t read web pages, they scan&lt;/a&gt;. On average, &lt;a href=&quot;https://www.nngroup.com/articles/how-little-do-users-read/&quot; rel=&quot;noopener&quot;&gt;people only read 20–28% of web page content&lt;/a&gt;. Reading from screens is much slower than reading from paper. People will give up and leave your site unless information is easy to access and understand.&lt;/p&gt;
&lt;h2 id=&quot;how-to-write-for-mobile&quot;&gt;How to write for mobile &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#how-to-write-for-mobile&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Focus on the subject at hand and tell the story upfront. For writing to work across a range of devices and viewports, make sure to get your main points across at the start: as a rule, ideally &lt;a href=&quot;http://www.bbc.co.uk/academy/journalism/article/art20130702112133610&quot; rel=&quot;noopener&quot;&gt;in the first four paragraphs, in around 70 words&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ask yourself what people want from your site. Are they trying to find something out? If people visit your site for information, make sure that all your text is oriented to helping them achieve their goal. Write in the &lt;a href=&quot;https://learnenglish.britishcouncil.org/en/english-grammar/verbs/active-and-passive-voice&quot; rel=&quot;noopener&quot;&gt;active voice&lt;/a&gt;, offer actions and solutions.&lt;/p&gt;
&lt;p&gt;Publish only what your visitors want, and nothing more.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.gov.uk/guidance/content-design/writing-for-gov-uk&quot; rel=&quot;noopener&quot;&gt;UK government research&lt;/a&gt; also shows that:&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; 80% of people preferred sentences written in clear English — and the more complex the issue, the greater that preference (e.g., 97% preferred &amp;quot;among other things&amp;quot; over the Latin &amp;quot;inter alia&amp;quot;).  The more educated the person and the more specialist their knowledge, the greater their preference for plain English. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In other words: use plain language, shorter words and simple sentence structures — even for a literate, technical audience. Unless there&#39;s a good reason not to, keep your tone of voice conversational. An old rule of journalism is to write as if you are speaking to an intelligent 11 year old.&lt;/p&gt;
&lt;h2 id=&quot;the-next-billion-users&quot;&gt;The next billion users &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#the-next-billion-users&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The pared-down approach to writing is particularly important for readers on mobile devices, and is crucial when creating content for low-cost phones with small viewports that require more scrolling and may have lower quality displays and less responsive screens.&lt;/p&gt;
&lt;p&gt;Most of the next billion users coming online will have cheap devices. They will not want to spend their data budget on navigating long-winded content, and may not be reading in their first language. Trim your text: use short sentences, minimal punctuation, paragraphs five lines or less, and single line headings. Consider responsive text (for example, using shorter headlines for smaller viewports) but &lt;a href=&quot;https://www.smashingmagazine.com/2012/02/ever-justification-for-responsive-text/&quot; rel=&quot;noopener&quot;&gt;beware of the downsides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A minimalist attitude to text will also make your content easier to localize and internationalize — and make it more likely that your content gets quoted in social media.&lt;/p&gt;
&lt;p&gt;The bottom line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep it simple&lt;/li&gt;
&lt;li&gt;Reduce clutter&lt;/li&gt;
&lt;li&gt;Get to the point&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;eliminate-unnecessary-content&quot;&gt;Eliminate unnecessary content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#eliminate-unnecessary-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In terms of byte size, web pages are &lt;a href=&quot;http://httparchive.org/trends.php#bytesTotal&amp;amp;reqTotal&quot; rel=&quot;noopener&quot;&gt;big and getting bigger&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/responsive-web-design-basics/&quot;&gt;Responsive design techniques&lt;/a&gt; make it possible to serve different content for smaller viewports, but it&#39;s always sensible to start by streamlining text, images and other content.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Web users are often action oriented, &amp;quot;leaning forward&amp;quot; in the hunt for answers to their current question, rather than leaning back to absorb a good book.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Jackob Nielsen&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ask yourself: what are people are trying to achieve when they visit my site?&lt;/p&gt;
&lt;p&gt;Does every page component help users achieve their goal?&lt;/p&gt;
&lt;h3 id=&quot;remove-redundant-page-elements&quot;&gt;Remove redundant page elements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#remove-redundant-page-elements&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HTML files constitute nearly 70k and more than nine requests for the average web page, according to &lt;a href=&quot;http://httparchive.org/trends.php#bytesHtml&amp;amp;reqHtml&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many popular sites use several thousand HTML elements per page, and several thousand lines of code, even on mobile. Excessive HTML file size &lt;a href=&quot;http://jsbin.com/zofavunapo/1/edit?html,js,output&quot; rel=&quot;noopener&quot;&gt;may not make pages load more slowly&lt;/a&gt;, but a heavy HTML payload can be a sign of content bloat: larger .html files mean more elements, more text content, or both.&lt;/p&gt;
&lt;p&gt;Reducing HTML complexity will also reduce page weight, help enable localization and internationalization and make responsive design easier to plan and debug. For information about writing more efficient HTML, see &lt;a href=&quot;https://samdutton.wordpress.com/2015/04/02/high-performance-html/&quot; rel=&quot;noopener&quot;&gt;High performance HTML&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every step you make a user perform before they get value out of your app will cost you 20% of users&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Gabor Cselle, Twitter&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same applies to content: help users get to what they want as quickly as possible.&lt;/p&gt;
&lt;p&gt;Don&#39;t just hide content from mobile users. Aim for &lt;a href=&quot;http://bradfrost.com/blog/mobile/content-parity/&quot; rel=&quot;noopener&quot;&gt;content parity&lt;/a&gt;, since guessing what features your mobile users won&#39;t miss is bound to fail for someone. If you have the resources, create alternative versions of the same content for different viewport sizes — even if only for high priority page elements.&lt;/p&gt;
&lt;p&gt;Consider content management and workflow: are legacy systems resulting in legacy content?&lt;/p&gt;
&lt;h3 id=&quot;simplify-text&quot;&gt;Simplify text &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#simplify-text&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As the web goes mobile, you need to change the way you write. Keep it simple, reduce clutter and get to the point.&lt;/p&gt;
&lt;h3 id=&quot;remove-redundant-images&quot;&gt;Remove redundant images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#remove-redundant-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;HTTP Archive showing increasing number of image transfer sizes and image requests&quot; decoding=&quot;async&quot; height=&quot;300&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ldqgben5yJcle6sDjV4A.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
    &lt;figcaption&gt;According to &lt;a href=&quot;http://httparchive.org/trends.php#bytesImg&amp;reqImg&quot;&gt;HTTP Archive data&lt;/a&gt;, the average web page makes 54 requests for images.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;Images can be beautiful, fun and informative — but they also use page real estate, add to page weight, and increase the number of file requests. &lt;a href=&quot;https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/&quot; rel=&quot;noopener&quot;&gt;Latency gets worse as connectivity gets worse&lt;/a&gt;, meaning that an excess of image file requests is an increasing problem as the web goes mobile.&lt;/p&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;HTTP Archive pie chart showing average bytes per page by content type, around 60% of which is images.&quot; decoding=&quot;async&quot; height=&quot;278&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1AvqhLhMKzboVrLLoHCW.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
    &lt;figcaption&gt;Images constitute over 60% of page weight.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;Images also consume power. After the screen, radio is the second biggest drain on your battery. More image requests, more radio usage, more flat batteries. Even just to render images takes power – and this is proportional to size and number. Check out the Stanford report &lt;a href=&quot;http://cdn.oreillystatic.com/en/assets/1/event/79/Who%20Killed%20My%20Battery_%20Analyzing%20Mobile%20Browser%20Energy%20Consumption%20Presentation.pdf&quot; rel=&quot;noopener&quot;&gt;Who Killed My Battery?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you can, get rid of images!&lt;/p&gt;
&lt;p&gt;Here are some suggestions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider designs that avoid images altogether, or use images sparingly. &lt;a href=&quot;https://onepagelove.com/tag/text-only&quot; rel=&quot;noopener&quot;&gt;Text-only can be beautiful&lt;/a&gt;! Ask yourself, &amp;quot;What are visitors to my site trying to achieve? Do images help that process?&amp;quot;&lt;/li&gt;
&lt;li&gt;In the old days, it was commonplace to save headings and other text as graphics. That approach does not respond well to viewport size changes, and adds to page weight and latency. Using text as a graphic also means the text can&#39;t be found by search engines, and isn&#39;t accessible by screenreaders and other assistive technologies. Use &amp;quot;real&amp;quot; text where possible — Web Fonts and CSS can enable beautiful typography.&lt;/li&gt;
&lt;li&gt;Use CSS rather than images for gradients, shadows, rounded corners, and &lt;a href=&quot;http://lea.verou.me/css3patterns/&quot; rel=&quot;noopener&quot;&gt;background textures&lt;/a&gt;, features &lt;a href=&quot;http://caniuse.com/#search=shadows&quot; rel=&quot;noopener&quot;&gt;supported by all modern browsers&lt;/a&gt;. Bear in mind, however, that CSS may be better than images, but there can still be a &lt;a href=&quot;http://www.smashingmagazine.com/2013/04/03/build-fast-loading-mobile-website/&quot; rel=&quot;noopener&quot;&gt;processing and rendering penalty&lt;/a&gt;, especially significant on mobile.&lt;/li&gt;
&lt;li&gt;Background images rarely work well on mobile. You can &lt;a href=&quot;http://udacity.github.io/responsive-images/examples/2-06/backgroundImageConditional/&quot; rel=&quot;noopener&quot;&gt;use media queries&lt;/a&gt; to avoid background images on small viewports.&lt;/li&gt;
&lt;li&gt;Avoid splash screen images.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/fundamentals/design-and-ux/animations/&quot;&gt;Use CSS for UI animations&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Get to know your glyphs; use &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_Unicode_characters&quot; rel=&quot;noopener&quot;&gt;Unicode symbols and icons&lt;/a&gt; instead of images, with Web Fonts if necessary.&lt;/li&gt;
&lt;li&gt;Consider &lt;a href=&quot;http://weloveiconfonts.com/#zocial&quot; rel=&quot;noopener&quot;&gt;icon fonts&lt;/a&gt;; they are vector graphics that can be infinitely scaled, and an entire set of images can be downloaded in one font. (Be aware of &lt;a href=&quot;https://sarasoueidan.com/blog/icon-fonts-to-svg/&quot; rel=&quot;noopener&quot;&gt;these concerns&lt;/a&gt;, however.)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element can be used to build images in JavaScript from lines, curves, text, and other images.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://udacity.github.io/responsive-images/examples/2-11/svgDataUri/&quot; rel=&quot;noopener&quot;&gt;Inline SVG or Data URI images&lt;/a&gt; will not reduce page weight, but they can reduce latency by reducing the number of resource requests. Inline SVG has &lt;a href=&quot;http://caniuse.com/#feat=svg-html5&quot; rel=&quot;noopener&quot;&gt;great support on mobile and desktop browsers&lt;/a&gt;, and &lt;a href=&quot;http://petercollingridge.appspot.com/svg-optimiser&quot; rel=&quot;noopener&quot;&gt;optimization tools&lt;/a&gt; can significantly reduce SVG size. Likewise, Data URIs are &lt;a href=&quot;http://caniuse.com/datauri&quot; rel=&quot;noopener&quot;&gt;well supported&lt;/a&gt;. Both can be inlined in CSS.&lt;/li&gt;
&lt;li&gt;Consider using &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; instead of animated GIFs. &lt;a href=&quot;http://caniuse.com/video&quot; rel=&quot;noopener&quot;&gt;The video element is supported by all browsers on mobile&lt;/a&gt; (apart from Opera Mini).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information see &lt;a href=&quot;https://web.dev/fast/#optimize-your-images&quot;&gt;Image Optimization&lt;/a&gt; and &lt;a href=&quot;https://web.dev/fast/#optimize-your-images&quot;&gt;Eliminating and replacing images&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;design-content-to-work-well-across-different-viewport-sizes&quot;&gt;Design content to work well across different viewport sizes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#design-content-to-work-well-across-different-viewport-sizes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Create a product, don&#39;t re-imagine one for small screens. Great mobile
products are created, never ported.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Mobile Design and Development, Brian Fling&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Great designers don&#39;t &amp;quot;optimize for mobile&amp;quot; — they think responsively to build sites that work across a range of devices. The structure of text and other page content is critical to cross-device success.&lt;/p&gt;
&lt;p&gt;Many of the next billion users coming online use low-cost devices with small viewports. Reading on a low resolution 3.5&amp;quot; or 4&amp;quot; screen can be hard work.&lt;/p&gt;
&lt;p&gt;Here is a photograph of the two together:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Photo comparing display of blog post on high end and low-cost smartphones&quot; decoding=&quot;async&quot; height=&quot;678&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 760px) 760px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KSRYKNTBLn1v5QbZt7Lw.jpg?auto=format&amp;w=1520 1520w&quot; width=&quot;760&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;On the larger screen, text is small but readable.&lt;/p&gt;
&lt;p&gt;On the smaller screen the browser renders the layout correctly, but the text is unreadable, even when zoomed in. The display is blurry and has a &#39;color cast&#39; — white doesn&#39;t look white — making content less legible.&lt;/p&gt;
&lt;h3 id=&quot;design-content-for-mobile&quot;&gt;Design content for mobile &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#design-content-for-mobile&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When building for a range of viewports, consider content as well as layout and graphic design,
&lt;a href=&quot;http://uxmyths.com/post/718187422/myth-you-dont-need-the-content-to-design-a-website&quot; rel=&quot;noopener&quot;&gt;design with real text and images, not placeholder content&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Content precedes design. Design in the absence of content is not design, it&#39;s decoration.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Jeffrey Zeldman&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Put your most important content at the top, since &lt;a href=&quot;https://www.nngroup.com/articles/f-shaped-pattern-reading-web-content/&quot; rel=&quot;noopener&quot;&gt;users tend to read web pages in an F-shaped pattern&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Users visit your site to achieve a goal. Ask yourself what they need to achieve that goal and get rid of everything else. Get tough on visual and textual embellishments, legacy content, excessive links, and other clutter.&lt;/li&gt;
&lt;li&gt;Be careful with social sharing icons; they can clutter layouts, and the code for them can slow down page loading.&lt;/li&gt;
&lt;li&gt;Design &lt;a href=&quot;https://web.dev/responsive-web-design-basics/&quot;&gt;responsive layouts&lt;/a&gt; for content, not fixed device sizes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;test-content&quot;&gt;Test content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#test-content&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Success&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Whatever you do — &lt;strong&gt;test&lt;/strong&gt;! &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;Check readability on smaller viewports using Chrome DevTools and other &lt;a href=&quot;https://web.dev/performance-poor-connectivity/&quot;&gt;emulation tools&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/performance-poor-connectivity/&quot;&gt;Test your content under conditions of low bandwidth and high latency&lt;/a&gt;; try out content in a variety of connectivity scenarios.&lt;/li&gt;
&lt;li&gt;Try reading and interacting with your content on a low-cost phone.&lt;/li&gt;
&lt;li&gt;Ask friends and colleagues to try out your app or site.&lt;/li&gt;
&lt;li&gt;Build a simple device test lab. The &lt;a href=&quot;https://github.com/GoogleChrome/MiniMobileDeviceLab&quot; rel=&quot;noopener&quot;&gt;GitHub repo&lt;/a&gt; for Google&#39;s Mini Mobile Device Lab has instructions on how to build your own. &lt;a href=&quot;https://github.com/openstf/stf&quot; rel=&quot;noopener&quot;&gt;OpenSTF&lt;/a&gt; is a simple web application for testing websites on multiple Android devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is OpenSTF in action:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;OpenSTF interface.&quot; decoding=&quot;async&quot; height=&quot;524&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 790px) 790px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VlCiQUMryWQ9wPvCEFVd.png?auto=format&amp;w=1580 1580w&quot; width=&quot;790&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Mobile devices are increasingly used to consume content and obtain information — not just as devices for communication, games and media.&lt;/p&gt;
&lt;p&gt;This makes it increasingly import to plan content to work well on a range of viewports, and to prioritize content when considering cross-device layout, interface and interaction design.&lt;/p&gt;
&lt;h2 id=&quot;understand-data-cost&quot;&gt;Understand data cost &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#understand-data-cost&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web pages are getting bigger.&lt;/p&gt;
&lt;p&gt;According to &lt;a href=&quot;http://httparchive.org/trends.php#bytesTotal&amp;amp;reqTotal&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt;, the average page weight for the &lt;a href=&quot;http://httparchive.org/about.php#listofurls&quot; rel=&quot;noopener&quot;&gt;top one million sites&lt;/a&gt; is now over 2MB.&lt;/p&gt;
&lt;p&gt;Users avoid sites or apps perceived to be slow or expensive, so it&#39;s crucial to understand the cost of loading page and app components.&lt;/p&gt;
&lt;p&gt;Reducing page weight can also be profitable. &lt;a href=&quot;http://blog.chriszacharias.com/page-weight-matters&quot; rel=&quot;noopener&quot;&gt;Chris Zacharias from YouTube&lt;/a&gt; found that when they reduced the watch-page size from 1.2MB to 250KB:&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; Large numbers of people who were previously unable to use YouTube before were suddenly able to. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In other words, reducing page weight &lt;strong&gt;can open up whole new markets&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;calculate-page-weight&quot;&gt;Calculate page weight &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#calculate-page-weight&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are a number of tools for calculating page weight. The Chrome DevTools Network panel shows the total byte size for all resources, and can be used to ascertain weights for individual asset types. You can also check which items have been retrieved from the browser cache.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Chrome DevTools Network panel showing resource sizes.&quot; decoding=&quot;async&quot; height=&quot;322&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOTzqHUDqXexvc80y6cU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Firefox and other browsers offer similar tools.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPagetest&lt;/a&gt; provides the ability to test first and subsequent page loads. You can automate testing with &lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting&quot; rel=&quot;noopener&quot;&gt;scripts&lt;/a&gt; (for example, to log in to a site) or by using their &lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/advanced-features/webpagetest-restful-apis&quot; rel=&quot;noopener&quot;&gt;RESTful APIs&lt;/a&gt;. The following example (loading &lt;a href=&quot;https://web.dev/multi-device-content/developers.google.com/web/&quot;&gt;developers.google.com/web&lt;/a&gt;) shows that caching was successful and that subsequent page loads required no additional resources.&lt;/p&gt;
&lt;p&gt;WebPagetest also gives a size and request breakdown by MIME type.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;WebPagetest pie charts showing requests and bytes by MIME type&quot; decoding=&quot;async&quot; height=&quot;146&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 587px) 587px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cJCxDLLctRo4rBCHPAa1.png?auto=format&amp;w=1174 1174w&quot; width=&quot;587&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;calculate-page-cost&quot;&gt;Calculate page cost &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-device-content/#calculate-page-cost&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For many users, data doesn&#39;t just cost bytes and performance — it costs money.&lt;/p&gt;
&lt;p&gt;The site &lt;a href=&quot;https://whatdoesmysitecost.com/&quot; rel=&quot;noopener&quot;&gt;What Does My Site Cost?&lt;/a&gt; enables you to estimate the actual financial cost of loading your site. The histogram below shows how much it costs (using a prepaid data plan) to load &lt;a href=&quot;https://www.amazon.com/&quot; rel=&quot;noopener&quot;&gt;amazon.com&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Estimated data cost in 12 countries) of loading the amazon.com homepage.&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 760px) 760px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/isVV64gZGINBHjHnHBxJ.png?auto=format&amp;w=1520 1520w&quot; width=&quot;760&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Bear in mind that this doesn&#39;t take into account affordability relative to income. Data from &lt;a href=&quot;https://blog.jana.com/2015/05/21/the-data-trap-affordable-smartphones-expensive-data/&quot; rel=&quot;noopener&quot;&gt;blog.jana.com&lt;/a&gt; shows the cost of data.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;strong&gt;500MB data plan&lt;br /&gt;cost (USD)&lt;/strong&gt;&lt;/td&gt;
    &lt;td&gt;&lt;strong&gt;Hourly minimum&lt;br /&gt;wage (USD)&lt;/strong&gt;&lt;/td&gt;
    &lt;td&gt;&lt;strong&gt;Hours of work to pay&lt;br /&gt;for 500MB data plan&lt;/strong&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;India&lt;/td&gt;
    &lt;td&gt;$3.38&lt;/td&gt;
    &lt;td&gt;$0.20&lt;/td&gt;
    &lt;td&gt;17 hours&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Indonesia&lt;/td&gt;
    &lt;td&gt;$2.39&lt;/td&gt;
    &lt;td&gt;$0.43&lt;/td&gt;
    &lt;td&gt;6 hours&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Brazil&lt;/td&gt;
    &lt;td&gt;$13.77&lt;/td&gt;
    &lt;td&gt;$1.04&lt;/td&gt;
    &lt;td&gt;13 hours&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Page weight isn&#39;t just a problem for emerging markets. In many countries, people use mobile plans with limited data, and will avoid your site or app if they perceive it to be heavy and expensive. Even &amp;quot;unlimited&amp;quot; cell and wifi data plans generally have a data limit beyond which they are blocked or throttled. For these reasons, it&#39;s best to be as transparent as possible about how much data your page consumes. The following blog post provides specific best practices: &lt;a href=&quot;https://medium.com/google-design/nurture-trust-through-cost-transparency-b61a5947d2fc&quot; rel=&quot;noopener&quot;&gt;Nurture trust through cost transparency&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The bottom line: page weight affects performance and costs money. &lt;a href=&quot;https://web.dev/performance-optimizing-content-efficiency/&quot;&gt;Optimizing content efficiency&lt;/a&gt; shows how to reduce that cost.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Understanding Low Bandwidth and High Latency</title>
    <link href="https://web.dev/performance-poor-connectivity/"/>
    <updated>2016-05-09T00:00:00Z</updated>
    <id>https://web.dev/performance-poor-connectivity/</id>
    <content type="html" mode="escaped">&lt;p&gt;It&#39;s important to understand what using your app or site feels like when
connectivity is poor or unreliable, and build accordingly. A range of tools
can help you.&lt;/p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Test with low bandwidth and high latency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#testing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An &lt;a href=&quot;http://adwords.blogspot.co.uk/2015/05/building-for-next-moment.html&quot;&gt;
increasing proportion&lt;/a&gt; of people experience the web on mobile devices. Even at home, &lt;a href=&quot;https://www.washingtonpost.com/news/the-switch/wp/2016/04/18/new-data-americans-are-abandoning-wired-home-internet/&quot;&gt;
many people are abandoning fixed broadband for mobile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this context, it&#39;s important to understand what using your app or site feels
like when connectivity is poor or unreliable. A range of software tools can help
you &lt;a href=&quot;https://stackoverflow.com/questions/1584617/simulator-or-emulator-what-is-the-difference&quot; rel=&quot;noopener&quot;&gt;emulate and simulate&lt;/a&gt;
low bandwidth and high &lt;a href=&quot;https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/&quot; rel=&quot;noopener&quot;&gt;latency&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;emulate-network-throttling&quot;&gt;Emulate network throttling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#emulate-network-throttling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When building or updating a site, you must ensure adequate performance in a variety
of connectivity conditions. Several tools can help.&lt;/p&gt;
&lt;h4 id=&quot;browser-tools&quot;&gt;Browser tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#browser-tools&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/web/tools/chrome-devtools&quot;&gt;Chrome DevTools&lt;/a&gt; lets you test your site with a
variety of upload/download speeds and &lt;a href=&quot;https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/&quot; rel=&quot;noopener&quot;&gt;round-trip times&lt;/a&gt;, using presets
or custom settings from the Network panel. See &lt;a href=&quot;https://web.dev/web/tools/chrome-devtools/network-performance&quot;&gt;Get Started with Analyze
Network Performance&lt;/a&gt; to
learn the basics.&lt;/p&gt;
&lt;img alt=&quot;Chrome DevTools throttling&quot; decoding=&quot;async&quot; height=&quot;327&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 777px) 777px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/qmYeCqk3BAGjUxX6EaIv.png?auto=format&amp;w=1554 1554w&quot; width=&quot;777&quot; /&gt;
&lt;h4 id=&quot;system-tools&quot;&gt;System tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#system-tools&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Network Link Conditioner is a preference panel available on Mac if you install
&lt;a href=&quot;https://developer.apple.com/downloads/?q=Hardware%20IO%20Tools&quot; rel=&quot;noopener&quot;&gt;Hardware IO Tools&lt;/a&gt;
for Xcode:&lt;/p&gt;
&lt;img alt=&quot;Mac Network Link Conditioner control panel&quot; decoding=&quot;async&quot; height=&quot;802&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 780px) 780px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/7laBIBHCJMICtJm2ram4.png?auto=format&amp;w=1560 1560w&quot; width=&quot;780&quot; /&gt;
&lt;img alt=&quot;Mac Network Link Conditioner settings&quot; decoding=&quot;async&quot; height=&quot;453&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 780px) 780px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bnLF2Rh2A7CLNAnbngBn.png?auto=format&amp;w=1560 1560w&quot; width=&quot;780&quot; /&gt;
&lt;img alt=&quot;Mac Network Link Conditioner custom settings&quot; decoding=&quot;async&quot; height=&quot;425&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 760px) 760px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/cMGvcZyfwvNZw6ce8bDj.png?auto=format&amp;w=1520 1520w&quot; width=&quot;760&quot; /&gt;
&lt;h4 id=&quot;device-emulation&quot;&gt;Device emulation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#device-emulation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;http://developer.android.com/tools/devices/emulator.html#netspeed&quot; rel=&quot;noopener&quot;&gt;Android Emulator&lt;/a&gt;
allows you to simulate various network conditions while running apps (including
web browsers and hybrid web apps) on Android:&lt;/p&gt;
&lt;img alt=&quot;Android Emulator&quot; decoding=&quot;async&quot; height=&quot;623&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 396px) 396px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/zrVCMkg3GHrNoR6B426n.png?auto=format&amp;w=792 792w&quot; width=&quot;396&quot; /&gt;
&lt;img alt=&quot;Android Emulator settings&quot; decoding=&quot;async&quot; height=&quot;113&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xfs1xIif9Fs9BGka0SMq.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;For iPhone, Network Link Conditioner can be used to simulate impaired network
conditions (see above).&lt;/p&gt;
&lt;h3 id=&quot;test-from-different-locations-and-networks&quot;&gt;Test from different locations and networks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#test-from-different-locations-and-networks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Connectivity performance depends on server location as well as network type.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPagetest&lt;/a&gt; is an online service that enables a set
of performance tests to be run for your site using a variety of networks and host
locations. For example, you can try out your site from a server in India on a 2G
network, or over cable from a city in the US.&lt;/p&gt;
&lt;img alt=&quot;WebPagetest settings&quot; decoding=&quot;async&quot; height=&quot;612&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f1lY7WJ6ErJSISNvYEsU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Select a location and, from advanced settings, select a connection type. You can
even automate testing using &lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting&quot; rel=&quot;noopener&quot;&gt;scripts&lt;/a&gt;
(for example, to log in to a site) or using their
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/advanced-features/webpagetest-restful-apis&quot; rel=&quot;noopener&quot;&gt;RESTful APIs&lt;/a&gt;.
This helps you to include connectivity testing into build processes or performance
logging.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.telerik.com/fiddler&quot; rel=&quot;noopener&quot;&gt;Fiddler&lt;/a&gt; supports Global proxying via
&lt;a href=&quot;http://www.geoedge.com/faq&quot; rel=&quot;noopener&quot;&gt;GeoEdge&lt;/a&gt;, and its custom rules can be used to simulate
modem speeds:&lt;/p&gt;
&lt;img alt=&quot;Fiddler proxy&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2THZAadL02oVQG98JYMs.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;test-on-an-impaired-network&quot;&gt;Test on an impaired network &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#test-on-an-impaired-network&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Software and hardware proxies enable you to emulate problematic mobile network
conditions, such as bandwidth throttling, packet delay, and random packet loss.
A shared proxy or impaired network can enable a team of developers to incorporate
real-world network testing in their workflow.&lt;/p&gt;
&lt;p&gt;Facebook&#39;s &lt;a href=&quot;http://facebook.github.io/augmented-traffic-control/&quot; rel=&quot;noopener&quot;&gt;Augmented Traffic Control&lt;/a&gt;
(ATC) is a BSD-licensed set of applications that can be used to shape traffic and
emulate impaired network conditions:&lt;/p&gt;
&lt;img alt=&quot;Facebook&amp;#x27;s Augmented Traffic Control&quot; decoding=&quot;async&quot; height=&quot;428&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/5ssAC84OwNizm2Kmk5kU.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;p&gt;Facebook even instituted &lt;a href=&quot;https://code.facebook.com/posts/1556407321275493/building-for-emerging-markets-the-story-behind-2g-tuesdays/&quot; rel=&quot;noopener&quot;&gt;2G Tuesdays&lt;/a&gt;
to help understand how people on 2G use their product. On Tuesdays, employees
get a pop-up that gives them the option to simulate a 2G connection.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.charlesproxy.com/&quot; rel=&quot;noopener&quot;&gt;Charles&lt;/a&gt; HTTP/HTTPS proxy can
be used to &lt;a href=&quot;http://www.charlesproxy.com/documentation/proxying/throttling/&quot; rel=&quot;noopener&quot;&gt;adjust bandwidth and latency&lt;/a&gt;.
Charles is commercial software, but a free trial is available.&lt;/p&gt;
&lt;img alt=&quot;Charles proxy bandwidth and latency settings&quot; decoding=&quot;async&quot; height=&quot;580&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 554px) 554px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DXtOAZk1IUuicwVdPWIa.png?auto=format&amp;w=1108 1108w&quot; width=&quot;554&quot; /&gt;
&lt;p&gt;More information about Charles is available from &lt;a href=&quot;http://codewithchris.com/tutorial-using-charles-proxy-with-your-ios-development-and-http-debugging/&quot; rel=&quot;noopener&quot;&gt;codewithchris.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;lie-fi&quot;&gt;Handle unreliable connectivity and &amp;quot;lie-fi&amp;quot; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#lie-fi&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;what-is-lie-fi&quot;&gt;What is lie-fi? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#what-is-lie-fi&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The term &lt;a href=&quot;http://www.urbandictionary.com/define.php?term=lie-fi&quot;&gt;lie-fi&lt;/a&gt;
dates back to at least 2008 (when phones looked like
&lt;a href=&quot;https://www.mobilegazette.com/2008-phones-wallchart.htm&quot; title=&quot;Images of phones from 2008&quot;&gt;this&lt;/a&gt;), and refers to connectivity that
isn&#39;t what it seems. Your browser behaves as if it has connectivity when, for
whatever reason, it doesn&#39;t.&lt;/p&gt;
&lt;p&gt;Misinterpreted connectivity can result in a poor experience as the browser
(or JavaScript) persists in trying to retrieve resources rather than giving up
and choosing a sensible fallback. Lie-fi can actually be worse than offline; at
least if a device is definitely offline, your JavaScript can take appropriate
evasive action.&lt;/p&gt;
&lt;p&gt;Lie-fi is likely to become a bigger problem as more people move to mobile and away
from fixed broadband. Recent &lt;a href=&quot;https://www.ntia.doc.gov/blog/2016/evolving-technologies-change-nature-internet-use&quot; rel=&quot;noopener&quot;&gt;US Census data&lt;/a&gt;
shows a &lt;a href=&quot;https://www.washingtonpost.com/news/the-switch/wp/2016/04/18/new-data-americans-are-abandoning-wired-home-internet/&quot; rel=&quot;noopener&quot;&gt;move away from fixed broadband&lt;/a&gt;.
The following chart shows the use of mobile internet at home in 2015 compared with 2013:&lt;/p&gt;
&lt;img alt=&quot;Chart from US census data showing the move to mobile away from fixed broadband, particularly in lower-income households&quot; decoding=&quot;async&quot; height=&quot;249&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/L8hi0MdcQ1yM0TGTFdis.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;h3 id=&quot;use-timeouts-to-handle-intermittent-connectivity&quot;&gt;Use timeouts to handle intermittent connectivity &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#use-timeouts-to-handle-intermittent-connectivity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the past, &lt;a href=&quot;http://stackoverflow.com/questions/189430/detect-that-the-internet-connection-is-offline&quot; rel=&quot;noopener&quot;&gt;hacky methods using XHR&lt;/a&gt;
have been used to test for intermittent connectivity, but service worker enables
more reliable methods to set network timeouts. This can be achieved using
&lt;a href=&quot;https://web.dev/web/tools/workbox/&quot;&gt;Workbox&lt;/a&gt; with only a few lines of code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;workboxSW&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;router&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&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;&#39;/path/to/image&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  workboxSW&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;networkFirst&lt;/span&gt;&lt;span class=&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;networkTimeoutSeconds&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;span class=&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;You can learn more about Workbox in Jeff Posnick&#39;s Chrome Dev Summit talk,
&lt;a href=&quot;https://www.youtube.com/watch?v=DtuJ55tmjps&quot; rel=&quot;noopener&quot;&gt;Workbox: Flexible PWA Libraries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/web/updates/2017/09/abortable-fetch&quot;&gt;Timeout functionality&lt;/a&gt; is also being developed
for the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/GlobalFetch/fetch&quot; rel=&quot;noopener&quot;&gt;Fetch API&lt;/a&gt;,
and the &lt;a href=&quot;https://www.w3.org/TR/streams-api/&quot; rel=&quot;noopener&quot;&gt;Streams API&lt;/a&gt; should help by optimizing
content delivery and avoiding monolithic requests. Jake Archibald gives more details
about tackling lie-fi in &lt;a href=&quot;https://youtu.be/d5_6yHixpsQ?t=6m42s&quot; rel=&quot;noopener&quot;&gt;Supercharging page load&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-poor-connectivity/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>The &lt;video&gt; and &lt;source&gt; tags</title>
    <link href="https://web.dev/video-and-source-tags/"/>
    <updated>2014-02-15T00:00:00Z</updated>
    <id>https://web.dev/video-and-source-tags/</id>
    <content type="html" mode="escaped">&lt;p&gt;You&#39;ve properly &lt;a href=&quot;https://web.dev/prepare-media/&quot;&gt;prepared a video file&lt;/a&gt; for the web. You&#39;ve given it correct
dimensions and the correct resolution. You&#39;ve even created separate WebM and
MP4 files for different browsers.&lt;/p&gt;
&lt;p&gt;For anyone to see your video, you still need to add it to a web page. Doing so
properly requires adding two HTML elements: the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/video&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;&lt;/a&gt; element and the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&lt;/a&gt; element. In addition to basics about these tags, this
article explains attributes you should add to those tags to craft a good
user experience.&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 always have the option of uploading your file to [YouTube] or [Vimeo]. In many cases, this is preferable to the procedure described here. Those services handle formatting and filetype conversion for you, as well as provide the means to embed a video in your web page. If you need to manage this yourself, read on. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;specify-a-single-file&quot;&gt;Specify a single file &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-a-single-file&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although it&#39;s not recommended, you can use the video element by itself. Always
use the &lt;code&gt;type&lt;/code&gt; attribute as shown below. The browser uses this to determine if
it can play the provided video file. If it can&#39;t, the enclosed text displays.&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;video&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;chrome.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Your browser cannot play the provided video file.&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;p&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;video&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;h3 id=&quot;specify-multiple-file-formats&quot;&gt;Specify multiple file formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-multiple-file-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Recall from &lt;a href=&quot;https://web.dev/media-file-basics/&quot;&gt;Media file basics&lt;/a&gt; that not all browsers support the same video
formats. The &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; element lets you specify multiple formats as a fallback
in case the user&#39;s browser doesn&#39;t support one of them.&lt;/p&gt;
&lt;p&gt;The example below produces the embedded video that is used as an example later
in this article.&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&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;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&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;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/mp4&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Your browser cannot play the provided video file.&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;p&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;video&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;&lt;a href=&quot;https://track-demonstration.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Try it on Glitch&lt;/a&gt; (&lt;a href=&quot;https://glitch.com/edit/#!/track-demonstration&quot; rel=&quot;noopener&quot;&gt;source&lt;/a&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; Notice in the previous example that the &lt;code&gt;controls&lt;/code&gt; attribute was introduced. This instructs browsers to allow the user to control video playback, including volume, seeking, selecting captions, and pause/resume playback among others. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You should always add a &lt;code&gt;type&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags event though it
is optional. This ensures that the browser only downloads the file that it is
capable of playing.&lt;/p&gt;
&lt;p&gt;This approach has several advantages over serving different HTML or server-side
scripting, especially on mobile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can list formats in order of preference.&lt;/li&gt;
&lt;li&gt;Client-side switching reduces latency; only one request is made to
get content.&lt;/li&gt;
&lt;li&gt;Letting the browser choose a format is simpler, quicker, and potentially
more reliable than using a server-side support database with user-agent
detection.&lt;/li&gt;
&lt;li&gt;Specifying each file source&#39;s type improves network performance; the browser
can select a video source without having to download part of the video to
&amp;quot;sniff&amp;quot; the format.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These issues are especially important in mobile contexts, where bandwidth and
latency are at a premium, and the user&#39;s patience is likely limited. Omitting
the &lt;code&gt;type&lt;/code&gt; attribute can affect performance when there are multiple sources
with unsupported types.&lt;/p&gt;
&lt;p&gt;There are a few ways you can dig into the details. Check out
&lt;a href=&quot;https://www.xiph.org/video/vid1.shtml&quot; rel=&quot;noopener&quot;&gt;A Digital Media Primer for Geeks&lt;/a&gt; to find out more about how video and audio
work on the web. You can also use &lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/&quot; rel=&quot;noopener&quot;&gt;remote debugging&lt;/a&gt; in DevTools to compare
network activity &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/video-main.html&quot; rel=&quot;noopener&quot;&gt;with type attributes&lt;/a&gt; and &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/notype.html&quot; rel=&quot;noopener&quot;&gt;without type attributes&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Be sure to check the response headers in your browser developer tools to [ensure your server reports the right MIME type]; otherwise video source type checks won&#39;t work. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;specify-start-and-end-times&quot;&gt;Specify start and end times &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-start-and-end-times&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Save bandwidth and make your site feel more responsive: use media fragments to
add start and end times to the video element.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; width=&quot;100%&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.webm#t=5,10&quot; type=&quot;video/webm&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.mp4#t=5,10&quot; type=&quot;video/mp4&quot; /&gt;
    &lt;p&gt;This browser does not support the video element.&lt;/p&gt;
  &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;To use a media fragment, add &lt;code&gt;#t=[start_time][,end_time]&lt;/code&gt; to the media URL. For
example, to play the video from seconds 5 to 10, specify:&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;source&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;video/chrome.webm#t=5,10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can also specify the times in &lt;code&gt;&amp;lt;hours&amp;gt;:&amp;lt;minutes&amp;gt;:&amp;lt;seconds&amp;gt;&lt;/code&gt;. For example,
&lt;code&gt;#t=00:01:05&lt;/code&gt; starts the video at one minute, five seconds. To play only the
first minute of video, specify &lt;code&gt;#t=,00:01:00&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can use this feature to deliver multiple views on the same video—like
cue points in a DVD–without having to encode and serve multiple files.&lt;/p&gt;
&lt;p&gt;For this feature to work, your server must support range requests and that
capability must be enabled. Most servers enable range requests by default.
Because some hosting services turn them off, you should confirm that range
requests are available for using fragments on your site.&lt;/p&gt;
&lt;p&gt;Fortunately, you can do this in your browser developer tools. In Chrome, for
instance, it&#39;s in the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/#network&quot; rel=&quot;noopener&quot;&gt;Network panel&lt;/a&gt;. Look for the &lt;code&gt;Accept-Ranges&lt;/code&gt; header and
verify that it says &lt;code&gt;bytes&lt;/code&gt;. In the image, I&#39;ve drawn a red box around this
header. If you do not see &lt;code&gt;bytes&lt;/code&gt; as the value, you&#39;ll need to contact your
hosting provider.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome DevTools screenshot: Accept-Ranges: bytes.&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Chrome DevTools screenshot: Accept-Ranges: bytes.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;include-a-poster-image&quot;&gt;Include a poster image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#include-a-poster-image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add a poster attribute to the &lt;code&gt;video&lt;/code&gt; element so that viewers have an idea of
the content as soon as the element loads, without needing to download the video
or start playback.&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;poster&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;poster.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&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;video&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;A poster can also be a fallback if the video &lt;code&gt;src&lt;/code&gt; is broken or if none of the
supplied video formats are supported. The only downside to a poster images is an
additional file request, which consumes some bandwidth and requires rendering.
For more information see &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/&quot; rel=&quot;noopener&quot;&gt;Efficiently encode images&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Without a fallback poster, the video just looks broken.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Without a fallback poster, the video just looks broken.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A fallback poster makes it seem as if the first frame has been captured.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;A fallback poster makes it seem as if the first frame has been captured.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;h3 id=&quot;ensure-videos-dont-overflow-containers&quot;&gt;Ensure videos don&#39;t overflow containers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#ensure-videos-dont-overflow-containers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When video elements are too big for the viewport, they may overflow their
container, making it impossible for the user to see the content or use the
controls.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Android Chrome screenshot, portrait: unstyled video element overflows viewport.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 338px) 338px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=676 676w&quot; width=&quot;338&quot; /&gt;
    &lt;figcaption&gt;Android Chrome screenshot, portrait: unstyled video element overflows
    viewport.&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Android Chrome screenshot, landscape: unstyled video element overflows viewport.&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/bCiZsNkZNsAhWbOBsLCs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;Android Chrome screenshot, landscape: unstyled video element overflows
    viewport.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;You can control video dimensions using CSS. If CSS does not meet all of your
needs, JavaScript libraries and plugins such as &lt;a href=&quot;http://fitvidsjs.com/&quot; rel=&quot;noopener&quot;&gt;FitVids&lt;/a&gt; (outside the scope
of this article) can help, even for videos from YouTube and other sources.
Unfortunately, these resources can increase your &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/&quot; rel=&quot;noopener&quot;&gt;network payload sizes&lt;/a&gt; with
negative consequences for your revenues and your users&#39; wallets.&lt;/p&gt;
&lt;p&gt;For simple uses like the ones I&#39;m describing here, use &lt;a href=&quot;https://web.dev/responsive-web-design-basics/#media-queries&quot;&gt;CSS media queries&lt;/a&gt; to
specify the size of elements depending on the viewport dimensions; &lt;code&gt;max-width: 100%&lt;/code&gt; is your friend.&lt;/p&gt;
&lt;p&gt;For media content in iframes (such as YouTube videos), try a responsive approach
(like the one &lt;a href=&quot;http://avexdesigns.com/responsive-youtube-embed/&quot; rel=&quot;noopener&quot;&gt;proposed by John Surdakowski&lt;/a&gt;).&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t force element sizing that results in an [aspect ratio] different from the original video. Squashed or stretched videos looks awful. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;css&quot;&gt;CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#css&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.video-container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 56.25%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.video-container iframe,&lt;br /&gt;.video-container object,&lt;br /&gt;.video-container embed&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;html&quot;&gt;HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#html&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token 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;video-container&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;iframe&lt;/span&gt;&lt;br /&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;//www.youtube.com/embed/l-BA9Ee2XuM&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;frameborder&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;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;width&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;560&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;height&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;315&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/responsive_embed.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Compare the &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/responsive_embed.html&quot; rel=&quot;noopener&quot;&gt;responsive sample&lt;/a&gt; to the &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/unyt.html&quot; rel=&quot;noopener&quot;&gt;unresponsive version&lt;/a&gt;. As you can see,
the unresponsive version isn&#39;t a great user experience.&lt;/p&gt;
&lt;h3 id=&quot;device-orientation&quot;&gt;Device orientation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#device-orientation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Device orientation isn&#39;t an issue for desktop monitors or laptops, but it&#39;s
hugely important when considering web page design for mobile devices and
tablets.&lt;/p&gt;
&lt;p&gt;Safari on iPhone does a good job of switching between portrait and landscape
orientation:&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPhone, portrait.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 338px) 338px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=676 676w&quot; width=&quot;338&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPhone, portrait.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPhone, landscape.&quot; decoding=&quot;async&quot; height=&quot;338&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPhone, landscape.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Device orientation on an iPad and Chrome on Android can be problematic.
For example, without any customization a video playing on an iPad in landscape
orientation looks like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPad, landscape.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPad, landscape.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Setting the video &lt;code&gt;width: 100%&lt;/code&gt; or &lt;code&gt;max-width: 100%&lt;/code&gt; with CSS can resolve
many device orientation layout problems.&lt;/p&gt;
&lt;h3 id=&quot;autoplay&quot;&gt;Autoplay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#autoplay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;autoplay&lt;/code&gt; attribute controls whether the browser downloads and plays a
video immediately. The precise way it works depends on the platform and browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Chrome: Depends on multiple factors including but not limited to whether the
viewing is on desktop and whether the mobile user has added your site or app
to their homescreen. For details, see &lt;a href=&quot;https://web.dev/autoplay-best-practices/&quot;&gt;Autoplay best practices&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firefox: Blocks all video and sound, but gives users the ability to relax these
restrictions for either all sites or particular sites. For details, see
&lt;a href=&quot;https://support.mozilla.org/en-US/kb/block-autoplay&quot; rel=&quot;noopener&quot;&gt;Allow or block media autoplay in Firefox&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Safari: Has historically required a user gesture, but has been relaxing that
requirement in recent versions. For details, see
&lt;a href=&quot;https://webkit.org/blog/6784/new-video-policies-for-ios/&quot; rel=&quot;noopener&quot;&gt;New &amp;lt;video&amp;gt; Policies for iOS&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even on platforms where autoplay is possible, you need to consider whether it&#39;s
a good idea to enable it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data usage can be expensive.&lt;/li&gt;
&lt;li&gt;Playing media before the user wants it can hog bandwidth and CPU, and thereby
delay page rendering.&lt;/li&gt;
&lt;li&gt;Users may be in a context where playing video or audio is intrusive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;preload&quot;&gt;Preload &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#preload&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;preload&lt;/code&gt; attribute provides a hint to the browser as to how much
information or content to preload.&lt;/p&gt;
&lt;table class=&quot;responsive&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Value&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;The user might chose not to watch the video, so don&#39;t
      preload anything.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Metadata (duration, dimensions, text tracks) should be
      preloaded, but with minimal video.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Downloading the entire video right away is considered
      desirable. An empty string produces the same result.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;preload&lt;/code&gt; attribute has different effects on different platforms.
For example, Chrome buffers 25 seconds of video on desktop but none on iOS or
Android. This means that on mobile, there may be playback startup delays
that don&#39;t happen on desktop. See &lt;a href=&quot;https://web.dev/fast-playback-with-preload/&quot;&gt;Fast playback with audio and video preload&lt;/a&gt; or
&lt;a href=&quot;https://www.stevesouders.com/blog/2013/04/12/html5-video-preload/&quot; rel=&quot;noopener&quot;&gt;Steve Souders&#39; blog&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;Now that you know how to add media to your web page it&#39;s time to learn about
&lt;a href=&quot;https://web.dev/media-accessibility/&quot;&gt;Media accessibility&lt;/a&gt; where you will add captions to your video for hearing
impaired, or when playing the audio is not a viable option.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author><author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Send data between browsers with WebRTC data channels</title>
    <link href="https://web.dev/webrtc-datachannels/"/>
    <updated>2014-02-04T00:00:00Z</updated>
    <id>https://web.dev/webrtc-datachannels/</id>
    <content type="html" mode="escaped">&lt;p&gt;Sending data between two browsers for communication, gaming, or file transfer can be a rather involved process. It requires setting up and paying for a server to relay data, and perhaps scaling this to multiple data centers. In this scenario, there is potential for high latency and it&#39;s difficult to keep data private.&lt;/p&gt;
&lt;p&gt;These problems can be alleviated by using WebRTC&#39;s &lt;code&gt;RTCDataChannel&lt;/code&gt; API to transfer data directly from one peer to another. This article covers the basics of how to set up and use data channels, as well as the common use cases on the web today.&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; To make the most of this article, you need some knowledge of the &lt;code&gt;RTCPeerConnection&lt;/code&gt; API, and an understanding of how STUN, TURN, and signaling work. For more information, see &lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/basics/&quot;&gt;Getting Started With WebRTC&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;why-another-data-channel&quot;&gt;Why another data channel? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#why-another-data-channel&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We have &lt;a href=&quot;https://www.html5rocks.com/tutorials/websockets/basics/&quot; rel=&quot;noopener&quot;&gt;WebSocket&lt;/a&gt;, &lt;a href=&quot;https://www.html5rocks.com/tutorials/file/xhr2/&quot; rel=&quot;noopener&quot;&gt;AJAX&lt;/a&gt; and &lt;a href=&quot;https://www.html5rocks.com/tutorials/eventsource/basics/&quot; rel=&quot;noopener&quot;&gt;Server Sent Events&lt;/a&gt;. Why do we need another communication channel? WebSocket is bidirectional, but all these technologies are designed for communication to or from a server.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; takes a different approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It works with the &lt;code&gt;RTCPeerConnection&lt;/code&gt; API, which enables peer-to-peer connectivity. This can result in lower latency - no intermediary server and fewer &#39;hops&#39;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Stream_Control_Transmission_Protocol#Features&quot; rel=&quot;noopener&quot;&gt;Stream Control Transmission Protocol&lt;/a&gt; (SCTP), allowing configurable delivery semantics-out-of-order delivery and retransmit configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; is available now with SCTP support on desktop and Android in Google Chrome, Opera, and Firefox.&lt;/p&gt;
&lt;h2 id=&quot;a-caveat-signaling,-stun,-and-turn&quot;&gt;A caveat: Signaling, STUN, and TURN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#a-caveat-signaling,-stun,-and-turn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebRTC enables peer-to-peer communication, but it still needs servers for &lt;strong&gt;signaling&lt;/strong&gt; to exchange media and network metadata to bootstrap a peer connection.&lt;/p&gt;
&lt;p&gt;WebRTC copes with NATs and firewalls with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/basics/#ice&quot; rel=&quot;noopener&quot;&gt;The ICE framework&lt;/a&gt; to establish the best possible network path between peers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/basics/#stun&quot; rel=&quot;noopener&quot;&gt;STUN servers&lt;/a&gt; to ascertain a publicly accessible IP and port for each peer.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webrtc.org/getting-started/turn-server&quot; rel=&quot;noopener&quot;&gt;TURN servers&lt;/a&gt; if direct connection fails and data relaying is required.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information about how WebRTC works with servers for signaling and networking, see &lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/infrastructure/&quot; rel=&quot;noopener&quot;&gt;WebRTC inthe real world: STUN, TURN, and signaling&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-capabilities&quot;&gt;The capabilities &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#the-capabilities&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;RTCDataChannel&lt;/code&gt; API supports a flexible set of data types. The API is designed to mimic WebSocket exactly, and &lt;code&gt;RTCDataChannel&lt;/code&gt; supports &lt;a href=&quot;https://www.w3.org/TR/webrtc/#widl-RTCDataChannel-send-void-DOMString-data&quot; rel=&quot;noopener&quot;&gt;strings&lt;/a&gt; as well as some of the binary types in JavaScript, such as &lt;a href=&quot;https://www.w3.org/TR/webrtc/#widl-RTCDataChannel-send-void-Blob-data&quot; rel=&quot;noopener&quot;&gt;Blob&lt;/a&gt;, &lt;a href=&quot;https://www.w3.org/TR/webrtc/#widl-RTCDataChannel-send-void-ArrayBuffer-data&quot; rel=&quot;noopener&quot;&gt;ArrayBuffer&lt;/a&gt;, and &lt;a href=&quot;https://www.w3.org/TR/webrtc/#widl-RTCDataChannel-send-void-ArrayBufferView-data&quot; rel=&quot;noopener&quot;&gt;ArrayBufferView&lt;/a&gt;. These types can be helpful when working with file transfer and multiplayer gaming.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; can work in unreliable and unordered mode (analogous to User Datagram Protocol or UDP), reliable and ordered mode (analogous to Transmission Control Protocol or TCP), and partial reliable modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reliable and ordered mode guarantees the transmission of messages and also the order in which they are delivered&lt;/strong&gt;. This takes extra overhead, thus potentially making this mode slower.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unreliable and unordered mode does not guarantee every message gets to the other side nor what order they get there in&lt;/strong&gt;. This removes the overhead, allowing this mode to work much faster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partial reliable mode guarantees the transmission of message under a specific condition, such as a retransmit timeout or a maximum amount of retransmissions&lt;/strong&gt;. The ordering of messages is also configurable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Performance for the first two modes is about the same when there are no packet losses. However, in reliable and ordered mode, a lost packet causes other packets to get blocked behind it, and the lost packet might be stale by the time that it is retransmitted and arrives. It is, of course, possible to use multiple data channels within the same app, each with their own reliable or unreliable semantics.&lt;/p&gt;
&lt;p&gt;Here&#39;s a helpful table from &lt;a href=&quot;https://www.oreilly.com/library/view/high-performance-browser/9781449344757/&quot; rel=&quot;noopener&quot;&gt;High Performance Browser Networking&lt;/a&gt; by &lt;a href=&quot;https://www.igvita.com/&quot; rel=&quot;noopener&quot;&gt;Ilya Grigorik&lt;/a&gt;:&lt;/p&gt;
&lt;table&gt;
    &lt;thead&gt;
        &lt;th&gt;
            &lt;/th&gt;&lt;/thead&gt;&lt;td&gt;TCP&lt;/td&gt;&lt;td&gt;UDP&lt;/td&gt;&lt;td&gt;SCTP&lt;/td&gt;
        
    
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Reliability&lt;/td&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;td&gt;Unreliable&lt;/td&gt;&lt;td&gt;Configurable&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Delivery&lt;/td&gt;&lt;td&gt;Ordered&lt;/td&gt;&lt;td&gt;Unordered&lt;/td&gt;&lt;td&gt;Configurable&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Transmission&lt;/td&gt;&lt;td&gt;Byte-oriented&lt;/td&gt;&lt;td&gt;Message-oriented&lt;/td&gt;&lt;td&gt;Message-oriented&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Flow control&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Congestion control&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;&lt;tbody&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Next, you learn how to configure &lt;code&gt;RTCDataChannel&lt;/code&gt; to use reliable and ordered or unreliable and unordered mode.&lt;/p&gt;
&lt;h2 id=&quot;configuring-data-channels&quot;&gt;Configuring data channels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#configuring-data-channels&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are several simple demos of &lt;code&gt;RTCDataChannel&lt;/code&gt; online:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://simpl.info/dc&quot; rel=&quot;noopener&quot;&gt;simpl.info &lt;code&gt;RTCDataChannel&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webrtc.github.io/samples/src/content/datachannel/basic/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Transmit text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webrtc.github.io/samples/src/content/datachannel/filetransfer/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Transfer a file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In these examples, the browser makes a peer connection to itself, then creates a data channel and sends a message through the peer connection. It is then creating a data channel and sending the message along the peer connection. Finally, your message appears in the box on the other side of the page!&lt;/p&gt;
&lt;p&gt;The code to get started with this is short:&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; peerConnection &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&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;// Establish your peer connection using your signaling channel here&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dataChannel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;  peerConnection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createDataChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;myLabel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataChannelOptions&lt;span 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;dataChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onerror&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 parameter&quot;&gt;error&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;  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;&quot;Data Channel Error:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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;br /&gt;dataChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&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;  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;&quot;Got Data Channel Message:&quot;&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;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;dataChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onopen&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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  dataChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&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;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;dataChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onclose&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 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;&lt;span class=&quot;token string&quot;&gt;&quot;The Data Channel is Closed&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 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;dataChannel&lt;/code&gt; object is created from an already-established peer connection. It can be created before or after signaling happens. You then pass in a label to distinguish this channel from others and a set of optional configuration settings:&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; dataChannelOptions &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;ordered&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;span class=&quot;token comment&quot;&gt;// do not guarantee order&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;maxPacketLifeTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// in milliseconds&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;It is also possible to add a &lt;code&gt;maxRetransmits&lt;/code&gt; option (the number of times to try before failing), but you can only specify maxRetransmits or maxPacketLifeTime, not both. For UDP semantics, set &lt;code&gt;maxRetransmits&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;ordered&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;. For more information, see these IETF RFCs: &lt;a href=&quot;https://tools.ietf.org/html/rfc4960&quot; rel=&quot;noopener&quot;&gt;Stream Control Transmission Protocol&lt;/a&gt; and &lt;a href=&quot;https://tools.ietf.org/html/rfc3758&quot; rel=&quot;noopener&quot;&gt;Stream Control Transmission Protocol Partial Reliability Extension&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ordered&lt;/code&gt;: if the data channel should guarantee order or not&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxPacketLifeTime&lt;/code&gt;: the maximum time to try and retransmit a failed message&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxRetransmits&lt;/code&gt;: the maximum number of times to try and retransmit a failed message&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protocol&lt;/code&gt;: allows a subprotocol to be used, which provides meta information toward the app&lt;/li&gt;
&lt;li&gt;&lt;code&gt;negotiated&lt;/code&gt;: if set to true, removes the automatic setting up of a data channel on the other peer, providing your own way to create a data channel with the same ID on the other side&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt;: allows you to provide your own ID for the channel that can only be used in combination with &lt;code&gt;negotiated&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only options that most people need to use are the first three: &lt;code&gt;ordered&lt;/code&gt;, &lt;code&gt;maxPacketLifeTime&lt;/code&gt;, and &lt;code&gt;maxRetransmits&lt;/code&gt;. With &lt;a href=&quot;https://en.wikipedia.org/wiki/Stream_Control_Transmission_Protocol&quot; rel=&quot;noopener&quot;&gt;SCTP&lt;/a&gt; (now used by all browsers that support WebRTC) reliable and ordered is true by default. It makes sense to use unreliable and unordered if you want full control from the app layer, but in most cases, partial reliability is helpful.&lt;/p&gt;
&lt;p&gt;Note that, as with WebSocket, &lt;code&gt;RTCDataChannel&lt;/code&gt; fires events when a connection is established, closed, or errors, and when it receives a message from the other peer.&lt;/p&gt;
&lt;h2 id=&quot;is-it-safe&quot;&gt;Is it safe? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#is-it-safe&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Encryption is mandatory for all WebRTC components. With &lt;code&gt;RTCDataChannel&lt;/code&gt;, all data is secured with &lt;a href=&quot;https://en.wikipedia.org/wiki/Datagram_Transport_Layer_Security&quot; rel=&quot;noopener&quot;&gt;Datagram Transport Layer Security&lt;/a&gt; (DTLS). DTLS is a derivative of SSL, meaning your data will be as secure as using any standard SSL-based connection. DTLS is standardized and built into all browsers that support WebRTC. For more information, see &lt;a href=&quot;https://wiki.wireshark.org/DTLS&quot; rel=&quot;noopener&quot;&gt;Wireshark wiki&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;change-how-you-think-about-data&quot;&gt;Change how you think about data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#change-how-you-think-about-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Handling large amounts of data can be a pain point in JavaScript. As the developers of &lt;a href=&quot;https://github.com/Peer5/ShareFest&quot; rel=&quot;noopener&quot;&gt;Sharefest&lt;/a&gt; pointed out, this required thinking about data in a new way. If you are transferring a file that is larger than the amount of memory you have available, you have to think about new ways to save this information. This is where technologies, such as the &lt;a href=&quot;https://www.html5rocks.com/tutorials/file/filesystem/&quot; rel=&quot;noopener&quot;&gt;FileSystem API&lt;/a&gt;, come into play, as you see next.&lt;/p&gt;
&lt;h2 id=&quot;build-a-file-sharing-app&quot;&gt;Build a file-sharing app &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#build-a-file-sharing-app&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Creating a web app that can share files in the browser is now possible with &lt;code&gt;RTCDataChannel&lt;/code&gt;. Building on top of &lt;code&gt;RTCDataChannel&lt;/code&gt; means that the transferred file data is encrypted and does not touch an app provider&#39;s servers. This functionality, combined with the possibility of connecting to multiple clients for faster sharing, makes WebRTC file sharing a strong candidate for the web.&lt;/p&gt;
&lt;p&gt;Several steps are required to make a successful transfer:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read a file in JavaScript using the &lt;a href=&quot;https://www.html5rocks.com/tutorials/file/dndfiles/&quot; rel=&quot;noopener&quot;&gt;File API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Make a peer connection between clients with &lt;code&gt;RTCPeerConnection&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a data channel between clients with &lt;code&gt;RTCDataChannel&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are several points to consider when trying to send files over &lt;code&gt;RTCDataChannel&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size:&lt;/strong&gt; if file size is reasonably small and can be stored and loaded as one Blob, you can load into memory using the File API and then send the file over a reliable channel as is (though bear in mind that browsers impose limits on maximum transfer size). As file size gets larger, things get trickier. When a chunking mechanism is required, file chunks are loaded and sent to another peer, accompanied with &lt;code&gt;chunkID&lt;/code&gt; metadata so the peer can recognize them. Note that, in this case, you also need to save the chunks first to offline storage (for example, using the FileSystem API) and save it to the user&#39;s disk only when you have the file in its entirety.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chunk size:&lt;/strong&gt; these are the smallest &amp;quot;atoms&amp;quot; of data for your app. Chunking is required because there is currently a send size limit (though this will be fixed in a future version of data channels). The current recommendation for maximum chunk size is 64KiB.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the file is fully transferred to the other side, it can be downloaded using an anchor tag:&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;saveFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &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;a&#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;  link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&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;  link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;download &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;File Name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&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;These file-sharing apps on &lt;a href=&quot;https://pubnub.github.io/rtc-pubnub-fileshare/&quot; rel=&quot;noopener&quot;&gt;PubShare&lt;/a&gt; and &lt;a href=&quot;https://github.com/Peer5/ShareFest&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt; use this technique. They are both open source and provide a good foundation for a file-sharing app based on &lt;code&gt;RTCDataChannel&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;so-what-can-you-do&quot;&gt;So what can you do? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#so-what-can-you-do&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; opens the doors to new ways to build apps for file sharing, multiplayer gaming, and content delivery.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Peer-to-peer file sharing as previously described&lt;/li&gt;
&lt;li&gt;Multiplayer gaming, paired with other technologies, such as WebGL, as seen in Mozilla&#39;s &lt;a href=&quot;https://hacks.mozilla.org/2013/03/webrtc-data-channels-for-great-multiplayer/&quot; rel=&quot;noopener&quot;&gt;BananaBread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Content delivery as being reinvented by &lt;a href=&quot;https://techcrunch.com/2013/12/17/yahoo-acquires-peercdn/&quot; rel=&quot;noopener&quot;&gt;PeerCDN&lt;/a&gt;, a framework that delivers web assets through peer-to-peer data communication&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;change-the-way-you-build-apps&quot;&gt;Change the way you build apps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#change-the-way-you-build-apps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can now provide more-engaging apps by using high-performance, low-latency connections through &lt;code&gt;RTCDataChannel&lt;/code&gt;. Frameworks, such as &lt;a href=&quot;https://peerjs.com/&quot; rel=&quot;noopener&quot;&gt;PeerJS&lt;/a&gt; and the &lt;a href=&quot;https://github.com/pubnub/webrtc&quot; rel=&quot;noopener&quot;&gt;PubNub WebRTC SDK&lt;/a&gt;, make &lt;code&gt;RTCDataChannel&lt;/code&gt; easier to implement and the API now has wide support across platforms.&lt;/p&gt;
&lt;p&gt;The advent of &lt;code&gt;RTCDataChannel&lt;/code&gt; can change the way that you think about data transfer in the browser.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-datachannels/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/basics/&quot; rel=&quot;noopener&quot;&gt;Getting started with WebRTC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/infrastructure/&quot; rel=&quot;noopener&quot;&gt;WebRTC in the real world: STUN, TURN, and signaling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bit.ly/webrtcwebaudio&quot; rel=&quot;noopener&quot;&gt;WebRTC and Web Audio resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/webrtc/#peer-to-peer-data-api&quot; rel=&quot;noopener&quot;&gt;Peer-to-peer Data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04&quot; rel=&quot;noopener&quot;&gt;IETF WebRTC DCP Draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bloggeek.me/send-file-webrtc-data-api/&quot; rel=&quot;noopener&quot;&gt;How to send a File Using WebRTC Data API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bloggeek.me/webrtc-data-channel-uses/&quot; rel=&quot;noopener&quot;&gt;7 Creative Uses of WebRTC’s Data Channel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/demos/detail/bananabread&quot; rel=&quot;noopener&quot;&gt;BananaBread&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author><author>
      <name>Dan Ristic</name>
    </author>
  </entry>
  
  <entry>
    <title>What is EME?</title>
    <link href="https://web.dev/media-eme/"/>
    <updated>2014-01-16T00:00:00Z</updated>
    <id>https://web.dev/media-eme/</id>
    <content type="html" mode="escaped">&lt;p&gt;Encrypted Media Extensions provides an API that enables web applications to
interact with content protection systems, to allow playback of encrypted audio
and video.&lt;/p&gt;
&lt;p&gt;EME is designed to enable the same app and encrypted files to be used in any
browser, regardless of the underlying protection system. The former is made
possible by the standardized APIs and flow while the latter is made possible by
the concept of
&lt;a href=&quot;https://www.html5rocks.com/en/tutorials/eme/basics/#common-encryption&quot; rel=&quot;noopener&quot;&gt;Common Encryption&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;EME is an extension to the HTMLMediaElement specification — hence the name.
Being an &#39;extension&#39; means that browser support for EME is optional: if a
browser does not support encrypted media, it will not be able to play encrypted
media, but EME is not required for HTML spec compliance. From
&lt;a href=&quot;https://w3c.github.io/encrypted-media/&quot; rel=&quot;noopener&quot;&gt;the EME spec&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;This proposal extends
&lt;a href=&quot;http://www.w3.org/TR/html5/embedded-content-0.html#htmlmediaelement&quot; rel=&quot;noopener&quot;&gt;HTMLMediaElement&lt;/a&gt;
providing APIs to control playback of protected content.&lt;/p&gt;
&lt;p&gt;The API supports use cases ranging from simple clear key decryption to high
value video (given an appropriate user agent implementation). License/key
exchange is controlled by the application, facilitating the development of
robust playback applications supporting a range of content decryption and
protection technologies.&lt;/p&gt;
&lt;p&gt;This specification does not define a content protection or Digital Rights
Management system. Rather, it defines a common API that may be used to
discover, select and interact with such systems as well as with simpler
content encryption systems. Implementation of Digital Rights Management is not
required for compliance with this specification: only the Clear Key system is
required to be implemented as a common baseline.&lt;/p&gt;
&lt;p&gt;The common API supports a simple set of content encryption capabilities,
leaving application functions such as authentication and authorization to page
authors. This is achieved by requiring content protection system-specific
messaging to be mediated by the page rather than assuming out-of-band
communication between the encryption system and a license or other server.&lt;/p&gt;
&lt;p&gt;EME implementations use the following external components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key System:&lt;/strong&gt; A content protection (DRM) mechanism. EME doesn&#39;t define Key
Systems themselves, apart from Clear Key (more about that &lt;a href=&quot;https://web.dev/media-eme/#clear-key&quot;&gt;below&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Decryption Module (CDM):&lt;/strong&gt; A client-side software or hardware
mechanism that enables playback of encrypted media. As with Key Systems, EME
doesn&#39;t define any CDMs, but provides an interface for applications to
interact with CDMs that are available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;License (Key) server:&lt;/strong&gt; Interacts with a CDM to provide keys to decrypt
media. Negotiation with the license server is the responsibility of the
application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Packaging service:&lt;/strong&gt; Encodes and encrypts media for distribution/consumption.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that an application using EME interacts with a license server to get keys
to enable decryption, but user identity and authentication are not part of EME.
Retrieval of keys to enable media playback happens after (optionally)
authenticating a user. Services such as Netflix must authenticate users within
their web application: when a user signs into the application, the application
determines the user&#39;s identity and privileges.&lt;/p&gt;
&lt;h2 id=&quot;how-does-eme-work&quot;&gt;How does EME work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#how-does-eme-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s how the components of EME interact, corresponding to the
&lt;a href=&quot;https://web.dev/media-eme/#getting-a-key-from-a-license-server&quot;&gt;code example below&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;If multiple formats or codecs are available,
&lt;a href=&quot;https://www.w3.org/TR/media-source/#dom-mediasource-istypesupported&quot; rel=&quot;noopener&quot;&gt;MediaSource.isTypeSupported()&lt;/a&gt; or
&lt;a href=&quot;https://dev.w3.org/html5/spec-preview/media-elements.html#dom-navigator-canplaytype&quot; rel=&quot;noopener&quot;&gt;HTMLMediaElement.canPlayType()&lt;/a&gt;
can both be used to select the right one.
However, the CDM may only support a subset of what the browser supports for
unencrypted content. It&#39;s best to negotiate a MediaKeys configuration before
selecting a format and codec. If the application waits for the encrypted event
but then MediaKeys shows it can&#39;t handle the chosen format/codec, it may be
too late to switch without interrupting playback.&lt;/p&gt;
&lt;p&gt;The recommended flow is to negotiate MediaKeys first, using
MediaKeysSystemAccess.getConfiguration() to find out the negotiated
configuration.&lt;/p&gt;
&lt;p&gt;If there is only one format/codec to choose from, then there&#39;s no need for
getConfiguration(). However, it&#39;s still preferable to set up MediaKeys first.
The only reason to wait for the encrypted event is if there is no way of
knowing whether the content is encrypted or not, but in practice that&#39;s
unlikely.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;A web application attempts to play audio or video that has one or more
encrypted streams.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The browser recognizes that the media is encrypted (see box below for how
that happens) and fires an encrypted event with metadata (initData) obtained
from the media about the encryption.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The application handles the encrypted event:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If no MediaKeys object has been associated with the media element, first
select an available Key System by using navigator.requestMediaKeySystemAccess()
to check what Key Systems are available, then create a MediaKeys object
for an available Key System via a MediaKeySystemAccess object. Note that
initialization of the MediaKeys object should happen before the first
encrypted event. Getting a license server URL is done by the app
independently of selecting an available key system. A MediaKeys object
represents all the keys available to decrypt the media for an audio or
video element. It represents a CDM instance and provides access to the
CDM, specifically for creating key sessions, which are used to obtain
keys from a license server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the MediaKeys object has been created, assign it to the media
element: setMediaKeys() associates the MediaKeys object with an
HTMLMediaElement, so that its keys can be used during playback, i.e.
during decoding.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The app creates a MediaKeySession by calling createSession() on the
MediaKeys. This creates a MediaKeySession, which represents the lifetime of
a license and its key(s).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The app generates a license request by passing the media data obtained in
the encrypted handler to the CDM, by calling generateRequest() on the
MediaKeySession.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CDM fires a message event: a request to acquire a key from a license
server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The MediaKeySession object receives the message event and the application
sends a message to the license server (via XHR, for example).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The application receives a response from the license server and passes the
data to the CDM using the update() method of the MediaKeySession.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CDM decrypts the media using the keys in the license. A valid key may be
used, from any session within the MediaKeys associated with the media
element. The CDM will access the key and policy, indexed by Key ID.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Media playback resumes.&lt;/p&gt;
&lt;p&gt;How does the browser know that media is encrypted?&lt;/p&gt;
&lt;p&gt;This information is in the metadata of the media container file, which will be
in a format such as ISO BMFF or WebM. For ISO BMFF this means header metadata,
called the protection scheme information box. WebM uses the Matroska
ContentEncryption element, with some WebM-specific additions. Guidelines are
provided for each container in an EME-specific registry.&lt;/p&gt;
&lt;p&gt;Note that there may be multiple messages between the CDM and the license server,
and all communication in this process is opaque to the browser and application:
messages are only understood by the CDM and license server, although the app
layer can see
&lt;a href=&quot;https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType&quot; rel=&quot;noopener&quot;&gt;what type of message&lt;/a&gt;
the CDM is sending. The license request contains proof
of the CDM&#39;s validity (and trust relationship) as well as a key to use when
encrypting the content key(s) in the resulting license.&lt;/p&gt;
&lt;h2 id=&quot;but-what-do-cdms-actually-do&quot;&gt;But what do CDMs actually do? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#but-what-do-cdms-actually-do&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An EME implementation does not in itself provide a way to decrypt media: it
simply provides an API for a web application to interact with Content Decryption
Modules.&lt;/p&gt;
&lt;p&gt;What CDMs actually do is not defined by the EME spec, and a CDM may handle
decoding (decompression) of media as well as decryption. From least to most
robust, there are several potential options for CDM functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Decryption only, enabling playback using the normal media pipeline, for example
via a &amp;lt;video&amp;gt; element.&lt;/li&gt;
&lt;li&gt;Decryption and decoding, passing video frames to the browser for rendering.&lt;/li&gt;
&lt;li&gt;Decryption and decoding, rendering directly in the hardware (for example, the GPU).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are multiple ways to make a CDM available to a web app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bundle a CDM with the browser.&lt;/li&gt;
&lt;li&gt;Distribute a CDM separately.&lt;/li&gt;
&lt;li&gt;Build a CDM into the operating system.&lt;/li&gt;
&lt;li&gt;Include a CDM in firmware.&lt;/li&gt;
&lt;li&gt;Embed a CDM in hardware.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How a CDM is made available is not defined by the EME spec, but in all cases the
browser is responsible for vetting and exposing the CDM.&lt;/p&gt;
&lt;p&gt;EME doesn&#39;t mandate a particular Key System; among current desktop and mobile
browsers, Chrome supports Widevine and IE11 supports PlayReady.&lt;/p&gt;
&lt;h2 id=&quot;getting-a-key-from-a-license-server&quot;&gt;Getting a key from a license server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#getting-a-key-from-a-license-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In typical commercial use, content will be encrypted and encoded using a
packaging service or tool. Once the encrypted media is made available online, a
web client can obtain a key (contained within a license) from a license server
and use the key to enable decryption and playback of the content.&lt;/p&gt;
&lt;p&gt;The following code (adapted from the
&lt;a href=&quot;http://w3c.github.io/encrypted-media/#examples&quot; rel=&quot;noopener&quot;&gt;spec examples&lt;/a&gt;)
shows how an application can select an appropriate key system and obtain a key
from a license server.&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; video &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;video&#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;var&lt;/span&gt; config &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 literal-property property&quot;&gt;initDataTypes&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;webm&#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;videoCapabilities&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 literal-property property&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video/webm; codecs=&quot;vp09.00.10.08&quot;&#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;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;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaKeys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;org.w3.clearkey&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;          config&lt;span class=&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keySystemAccess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaKeys&lt;/span&gt;&lt;span class=&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;          promise&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;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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unable to create MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          promise&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;br /&gt;            &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&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; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;createdMediaKeys&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unable to set MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          promise&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;br /&gt;            &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; initData &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;Uint8Array&lt;/span&gt;&lt;span class=&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 punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keySession &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; createdMediaKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createSession&lt;/span&gt;&lt;span class=&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;              keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&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 keyword&quot;&gt;return&lt;/span&gt; keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;webm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initData&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token string&quot;&gt;&#39;Unable to create or initialize key session&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMessage&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;var&lt;/span&gt; keySession &lt;span class=&quot;token operator&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; license &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;Uint8Array&lt;/span&gt;&lt;span class=&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 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;      keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;license&lt;span class=&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;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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;update() failed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;common-encryption&quot;&gt;Common encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#common-encryption&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Common Encryption solutions allow content providers to encrypt and package their
content once per container/codec and use it with a variety of Key Systems, CDMs
and clients: that is, any CDM that supports Common Encryption. For example, a
video packaged using Playready could be played back in a browser using a
Widevine CDM obtaining a key from a Widevine license server.&lt;/p&gt;
&lt;p&gt;This is in contrast to legacy solutions that would only work with a complete
vertical stack, including a single client that often also included an
application runtime.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;Common Encryption&lt;/a&gt;
(CENC) is an &lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;ISO standard&lt;/a&gt;
defining a protection scheme for ISO BMFF; a similar concept applies to WebM.&lt;/p&gt;
&lt;h2 id=&quot;clear-key&quot;&gt;Clear Key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#clear-key&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although EME does not define DRM functionality, the spec currently mandates that
all browsers supporting EME must implement Clear Key. Using this system, media
can be encrypted with a key and then played back simply by providing that key.
Clear Key can be built into the browser: it does not require the use of a
separate decryption module.&lt;/p&gt;
&lt;p&gt;While not likely to be used for many types of commercial content, Clear Key is
fully interoperable across all browsers that support EME. It is also handy for
testing EME implementations, and applications using EME, without the need to
request a content key from a license server. There is a simple Clear Key example
at &lt;a href=&quot;http://simpl.info/eme/clearkey&quot; rel=&quot;noopener&quot;&gt;simpl.info/ck&lt;/a&gt;. Below is a walkthrough of
the code, which parallels the steps described
&lt;a href=&quot;https://web.dev/media-eme/#getting-a-key-from-a-license-server&quot;&gt;above&lt;/a&gt;, though without license server
interaction.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Define a key: hardcoded in this example&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// – this corresponds to the key used for encryption&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEY&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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token number&quot;&gt;0xeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xdd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x62&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xf1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x68&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xd2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x7b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x68&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x2a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xfc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token number&quot;&gt;0xe4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xae&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x3c&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;var&lt;/span&gt; config &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;span class=&quot;token literal-property property&quot;&gt;initDataTypes&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;webm&#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;videoCapabilities&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video/webm; codecs=&quot;vp8&quot;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;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 punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; video &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;video&#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;video&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;encrypted&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleEncrypted&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;navigator&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;org.w3.clearkey&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&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; keySystemAccess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaKeys&lt;/span&gt;&lt;span class=&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 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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&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; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;createdMediaKeys&lt;span class=&quot;token punctuation&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;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to set up MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleEncrypted&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;var&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createSession&lt;/span&gt;&lt;span class=&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;  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  session&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRequest&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;initDataType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initData&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;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to generate a license request&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMessage&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 comment&quot;&gt;// If you had a license server, you would make an asynchronous XMLHttpRequest&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// with event.message as the body.  The response from the server, as a&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Uint8Array, would then be passed to session.update().&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Instead, we will generate the license synchronously on the client, using&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// the hard-coded KEY at the top.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; license &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateLicense&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;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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; session &lt;span class=&quot;token operator&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;br /&gt;  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;license&lt;span class=&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to update the session&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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 comment&quot;&gt;// Convert Uint8Array into base64 using base64url alphabet, without padding.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toBase64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;u8arr&lt;/span&gt;&lt;span class=&quot;token punctuation&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;btoa&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromCharCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&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; u8arr&lt;span class=&quot;token punctuation&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;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token special-escape escape&quot;&gt;\+&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&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;&#39;-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token escape&quot;&gt;\/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&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;&#39;_&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;=&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token anchor function&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&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;&#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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// This takes the place of a license server.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// kids is an array of base64-encoded key IDs&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// keys is an array of base64-encoded keys&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateLicense&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// Parse the clearkey license request.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; request &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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextDecoder&lt;/span&gt;&lt;span class=&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;decode&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// We only know one key, so there should only be one key ID.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// A real license server could easily serve multiple keys.&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;assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kids&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keyObj &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;kty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;oct&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;alg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;A128KW&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;kid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kids&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 literal-property property&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toBase64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&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;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;TextEncoder&lt;/span&gt;&lt;span class=&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;encode&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;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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;keys&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;keyObj&lt;span class=&quot;token punctuation&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;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;To test this code, you need an encrypted video to play. Encrypting a video for
use with Clear Key can be done for WebM as per the webm_crypt instructions.
Commercial services are also available (for ISO BMFF/MP4 at least) and other
solutions are being developed.&lt;/p&gt;
&lt;h2 id=&quot;related-technology-1-media-source-extensions-mse&quot;&gt;Related technology 1: Media Source Extensions (MSE) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#related-technology-1-media-source-extensions-mse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLMediaElement&quot; rel=&quot;noopener&quot;&gt;HTMLMediaElement&lt;/a&gt;
is a creature of simple beauty.&lt;/p&gt;
&lt;p&gt;We can load, decode and play media simply by providing a src URL:&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;video&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;foo.webm&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;video&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 Media Source API is an extension to HTMLMediaElement enabling more fine-
grained control over the source of media, by allowing JavaScript to build
streams for playback from &#39;chunks&#39; of video. This in turn enables techniques
such as adaptive streaming and time shifting.&lt;/p&gt;
&lt;p&gt;Why is MSE important to EME? Because in addition to distributing protected
content, commercial content providers must be able to adapt content delivery to
network conditions and other requirements. Netflix, for example, dynamically
changes stream bitrate as network conditions change. EME works with playback of
media streams provided by an MSE implementation, just as it would with media
provided via a src attribute.&lt;/p&gt;
&lt;p&gt;How to chunk and play back media encoded at different bitrates? See the
&lt;a href=&quot;https://web.dev/media-eme/#related-technology-2-dynamic-adaptive-streaming-over-http-dash&quot;&gt;DASH section below&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see MSE in action at &lt;a href=&quot;http://simpl.info/mse&quot; rel=&quot;noopener&quot;&gt;simpl.info/mse&lt;/a&gt;;
for the purposes of this example, a WebM video is split into five chunks using
the File APIs. In a production application, chunks of video would be retrieved via AJAX.&lt;/p&gt;
&lt;p&gt;First a SourceBuffer is created:&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; sourceBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSourceBuffer&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;&#39;video/webm; codecs=&quot;vorbis,vp8&quot;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The entire movie is then &#39;streamed&#39; to a video element by appending each chunk
using the appendBuffer() method:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  sourceBuffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendBuffer&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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;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;i &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NUM_CHUNKS&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;    mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endOfStream&lt;/span&gt;&lt;span class=&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paused&lt;span class=&quot;token punctuation&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;// start playing after first chunk is appended&lt;/span&gt;&lt;br /&gt;      video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&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 function&quot;&gt;readChunk_&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;i&lt;span class=&quot;token punctuation&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Find out more about MSE in the
&lt;a href=&quot;https://web.dev/web/fundamentals/media/mse/basics&quot;&gt;MSE primer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;related-technology-2-dynamic-adaptive-streaming-over-http-dash&quot;&gt;Related technology 2: Dynamic Adaptive Streaming over HTTP (DASH) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#related-technology-2-dynamic-adaptive-streaming-over-http-dash&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Multi-device, multi-platform, mobile — whatever you call it, the web is often
experienced under conditions of changeable connectivity. Dynamic, adaptive
delivery is crucial for coping with bandwidth constraints and variability in the
multi-device world.&lt;/p&gt;
&lt;p&gt;DASH (aka MPEG-DASH) is designed to enable the best possible media delivery in a
flaky world, for both streaming and download. Several other technologies do
something similar — such as Apple&#39;s
&lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Live_Streaming&quot; rel=&quot;noopener&quot;&gt;HTTP Live Streaming&lt;/a&gt;
(HLS) and Microsoft&#39;s
&lt;a href=&quot;https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming#Microsoft_Smooth_Streaming&quot; rel=&quot;noopener&quot;&gt;Smooth Streaming&lt;/a&gt;
— but DASH is the only method of adaptive bitrate streaming via HTTP that is
based on an open standard. DASH is already in use by sites such as YouTube.&lt;/p&gt;
&lt;p&gt;What does this have to do with EME and MSE? MSE-based DASH implementations can
parse a manifest, download segments of video at an appropriate bitrate, and feed
them to a video element when it gets hungry — using existing HTTP
infrastructure.&lt;/p&gt;
&lt;p&gt;In other words, DASH enables commercial content providers to do adaptive
streaming of protected content.&lt;/p&gt;
&lt;p&gt;DASH does what it says on the tin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dynamic:&lt;/strong&gt; responds to changing conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptive:&lt;/strong&gt; adapts to provide an appropriate audio or video bitrate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming:&lt;/strong&gt; allows for streaming as well as download.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP:&lt;/strong&gt; enables content delivery with the advantage of HTTP, without the
disadvantages of a traditional streaming server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The BBC has begun &lt;a href=&quot;http://bbc.co.uk/rd/blog/2013/09/mpeg-dash-test-streams&quot; rel=&quot;noopener&quot;&gt;providing test streams using DASH&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;The media is encoded a number of times at different bitrates. Each encoding is
called a Representation. These are split into a number of Media Segments. The
client plays a programme by requesting segments, in order, from a
representation over HTTP. Representations can be grouped into Adaptation Sets
of representations containing equivalent content. If the client wishes to
change bitrate it can pick an alternative from the current adaption set and
start requesting segments from that representation. Content is encoded in such
a way to make this switching easy for the client to do. In addition to a
number of media segments, a representation generally also has an
Initialization Segment. This can be thought of as a header, containing
information about the encoding, frame sizes, etc. A client needs to obtain
this for a given representation before consuming media segments from that
representation.&lt;/p&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Media is encoded at different bitrates.&lt;/li&gt;
&lt;li&gt;The different bitrate files are made available from an HTTP server.&lt;/li&gt;
&lt;li&gt;A client web app chooses which bitrate to retrieve and play back with DASH.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As part of the video segmentation process, an XML manifest known as a Media
Presentation Description (MPD) is built programmatically. This describes
Adaptation Sets and Representations, with durations and URLs. An MPD looks like
this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;MPD&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&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;urn:mpeg:DASH:schema:MPD:2011&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mediaPresentationDuration&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;PT0H3M1.63S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;minBufferTime&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;PT1.5S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;profiles&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;urn:mpeg:dash:profile:isoff-on-demand:2011&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;static&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;Period&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;duration&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;PT0H3M1.63S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;start&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;PT0S&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;AdaptationSet&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;ContentComponent&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;contentType&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;video&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;1&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;Representation&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;bandwidth&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;4190760&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;codecs&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;avc1.640028&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&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;1080&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mimeType&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;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&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;1920&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;BaseURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;car-20120827-89.mp4&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;BaseURL&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;SegmentBase&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;indexRange&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;674-1149&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;Initialization&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;range&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;0-673&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;SegmentBase&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;Representation&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;Representation&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;bandwidth&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;2073921&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;codecs&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;avc1.4d401f&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&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;720&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mimeType&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;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&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;1280&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;BaseURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;car-20120827-88.mp4&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;BaseURL&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;SegmentBase&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;indexRange&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;708-1183&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;Initialization&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;range&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;0-707&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;SegmentBase&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;Representation&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;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;AdaptationSet&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;Period&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;MPD&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;(This XML is taken from
&lt;a href=&quot;http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd&quot; rel=&quot;noopener&quot;&gt;the .mpd file&lt;/a&gt;
used for the
&lt;a href=&quot;http://dash-mse-test.appspot.com/dash-player.html?url=http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd&quot; rel=&quot;noopener&quot;&gt;YouTube DASH demo player&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;According to the DASH spec, an MPD file could in theory be used as the src for a
video. However, to give more flexibility to web developers, browser vendors have
chosen instead to leave DASH support up to JavaScript libraries using MSE such
as &lt;a href=&quot;http://www-itec.uni-klu.ac.at/dash/?p=792&quot; rel=&quot;noopener&quot;&gt;dash.js&lt;/a&gt;. Implementing DASH in
JavaScript allows the adaptation algorithm to evolve without requiring browser
updates. Using MSE also allows experimentation with alternative manifest formats
and delivery mechanisms without requiring browser changes. Google&#39;s
&lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt; implements a DASH client with
EME support.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;Mozilla Developer Network has instructions&lt;/a&gt;
on how to use WebM tools and FFmpeg to segment video and build an MPD.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use of the web to deliver paid-for video and audio is growing at a
&lt;a href=&quot;http://www.cmo.com/content/dam/CMO_Other/ADI/Video_Benchmark_Q2_2014/video_benchmark_report-2014.pdf&quot; rel=&quot;noopener&quot;&gt;huge rate&lt;/a&gt;.
It seems that every new device, whether it&#39;s a tablet,
game console, connected TV, or set-top box, is able to stream media from the
major content providers over HTTP. &lt;a href=&quot;http://longtailvideo.com/html5&quot; rel=&quot;noopener&quot;&gt;Over 85%&lt;/a&gt; of
mobile and desktop browsers now support &amp;lt;video&amp;gt; and &amp;lt;audio&amp;gt;, and
Cisco estimates that
&lt;a href=&quot;http://www.cisco.com/en/US/solutions/collateral/ns341/ns525/ns537/ns705/ns827/white_paper_c11-481360_ns827_Networking_Solutions_White_Paper.html&quot; rel=&quot;noopener&quot;&gt;video will be 80 to 90 percent of global consumer internet traffic&lt;/a&gt;
by 2017. In this context, browser support for protected content distribution is
likely to be become increasingly significant, as
&lt;a href=&quot;https://blog.mozilla.org/security/2013/01/29/putting-users-in-control-of-plugins/&quot; rel=&quot;noopener&quot;&gt;browser vendors&lt;/a&gt;
&lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/developers/npapi-deprecation&quot; rel=&quot;noopener&quot;&gt;curtail support&lt;/a&gt;
for APIs that most media plugins rely on.&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/media-eme/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;specs-and-standards&quot;&gt;Specs and standards &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#specs-and-standards&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://w3c.github.io/encrypted-media/&quot; rel=&quot;noopener&quot;&gt;EME spec&lt;/a&gt;: latest Editor&#39;s Draft
&lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;Common Encryption (CENC)&lt;/a&gt;
&lt;a href=&quot;https://w3c.github.io/media-source/&quot; rel=&quot;noopener&quot;&gt;Media Source Extensions&lt;/a&gt;: latest Editor&#39;s Draft
&lt;a href=&quot;http://standards.iso.org/ittf/PubliclyAvailableStandards/c057623_ISO_IEC_23009-1_2012.zip&quot; rel=&quot;noopener&quot;&gt;DASH standard&lt;/a&gt;
(yes, it&#39;s a PDF)
&lt;a href=&quot;http://dashif.org/about/&quot; rel=&quot;noopener&quot;&gt;Overview of the DASH standard&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;articles&quot;&gt;Articles &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#articles&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://vimeo.com/62269279&quot; rel=&quot;noopener&quot;&gt;DTG Webinar&lt;/a&gt; (partially obsolete)
&lt;a href=&quot;https://hsivonen.fi/eme/&quot; rel=&quot;noopener&quot;&gt;What is EME?&lt;/a&gt;, by Henri Sivonen
&lt;a href=&quot;https://web.dev/web/fundamentals/media/mse/basics&quot;&gt;Media Source Extensions&lt;/a&gt; primer
MPEG-DASH Test Streams: &lt;a href=&quot;http://bbc.co.uk/rd/blog/2013/09/mpeg-dash-test-streams&quot; rel=&quot;noopener&quot;&gt;BBC R&amp;amp;D blog post&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;demos&quot;&gt;Demos &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#demos&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Clear Key demo: &lt;a href=&quot;http://simpl.info/eme/clearkey&quot; rel=&quot;noopener&quot;&gt;simpl.info/ck&lt;/a&gt;
&lt;a href=&quot;http://simpl.info/mse&quot; rel=&quot;noopener&quot;&gt;Media Source Extensions (MSE) demo&lt;/a&gt;
Google&#39;s &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt;
implements a DASH client with EME support&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-eme/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>EME WTF?</title>
    <link href="https://web.dev/eme-basics/"/>
    <updated>2014-01-16T00:00:00Z</updated>
    <id>https://web.dev/eme-basics/</id>
    <content type="html" mode="escaped">&lt;p&gt;Encrypted Media Extensions provides an API that enables web applications to interact with content protection systems, to allow playback of encrypted audio and video.&lt;/p&gt;
&lt;p&gt;EME is designed to enable the same app and encrypted files to be used in any browser, regardless of the underlying protection system. The former is made possible by the standardized APIs and flow while the latter is made possible by the concept of Common Encryption.&lt;/p&gt;
&lt;p&gt;EME is an extension to the &lt;code&gt;HTMLMediaElement&lt;/code&gt; specification - hence the name. Being an &#39;extension&#39; means that browser support for EME is optional: if a browser does not support encrypted media, it will not be able to play encrypted media, but EME is not required for HTML spec compliance. From &lt;a href=&quot;https://w3c.github.io/encrypted-media/&quot; rel=&quot;noopener&quot;&gt;the EME spec&lt;/a&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;  This proposal extends &lt;a href=&quot;http://www.w3.org/TR/html5/embedded-content-0.html#htmlmediaelement&quot;&gt;HTMLMediaElement&lt;/a&gt; providing APIs to control playback of protected content.  The API supports use cases ranging from simple clear key decryption to high value video (given an appropriate user agent implementation). License/key exchange is controlled by the application, facilitating the development of robust playback applications supporting a range of content decryption and protection technologies.  This specification does not define a content protection or Digital Rights Management system. Rather, it defines a common API that may be used to discover, select and interact with such systems as well as with simpler content encryption systems. Implementation of Digital Rights Management is not required for compliance with this specification: only the Clear Key system is required to be implemented as a common baseline.  The common API supports a simple set of content encryption capabilities, leaving application functions such as authentication and authorization to page authors. This is achieved by requiring content protection system-specific messaging to be mediated by the page rather than assuming out-of-band communication between the encryption system and a license or other server.  &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;EME implementations use the following external components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key System:&lt;/strong&gt; A content protection (DRM) mechanism. EME doesn&#39;t define Key Systems themselves, apart from Clear Key (more about that below).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content Decryption Module (CDM):&lt;/strong&gt; A client-side software or hardware mechanism that enables playback of encrypted media. As with Key Systems, EME doesn&#39;t define any CDMs, but provides an interface for applications to interact with CDMs that are available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;License (Key) server:&lt;/strong&gt; Interacts with a CDM to provide keys to decrypt media. Negotiation with the license server is the responsibility of the application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Packaging service:&lt;/strong&gt; Encodes and encrypts media for distribution/consumption.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that an application using EME interacts with a license server to get keys to enable decryption, but user identity and authentication are not part of EME. Retrieval of keys to enable media playback happens after (optionally) authenticating a user. Services such as Netflix must authenticate users within their web application: when a user signs into the application, the application determines the user&#39;s identity and privileges.&lt;/p&gt;
&lt;h2 id=&quot;how-does-eme-work&quot;&gt;How does EME work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#how-does-eme-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s how the components of EME interact, corresponding to the code example below:&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 multiple formats or codecs are available, &lt;a href=&quot;http://www.w3.org/TR/media-source/#widl-MediaSource-isTypeSupported-boolean-DOMString-type&quot;&gt;&lt;code&gt;MediaSource.isTypeSupported()&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;http://dev.w3.org/html5/spec-preview/media-elements.html#dom-navigator-canplaytype&quot;&gt;&lt;code&gt;HTMLMediaElement.canPlayType()&lt;/code&gt;&lt;/a&gt; can both be used to select the right one. However, the CDM may only support a subset of what the browser supports for unencrypted content. It&#39;s best to negotiate a &lt;a href=&quot;https://w3c.github.io/encrypted-media/#mediakeys-interface&quot;&gt;&lt;code&gt;MediaKeys&lt;/code&gt;&lt;/a&gt; configuration before selecting a format and codec. If the application waits for the &lt;code&gt;encrypted&lt;/code&gt; event but then &lt;code&gt;MediaKeys&lt;/code&gt; shows it can&#39;t handle the chosen format/codec, it may be too late to switch without interrupting playback.  The recommended flow is to negotiate &lt;code&gt;MediaKeys&lt;/code&gt; first, using &lt;a href=&quot;https://w3c.github.io/encrypted-media/#widl-MediaKeySystemAccess-getConfiguration-MediaKeySystemConfiguration&quot;&gt;&lt;code&gt;MediaKeysSystemAccess.getConfiguration()&lt;/code&gt;&lt;/a&gt; to find out the negotiated configuration. If there is only one format/codec to choose from, then there&#39;s no need for &lt;code&gt;getConfiguration()&lt;/code&gt;. However, it&#39;s still preferable to set up &lt;code&gt;MediaKeys&lt;/code&gt; first. The only reason to wait for the &lt;code&gt;encrypted&lt;/code&gt; event is if there is no way of knowing whether the content is encrypted or not, but in practice that&#39;s unlikely. &lt;/div&gt;&lt;/aside&gt;
&lt;ol&gt;
&lt;li&gt;A web application attempts to play audio or video that has one or more encrypted streams.&lt;/li&gt;
&lt;li&gt;The browser recognizes that the media is encrypted (see box below for how that happens) and fires an &lt;code&gt;encrypted&lt;/code&gt; event with metadata (&lt;code&gt;initData&lt;/code&gt;) obtained from the media about the encryption.&lt;/li&gt;
&lt;li&gt;The application handles the &lt;code&gt;encrypted&lt;/code&gt; event:
&lt;ol&gt;
&lt;li&gt;If no &lt;code&gt;MediaKeys&lt;/code&gt; object has been associated with the media element, first select an available Key System by using &lt;a href=&quot;https://w3c.github.io/encrypted-media/#navigator-extension-requestmediakeysystemaccess&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.requestMediaKeySystemAccess()&lt;/code&gt;&lt;/a&gt; to check what Key Systems are available, then create a &lt;code&gt;MediaKeys&lt;/code&gt; object for an available Key System via a &lt;code&gt;MediaKeySystemAccess&lt;/code&gt; object. Note that initialization of the MediaKeys object should happen before the first &lt;code&gt;encrypted&lt;/code&gt; event. Getting a license server URL is done by the app independently of selecting an available key system. A &lt;code&gt;MediaKeys&lt;/code&gt; object represents all the keys available to decrypt the media for an audio or video element. It represents a CDM instance and provides access to the CDM, specifically for creating key sessions, which are used to obtain keys from a license server.&lt;/li&gt;
&lt;li&gt;Once the &lt;code&gt;MediaKeys&lt;/code&gt; object has been created, assign it to the media element: &lt;a href=&quot;https://w3c.github.io/encrypted-media/#widl-HTMLMediaElement-setMediaKeys-Promise-void--MediaKeys-mediaKeys&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;setMediaKeys()&lt;/code&gt;&lt;/a&gt; associates the &lt;code&gt;MediaKeys&lt;/code&gt; object with an HTMLMediaElement, so that its keys can be used during playback, i.e. during decoding.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;The app creates a &lt;code&gt;MediaKeySession&lt;/code&gt; by calling &lt;code&gt;createSession()&lt;/code&gt; on the &lt;code&gt;MediaKeys&lt;/code&gt;. This creates a &lt;code&gt;MediaKeySession&lt;/code&gt;, which represents the lifetime of a license and its key(s).&lt;/li&gt;
&lt;li&gt;The app generates a license request by passing the media data obtained in the &lt;code&gt;encrypted&lt;/code&gt; handler to the CDM, by calling &lt;code&gt;generateRequest()&lt;/code&gt; on the &lt;code&gt;MediaKeySession&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The CDM fires a &lt;code&gt;message&lt;/code&gt; event: a request to acquire a key from a license server.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;MediaKeySession&lt;/code&gt; object receives the &lt;code&gt;message&lt;/code&gt; event and the application sends a message to the license server (via XHR, for example).&lt;/li&gt;
&lt;li&gt;The application receives a response from the license server and passes the data to the CDM using the &lt;code&gt;update()&lt;/code&gt; method of the &lt;code&gt;MediaKeySession&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The CDM decrypts the media using the keys in the license. A valid key may be used, from any session within the &lt;code&gt;MediaKey&lt;/code&gt;s associated with the media element. The CDM will access the key and policy, indexed by Key ID.&lt;/li&gt;
&lt;li&gt;Media playback resumes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Phew…&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; How does the browser know that media is encrypted?  This information is in the metadata of the media container file, which will be in a format such as &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_base_media_file_format&quot;&gt;ISO BMFF&lt;/a&gt; or  &lt;a href=&quot;https://en.wikipedia.org/wiki/WebM&quot;&gt;WebM&lt;/a&gt;. For ISO BMFF this means header metadata, called the &lt;strong&gt;protection scheme information box&lt;/strong&gt;. WebM uses the Matroska &lt;a href=&quot;http://matroska.org/technical/specs/notes.html&quot;&gt;ContentEncryption element&lt;/a&gt;, with some &lt;a href=&quot;http://www.webmproject.org/docs/webm-encryption/&quot;&gt;WebM-specific additions&lt;/a&gt;. Guidelines are provided for each container &lt;a href=&quot;http://w3c.github.io/encrypted-media/initdata-format-registry.html&quot;&gt;in an EME-specific registry&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Note that there may be multiple messages between the CDM and the license server, and all communication in this process is opaque to the browser and application: messages are only understood by the CDM and license server, although the app layer can see &lt;a href=&quot;https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType&quot; rel=&quot;noopener&quot;&gt;what type of message&lt;/a&gt; the CDM is sending. The license request contains proof of the CDM&#39;s validity (and trust relationship) as well as a key to use when encrypting the content key(s) in the resulting license.&lt;/p&gt;
&lt;h2 id=&quot;but-what-do-cdms-actually-do&quot;&gt;…but what do CDMs actually do? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#but-what-do-cdms-actually-do&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An EME implementation does not in itself provide a way to decrypt media: it simply provides an API for a web application to interact with Content Decryption Modules.&lt;/p&gt;
&lt;p&gt;What CDMs actually do is not defined by the EME spec, and a CDM may handle decoding (decompression) of media as well as decryption. From least to most robust, there are several potential options for CDM functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Decryption only, enabling playback using the normal media pipeline, for example via a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Decryption and decoding, passing video frames to the browser for rendering.&lt;/li&gt;
&lt;li&gt;Decryption and decoding, rendering directly in the hardware (for example, the GPU).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are multiple ways to make a CDM available to a web app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bundle a CDM with the browser.&lt;/li&gt;
&lt;li&gt;Distribute a CDM separately.&lt;/li&gt;
&lt;li&gt;Build a CDM into the operating system.&lt;/li&gt;
&lt;li&gt;Include a CDM in firmware.&lt;/li&gt;
&lt;li&gt;Embed a CDM in hardware.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How a CDM is made available is not defined by the EME spec, but in all cases the browser is responsible for vetting and exposing the CDM.&lt;/p&gt;
&lt;p&gt;EME doesn&#39;t mandate a particular Key System; among current desktop and mobile browsers, Chrome supports Widevine and IE11 supports PlayReady.&lt;/p&gt;
&lt;h2 id=&quot;getting-a-key-from-a-license-server&quot;&gt;Getting a key from a license server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#getting-a-key-from-a-license-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In typical commercial use, content will be encrypted and encoded using a packaging service or tool. Once the encrypted media is made available online, a web client can obtain a key (contained within a license) from a license server and use the key to enable decryption and playback of the content.&lt;/p&gt;
&lt;p&gt;The following code (adapted from the &lt;a href=&quot;http://w3c.github.io/encrypted-media/#examples&quot; rel=&quot;noopener&quot;&gt;spec examples&lt;/a&gt;) shows how an application can select an appropriate key system and obtain a key from a license server.&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; video &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;video&#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;var&lt;/span&gt; config &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 literal-property property&quot;&gt;initDataTypes&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;webm&#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;videoCapabilities&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 literal-property property&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video/webm; codecs=&quot;vp9&quot;&#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;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;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaKeys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;org.w3.clearkey&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      config&lt;span class=&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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keySystemAccess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaKeys&lt;/span&gt;&lt;span class=&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;      promise&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;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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unable to create MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      promise&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&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; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;createdMediaKeys&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unable to set MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      promise&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; initData &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;Uint8Array&lt;/span&gt;&lt;span class=&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 punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keySession &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; createdMediaKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createSession&lt;/span&gt;&lt;span class=&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;          keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&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 keyword&quot;&gt;return&lt;/span&gt; keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;webm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initData&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token string&quot;&gt;&#39;Unable to create or initialize key session&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMessage&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;var&lt;/span&gt; keySession &lt;span class=&quot;token operator&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; license &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;Uint8Array&lt;/span&gt;&lt;span class=&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 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;  keySession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;license&lt;span class=&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;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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;update() failed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;common-encryption&quot;&gt;Common Encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#common-encryption&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Common Encryption solutions allow content providers to encrypt and package their content once per container/codec and use it with a variety of Key Systems, CDMs and clients: that is, any CDM that supports Common Encryption. For example, a video packaged using Playready could be played back in a browser using a Widevine CDM obtaining a key from a Widevine license server.&lt;/p&gt;
&lt;p&gt;This is in contrast to legacy solutions that would only work with a complete vertical stack, including a single client that often also included an application runtime.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;Common Encryption&lt;/a&gt; (CENC) is an &lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;ISO standard&lt;/a&gt; defining a protection scheme for ISO BMFF; a similar concept applies to WebM.&lt;/p&gt;
&lt;h2 id=&quot;clear-key&quot;&gt;Clear Key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#clear-key&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although EME does not define DRM functionality, the spec currently mandates that all browsers supporting EME must implement Clear Key. Using this system, media can be encrypted with a key and then played back simply by providing that key. Clear Key can be built into the browser: it does not require the use of a separate decryption module.&lt;/p&gt;
&lt;p&gt;While not likely to be used for many types of commercial content, Clear Key is fully interoperable across all browsers that support EME. It is also handy for testing EME implementations, and applications using EME, without the need to request a content key from a license server. There is a simple Clear Key example at &lt;a href=&quot;http://simpl.info/eme/clearkey&quot; rel=&quot;noopener&quot;&gt;simpl.info/ck&lt;/a&gt;. Below is a walkthrough of the code, which parallels the steps described above, though without license server interaction.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Define a key: hardcoded in this example&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// – this corresponds to the key used for encryption&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEY&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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token number&quot;&gt;0xeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xdd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x62&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xf1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x68&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xd2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x7b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token number&quot;&gt;0x68&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x2a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xfc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xe4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xae&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x3c&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;var&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;initDataTypes&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;webm&#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;videoCapabilities&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;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video/webm; codecs=&quot;vp8&quot;&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;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;var&lt;/span&gt; video &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;video&#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;video&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;encrypted&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleEncrypted&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;org.w3.clearkey&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&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; keySystemAccess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaKeys&lt;/span&gt;&lt;span class=&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 function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;createdMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&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; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;createdMediaKeys&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to set up MediaKeys&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleEncrypted&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;var&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createSession&lt;/span&gt;&lt;span class=&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;  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateRequest&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;initDataType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initData&lt;span class=&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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to generate a license request&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMessage&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 comment&quot;&gt;// If you had a license server, you would make an asynchronous XMLHttpRequest&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// with event.message as the body.  The response from the server, as a&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Uint8Array, would then be passed to session.update().&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Instead, we will generate the license synchronously on the client, using&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// the hard-coded KEY at the top.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; license &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateLicense&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;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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; session &lt;span class=&quot;token operator&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;br /&gt;  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;license&lt;span class=&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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Failed to update the session&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Convert Uint8Array into base64 using base64url alphabet, without padding.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toBase64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;u8arr&lt;/span&gt;&lt;span class=&quot;token punctuation&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;btoa&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromCharCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&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; u8arr&lt;span class=&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;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token special-escape escape&quot;&gt;\+&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&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;&#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 function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;&lt;span class=&quot;token escape&quot;&gt;\/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&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;&#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 function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;=&lt;span class=&quot;token quantifier number&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token anchor function&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&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;&#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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// This takes the place of a license server.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// kids is an array of base64-encoded key IDs&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// keys is an array of base64-encoded keys&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateLicense&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// Parse the clearkey license request.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; request &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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextDecoder&lt;/span&gt;&lt;span class=&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;decode&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// We only know one key, so there should only be one key ID.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// A real license server could easily serve multiple keys.&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;assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kids&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keyObj &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;kty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;oct&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;alg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;A128KW&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;kid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kids&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 literal-property property&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toBase64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KEY&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;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;TextEncoder&lt;/span&gt;&lt;span class=&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;encode&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;keys&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;keyObj&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To test this code, you need an encrypted video to play. Encrypting a video for use with Clear Key can be done for WebM as per the &lt;a href=&quot;https://docs.google.com/document/d/17d6_KX5jX0gY1ygYbjqOEdVzuUGkPO53wL8t40dMGeQ/edit?usp=sharing&quot; rel=&quot;noopener&quot;&gt;webm_crypt instructions&lt;/a&gt;. Commercial services are also available (for ISO BMFF/MP4 at least) and other solutions are being developed.&lt;/p&gt;
&lt;h2 id=&quot;related-technology-#1&quot;&gt;Related technology #1 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#related-technology-#1&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;media-source-extensions-mse&quot;&gt;Media Source Extensions (MSE) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#media-source-extensions-mse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/HTMLMediaElement&quot; rel=&quot;noopener&quot;&gt;HTMLMediaElement&lt;/a&gt; is a creature of simple beauty.&lt;/p&gt;
&lt;p&gt;We can load, decode and play media simply by providing a src URL:&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;video&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;&#39;&lt;/span&gt;foo.webm&lt;span class=&quot;token punctuation&quot;&gt;&#39;&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;video&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 Media Source API is an extension to HTMLMediaElement enabling more fine-grained control over the source of media, by allowing JavaScript to build streams for playback from &#39;chunks&#39; of video. This in turn enables techniques such as adaptive streaming and time shifting.&lt;/p&gt;
&lt;p&gt;Why is MSE important to EME? Because in addition to distributing protected content, commercial content providers must be able to adapt content delivery to network conditions and other requirements. Netflix, for example, dynamically changes stream bitrate as network conditions change. EME works with playback of media streams provided by an MSE implementation, just as it would with media provided via a &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;How to chunk and play back media encoded at different bitrates? See the DASH section below.&lt;/p&gt;
&lt;p&gt;You can see MSE in action at &lt;a href=&quot;http://simpl.info/mse&quot; rel=&quot;noopener&quot;&gt;simpl.info/mse&lt;/a&gt;; for the purposes of this example, a WebM video is split into five chunks using the File APIs. In a production application, chunks of video would be retrieved via Ajax.&lt;/p&gt;
&lt;p&gt;First a SourceBuffer is created:&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; sourceBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSourceBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;video/webm; codecs=&quot;vorbis,vp8&quot;&#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;The entire movie is then &#39;streamed&#39; to a video element by appending each chunk using the appendBuffer() method:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  sourceBuffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendBuffer&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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;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;i &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NUM_CHUNKS&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;    mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endOfStream&lt;/span&gt;&lt;span class=&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paused&lt;span class=&quot;token punctuation&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;// start playing after first chunk is appended&lt;/span&gt;&lt;br /&gt;      video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&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 function&quot;&gt;readChunk_&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;i&lt;span class=&quot;token punctuation&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Find out more about MSE in the &lt;a href=&quot;http://updates.html5rocks.com/2011/11/Stream-video-using-the-MediaSource-API&quot; rel=&quot;noopener&quot;&gt;HTML5 Rocks article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;related-technology-#2&quot;&gt;Related technology #2 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#related-technology-#2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;dynamic-adaptive-streaming-over-http-dash&quot;&gt;Dynamic Adaptive Streaming over HTTP (DASH) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#dynamic-adaptive-streaming-over-http-dash&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Multi-device, multi-platform, mobile - whatever you call it, the web is often experienced under conditions of changeable connectivity. Dynamic, adaptive delivery is crucial for coping with bandwidth constraints and variability in the multi-device world.&lt;/p&gt;
&lt;p&gt;DASH (aka MPEG-DASH) is designed to enable the best possible media delivery in a flaky world, for both streaming and download. Several other technologies do something similar - such as Apple&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Live_Streaming&quot; rel=&quot;noopener&quot;&gt;HTTP Live Streaming&lt;/a&gt; (HLS) and Microsoft&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming#Microsoft_Smooth_Streaming&quot; rel=&quot;noopener&quot;&gt;Smooth Streaming&lt;/a&gt; - but DASH is the only method of adaptive bitrate streaming via HTTP that is based on an open standard. DASH is already in use by sites such as YouTube.&lt;/p&gt;
&lt;p&gt;What does this have to do with EME and MSE? MSE-based DASH implementations can parse a manifest, download segments of video at an appropriate bitrate, and feed them to a video element when it gets hungry - using existing HTTP infrastructure.&lt;/p&gt;
&lt;p&gt;In other words, DASH enables commercial content providers to do adaptive streaming of protected content.&lt;/p&gt;
&lt;p&gt;DASH does what it says on the tin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dynamic:&lt;/strong&gt; responds to changing conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptive:&lt;/strong&gt; adapts to provide an appropriate audio or video bitrate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming:&lt;/strong&gt; allows for streaming as well as download.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP:&lt;/strong&gt; enables content delivery with the advantage of HTTP, without the disadvantages of a traditional streaming server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The BBC has begun &lt;a href=&quot;http://bbc.co.uk/rd/blog/2013/09/mpeg-dash-test-streams&quot; rel=&quot;noopener&quot;&gt;providing test streams using DASH&lt;/a&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; The media is encoded a number of times at different bitrates. Each encoding is called a Representation. These are split into a number of Media Segments. The client plays a programme by requesting segments, in order, from a representation over HTTP. Representations can be grouped into Adaptation Sets of representations containing equivalent content. If the client wishes to change bitrate it can pick an alternative from the current adaption set and start requesting segments from that representation. Content is encoded in such a way to make this switching easy for the client to do. In addition to a number of media segments, a representation generally also has an Initialisation Segment. This can be thought of as a header, containing information about the encoding, frame sizes, etc. A client needs to obtain this for a given representation before consuming media segments from that representation. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Media is encoded at different bitrates.&lt;/li&gt;
&lt;li&gt;The different bitrate files are made available from an HTTP server.&lt;/li&gt;
&lt;li&gt;A client web app chooses which bitrate to retrieve and play back with DASH.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As part of the video segmentation process, an XML manifest known as a Media Presentation Description (MPD) is built programmatically. This describes Adaptation Sets and Representations, with durations and URLs. An MPD looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;MPD&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&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;urn:mpeg:DASH:schema:MPD:2011&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mediaPresentationDuration&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;PT0H3M1.63S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;minBufferTime&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;PT1.5S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;profiles&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;urn:mpeg:dash:profile:isoff-on-demand:2011&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;static&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;Period&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;duration&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;PT0H3M1.63S&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;start&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;PT0S&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;AdaptationSet&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;ContentComponent&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;contentType&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;video&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;1&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;Representation&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;bandwidth&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;4190760&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;codecs&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;avc1.640028&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&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;1080&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mimeType&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;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&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;1920&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;BaseURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;car-20120827-89.mp4&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;BaseURL&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;SegmentBase&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;indexRange&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;674-1149&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;Initialization&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;range&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;0-673&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;SegmentBase&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;Representation&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;Representation&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;bandwidth&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;2073921&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;codecs&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;avc1.4d401f&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&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;720&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mimeType&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;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&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;1280&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;BaseURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;car-20120827-88.mp4&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;BaseURL&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;SegmentBase&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;indexRange&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;708-1183&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;Initialization&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;range&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;0-707&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;SegmentBase&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;Representation&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;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;AdaptationSet&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;Period&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;MPD&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;(This XML is taken from &lt;a href=&quot;http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd&quot; rel=&quot;noopener&quot;&gt;the .mpd file&lt;/a&gt; used for the &lt;a href=&quot;http://dash-mse-test.appspot.com/dash-player.html?url=http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd&quot; rel=&quot;noopener&quot;&gt;YouTube DASH demo player&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;According to the DASH spec, an MPD file could in theory be used as the &lt;code&gt;src&lt;/code&gt; for a video. However, to give more flexibility to web developers, browser vendors have chosen instead to leave DASH support up to JavaScript libraries using MSE such as &lt;a href=&quot;http://www-itec.uni-klu.ac.at/dash/?p=792&quot; rel=&quot;noopener&quot;&gt;dash.js&lt;/a&gt;. Implementing DASH in JavaScript allows the adaptation algorithm to evolve without requiring browser updates. Using MSE also allows experimentation with alternative manifest formats and delivery mechanisms without requiring browser changes. Google&#39;s &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt; implements a DASH client with EME support.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;Mozilla Developer Network has instructions&lt;/a&gt; on how to use WebM tools and FFmpeg to segment video and build an MPD.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use of the web to deliver paid-for video and audio is growing at a &lt;a href=&quot;http://www.cmo.com/content/dam/CMO_Other/ADI/Video_Benchmark_Q2_2014/video_benchmark_report-2014.pdf&quot; rel=&quot;noopener&quot;&gt;huge rate&lt;/a&gt;. It seems that every new device, whether it&#39;s a tablet, game console, connected TV, or set-top box, is able to stream media from the major content providers over HTTP. &lt;a href=&quot;http://longtailvideo.com/html5&quot; rel=&quot;noopener&quot;&gt;Over 85%&lt;/a&gt; of mobile and desktop browsers now support &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt;, and Cisco estimates that &lt;a href=&quot;http://www.cisco.com/en/US/solutions/collateral/ns341/ns525/ns537/ns705/ns827/white_paper_c11-481360_ns827_Networking_Solutions_White_Paper.html&quot; rel=&quot;noopener&quot;&gt;video will be 80 to 90 percent of global consumer internet traffic&lt;/a&gt; by 2017.  In this context, browser support for protected content distribution is likely to be become increasingly significant, as &lt;a href=&quot;https://blog.mozilla.org/security/2013/01/29/putting-users-in-control-of-plugins/&quot; rel=&quot;noopener&quot;&gt;browser vendors&lt;/a&gt; &lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/developers/npapi-deprecation&quot; rel=&quot;noopener&quot;&gt;curtail support&lt;/a&gt; for APIs that most media plugins rely on.&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/eme-basics/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;specs-and-standards&quot;&gt;Specs and standards &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#specs-and-standards&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/encrypted-media/&quot; rel=&quot;noopener&quot;&gt;EME spec&lt;/a&gt;: latest Editor&#39;s Draft&amp;lt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=60397&quot; rel=&quot;noopener&quot;&gt;Common Encryption (CENC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.w3.org/TR/media-source/&quot; rel=&quot;noopener&quot;&gt;Media Source Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://standards.iso.org/ittf/PubliclyAvailableStandards/c057623_ISO_IEC_23009-1_2012.zip&quot; rel=&quot;noopener&quot;&gt;DASH standard&lt;/a&gt; (yes, it&#39;s a PDF)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dashif.org/mpeg-dash/&quot; rel=&quot;noopener&quot;&gt;Overview of the DASH standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;articles&quot;&gt;Articles &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/eme-basics/#articles&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://vimeo.com/62269279&quot; rel=&quot;noopener&quot;&gt;DTG Webinar&lt;/a&gt; (partially obsolete)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hsivonen.fi/eme/&quot; rel=&quot;noopener&quot;&gt;What is EME?&lt;/a&gt;, by Henri Sivonen&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://updates.html5rocks.com/2011/11/Stream-video-using-the-MediaSource-API&quot; rel=&quot;noopener&quot;&gt;HTML5 Rocks Media Source Extensions article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MPEG-DASH Test Streams: &lt;a href=&quot;http://bbc.co.uk/rd/blog/2013/09/mpeg-dash-test-streams&quot; rel=&quot;noopener&quot;&gt;BBC R&amp;amp;D blog post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Build the backend services needed for a WebRTC app</title>
    <link href="https://web.dev/webrtc-infrastructure/"/>
    <updated>2013-11-04T00:00:00Z</updated>
    <id>https://web.dev/webrtc-infrastructure/</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;  WebRTC enables peer-to-peer communication, but it still needs servers so that clients can exchange metadata to coordinate communication through a process called signaling, and to cope with network address translators (NATs) and firewalls.  This article shows you how to build a signaling service, and how to deal with the quirks of real-world connectivity with STUN and TURN servers. It also explains how WebRTC apps can handle multiparty calls and interact with services, such as VoIP and PSTN (also known as telephones).  If you&#39;re not familiar with the basics of WebRTC, see &lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/basics&quot;&gt;Get started with WebRTC&lt;/a&gt; before you read this article.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-is-signaling&quot;&gt;What is signaling? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#what-is-signaling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Signaling is the process of coordinating communication. In order for a WebRTC app to set up a call, its clients need to exchange the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Session-control messages used to open or close communication&lt;/li&gt;
&lt;li&gt;Error messages&lt;/li&gt;
&lt;li&gt;Media metadata, such as codecs, codec settings, bandwidth, and media types&lt;/li&gt;
&lt;li&gt;Key data used to establish secure connections&lt;/li&gt;
&lt;li&gt;Network data, such as a host&#39;s IP address and port as seen by the outside world&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This signaling process needs a way for clients to pass messages back and forth. That mechanism is not implemented by the WebRTC APIs. You need to build it yourself. Later in this article, you learn ways to build a signaling service. First, however, you need a little context.&lt;/p&gt;
&lt;h3 id=&quot;why-is-signaling-not-defined-by-webrtc&quot;&gt;Why is signaling not defined by WebRTC? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#why-is-signaling-not-defined-by-webrtc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To avoid redundancy and to maximize compatibility with established technologies, signaling methods and protocols are not specified by WebRTC standards. This approach is outlined by the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-03#section-1.1&quot; rel=&quot;noopener&quot;&gt;JavaScript Session Establishment Protocol (JSEP)&lt;/a&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; The thinking behind WebRTC call setup has been to fully specify and control the media plane, but to leave the signaling plane up to the app as much as possible. The rationale is that different apps may prefer to use different protocols, such as the existing SIP or Jingle call signaling protocols, or something custom to the particular app, perhaps for a novel use case. In this approach, the key information that needs to be exchanged is the multimedia session description, which specifies the necessary transport and media configuration information necessary to establish the media plane. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;JSEP&#39;s architecture also avoids a browser having to save state, that is, to function as a signaling state machine. This would be problematic if, for example, signaling data was lost each time a page was reloaded. Instead, signaling state can be saved on a server.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;JSEP architecture diagram&quot; decoding=&quot;async&quot; height=&quot;499&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/EzU1PV2xaYrKCARCFIW9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;JSEP architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;JSEP requires the exchange between peers of &lt;strong&gt;offer&lt;/strong&gt; and &lt;strong&gt;answer&lt;/strong&gt;, the media metadata mentioned above. Offers and answers are communicated in Session Description Protocol (SDP) format, which look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;v=0&lt;br /&gt;o=- 7614219274584779017 2 IN IP4 127.0.0.1&lt;br /&gt;s=-&lt;br /&gt;t=0 0&lt;br /&gt;a=group:BUNDLE audio video&lt;br /&gt;a=msid-semantic: WMS&lt;br /&gt;m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126&lt;br /&gt;c=IN IP4 0.0.0.0&lt;br /&gt;a=rtcp:1 IN IP4 0.0.0.0&lt;br /&gt;a=ice-ufrag:W2TGCZw2NZHuwlnf&lt;br /&gt;a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW&lt;br /&gt;a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level&lt;br /&gt;a=mid:audio&lt;br /&gt;a=rtcp-mux&lt;br /&gt;a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe&lt;br /&gt;a=rtpmap:111 opus/48000/2&lt;br /&gt;…&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Want to know what all this SDP gobbledygook actually means? Take a look at the &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-nandakumar-rtcweb-sdp/?include_text=1&quot; rel=&quot;noopener&quot;&gt;Internet Engineering Task Force (IETF) examples&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bear in mind that WebRTC is designed so that the offer or answer can be tweaked before being set as the local or remote description by editing the values in the SDP text. For example, the &lt;code&gt;preferAudioCodec()&lt;/code&gt; function in &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt; can be used to set the default codec and bitrate. SDP is somewhat painful to manipulate with JavaScript and there is discussion about whether future versions of WebRTC should use JSON instead, but there are &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-03#section-3.3&quot; rel=&quot;noopener&quot;&gt;some advantages&lt;/a&gt; to sticking with SDP.&lt;/p&gt;
&lt;h3 id=&quot;rtcpeerconnection-api-and-signaling-offer,-answer,-and-candidate&quot;&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; API and signaling: Offer, answer, and candidate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#rtcpeerconnection-api-and-signaling-offer,-answer,-and-candidate&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; is the API used by WebRTC apps to create a connection between peers, and communicate audio and video.&lt;/p&gt;
&lt;p&gt;To initialize this process, &lt;code&gt;RTCPeerConnection&lt;/code&gt; has two tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ascertain local media conditions, such as resolution and codec capabilities. This is the metadata used for the offer-and-answer mechanism.&lt;/li&gt;
&lt;li&gt;Get potential network addresses for the app&#39;s host, known as &lt;em&gt;candidates&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once this local data has been ascertained, it must be exchanged through a signaling mechanism with the remote peer.&lt;/p&gt;
&lt;p&gt;Imagine &lt;a href=&quot;https://xkcd.com/177/&quot; rel=&quot;noopener&quot;&gt;Alice is trying to call Eve&lt;/a&gt;. Here&#39;s the full offer/answer mechanism in all its gory detail:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice creates an &lt;code&gt;RTCPeerConnection&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Alice creates an offer (an SDP session description) with the &lt;code&gt;RTCPeerConnection&lt;/code&gt; &lt;code&gt;createOffer()&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Alice calls &lt;code&gt;setLocalDescription()&lt;/code&gt; with her offer.&lt;/li&gt;
&lt;li&gt;Alice stringifies the offer and uses a signaling mechanism to send it to Eve.&lt;/li&gt;
&lt;li&gt;Eve calls &lt;code&gt;setRemoteDescription()&lt;/code&gt; with Alice&#39;s offer, so that her &lt;code&gt;RTCPeerConnection&lt;/code&gt; knows about Alice&#39;s setup.&lt;/li&gt;
&lt;li&gt;Eve calls &lt;code&gt;createAnswer()&lt;/code&gt; and the success callback for this is passed a local session description - Eve&#39;s answer.&lt;/li&gt;
&lt;li&gt;Eve sets her answer as the local description by calling &lt;code&gt;setLocalDescription()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Eve then uses the signaling mechanism to send her stringified answer to Alice.&lt;/li&gt;
&lt;li&gt;Alice sets Eve&#39;s answer as the remote session description using &lt;code&gt;setRemoteDescription()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&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;a href=&quot;https://www.urbandictionary.com/define.php?term=strewth&quot;&gt;Strewth!&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Alice and Eve also need to exchange network information. The expression &amp;quot;finding candidates&amp;quot; refers to the process of finding network interfaces and ports using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment&quot; rel=&quot;noopener&quot;&gt;ICE framework&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice creates an &lt;code&gt;RTCPeerConnection&lt;/code&gt; object with an &lt;code&gt;onicecandidate&lt;/code&gt; handler.&lt;/li&gt;
&lt;li&gt;The handler is called when network candidates become available.&lt;/li&gt;
&lt;li&gt;In the handler, Alice sends stringified candidate data to Eve through their signaling channel.&lt;/li&gt;
&lt;li&gt;When Eve gets a candidate message from Alice, she calls &lt;code&gt;addIceCandidate()&lt;/code&gt; to add the candidate to the remote peer description.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JSEP supports &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-03#section-3.4.1&quot; rel=&quot;noopener&quot;&gt;ICE Candidate Trickling&lt;/a&gt;, which allows the caller to incrementally provide candidates to the callee after the initial offer, and for the callee to begin acting on the call and set up a connection without waiting for all candidates to arrive.&lt;/p&gt;
&lt;h3 id=&quot;code-webrtc-for-signaling&quot;&gt;Code WebRTC for signaling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#code-webrtc-for-signaling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following code snippet is a &lt;a href=&quot;https://w3c.github.io/webrtc-pc/#simple-peer-to-peer-example&quot; rel=&quot;noopener&quot;&gt;W3C code example&lt;/a&gt; that summarizes the complete signaling process. The code assumes the existence of some signaling mechanism, &lt;code&gt;SignalingChannel&lt;/code&gt;. Signaling is discussed in greater detail later.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// handles JSON.stringify/parse&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; signaling &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;SignalingChannel&lt;/span&gt;&lt;span class=&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; constraints &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 literal-property property&quot;&gt;audio&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;span class=&quot;token literal-property property&quot;&gt;video&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;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; configuration &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 literal-property property&quot;&gt;iceServers&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 literal-property property&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;stun:stun.example.org&#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 keyword&quot;&gt;const&lt;/span&gt; pc &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;configuration&lt;span 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;// Send any ice candidates to the other peer.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onicecandidate&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 parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;candidate&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 operator&quot;&gt;=&gt;&lt;/span&gt; signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;candidate&lt;span class=&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;// Let the &quot;negotiationneeded&quot; event trigger offer generation.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onnegotiationneeded&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 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;try&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocalDescription&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createOffer&lt;/span&gt;&lt;span class=&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;// send the offer to the other peer&lt;/span&gt;&lt;br /&gt;    signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&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;desc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localDescription&lt;span class=&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// After remote track media arrives, show it in remote video element.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;ontrack&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 parameter&quot;&gt;event&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 comment&quot;&gt;// Don&#39;t set srcObject again if it is already set.&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;remoteView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&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;  remoteView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;streams&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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Call start() to initiate.&lt;/span&gt;&lt;br /&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;start&lt;/span&gt;&lt;span class=&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;try&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 local stream, show it in self-view, and add it to be sent.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracks&lt;/span&gt;&lt;span class=&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;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;track&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;br /&gt;      pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;track&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stream&lt;span class=&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;    selfView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stream&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;desc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; candidate&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 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;try&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;desc&lt;span class=&quot;token punctuation&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;// If you get an offer, you need to reply with an answer.&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;desc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;offer&#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;await&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRemoteDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&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; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracks&lt;/span&gt;&lt;span class=&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;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;track&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;br /&gt;          pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;track&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stream&lt;span class=&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocalDescription&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAnswer&lt;/span&gt;&lt;span class=&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;        signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&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;desc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localDescription&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;answer&#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;await&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRemoteDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&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;        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;Unsupported SDP type.&#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;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;candidate&lt;span class=&quot;token punctuation&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addIceCandidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;candidate&lt;span class=&quot;token punctuation&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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To see the offer/answer and candidate-exchange processes in action, see &lt;a href=&quot;https://simpl.info/rtcpeerconnection/&quot; rel=&quot;noopener&quot;&gt;simpl.info RTCPeerConnection&lt;/a&gt; and look at the console log for a single-page video chat example. If you want more, download a complete dump of WebRTC signaling and stats from the about://webrtc-internals page in Google Chrome or the opera://webrtc-internals page in Opera.&lt;/p&gt;
&lt;h3 id=&quot;peer-discovery&quot;&gt;Peer discovery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#peer-discovery&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is a fancy way of asking, &amp;quot;How do I find someone to talk to?&amp;quot;&lt;/p&gt;
&lt;p&gt;For telephone calls, you have telephone numbers and directories. For online video chat and messaging, you need identity and presence management systems, and a means for users to initiate sessions. WebRTC apps need a way for clients to signal to each other that they want to start or join a call.&lt;/p&gt;
&lt;p&gt;Peer discovery mechanisms are not defined by WebRTC and you don&#39;t go into the options here. The process can be as simple as emailing or messaging a URL. For video chat apps, such as &lt;a href=&quot;https://talky.io/&quot; rel=&quot;noopener&quot;&gt;Talky&lt;/a&gt;, &lt;a href=&quot;https://tawk.com/&quot; rel=&quot;noopener&quot;&gt;tawk.to&lt;/a&gt; and &lt;a href=&quot;https://browsermeeting.com/&quot; rel=&quot;noopener&quot;&gt;Browser Meeting&lt;/a&gt;, you invite people to a call by sharing a custom link. Developer Chris Ball built an intriguing &lt;a href=&quot;https://blog.printf.net/articles/2013/05/17/webrtc-without-a-signaling-server/&quot; rel=&quot;noopener&quot;&gt;serverless-webrtc&lt;/a&gt; experiment that enables WebRTC call participants to exchange metadata by any messaging service they like, such as IM, email, or homing pigeon.&lt;/p&gt;
&lt;h2 id=&quot;how-can-you-build-a-signaling-service&quot;&gt;How can you build a signaling service? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#how-can-you-build-a-signaling-service&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To reiterate, signaling protocols and mechanisms are not defined by WebRTC standards. Whatever you choose, you need an intermediary server to exchange signaling messages and app data between clients. Sadly, a web app cannot simply shout into the internet, &amp;quot;Connect me to my friend!&amp;quot;&lt;/p&gt;
&lt;p&gt;Thankfully signaling messages are small and mostly exchanged at the start of a call. In testing with &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt; for a video chat session, a total of around 30-45 messages were handled by the signaling service with a total size for all messages of around 10KB.&lt;/p&gt;
&lt;p&gt;As well as being relatively undemanding in terms of bandwidth, WebRTC signaling services don&#39;t consume much processing or memory because they only need to relay messages and retain a small amount of session state data, such as which clients are connected.&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 signaling mechanism used to exchange session metadata can also be used to communicate app data. It&#39;s just a messaging service! &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;push-messages-from-the-server-to-the-client&quot;&gt;Push messages from the server to the client &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#push-messages-from-the-server-to-the-client&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A message service for signaling needs to be bidirectional: client to server and server to client. Bidirectional communication goes against the HTTP client/server request/response model, but various hacks such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Comet_(programming)&quot; rel=&quot;noopener&quot;&gt;long polling&lt;/a&gt; have been developed over many years in order to push data from a service running on a web server to a web app running in a browser.&lt;/p&gt;
&lt;p&gt;More recently, the &lt;a href=&quot;https://www.html5rocks.com/tutorials/eventsource/basics/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;EventSource&lt;/code&gt; API&lt;/a&gt; has been &lt;a href=&quot;https://caniuse.com/?search=eventsource&quot; rel=&quot;noopener&quot;&gt;widely implemented&lt;/a&gt;. This enables server-sent events - data sent from a web server to a browser client through HTTP. &lt;code&gt;EventSource&lt;/code&gt; is designed for one-way messaging, but it can be used in combination with XHR to build a service for exchanging signaling messages. A signaling service passes a message from a caller, delivered by XHR request, by pushing it through &lt;code&gt;EventSource&lt;/code&gt; to the callee.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.html5rocks.com/tutorials/websockets/basics/&quot; rel=&quot;noopener&quot;&gt;WebSocket&lt;/a&gt; is a more-natural solution, designed for full duplex client–server communication - messages that can flow in both directions at the same time. One advantage of a signaling service built with pure WebSocket or server-sent events (&lt;code&gt;EventSource&lt;/code&gt;) is that the backend for these APIs can be implemented on a variety of web frameworks common to most web-hosting packages for languages such as PHP, Python, and Ruby.&lt;/p&gt;
&lt;p&gt;All modern browsers except Opera Mini &lt;a href=&quot;https://caniuse.com/#search=websocket&quot; rel=&quot;noopener&quot;&gt;support WebSocket&lt;/a&gt; and, more importantly, all browsers that support WebRTC also support WebSocket, both on desktop and mobile. &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security&quot; rel=&quot;noopener&quot;&gt;TLS&lt;/a&gt; should be used for all connections to ensure messages cannot be intercepted unencrypted and also to  &lt;a href=&quot;https://www.infoq.com/articles/Web-Sockets-Proxy-Servers&quot; rel=&quot;noopener&quot;&gt;reduce problems with proxy traversal&lt;/a&gt;. (For more information about WebSocket and proxy traversal see the &lt;a href=&quot;https://hpbn.co/webrtc&quot; rel=&quot;noopener&quot;&gt;WebRTC chapter&lt;/a&gt; in Ilya Grigorik&#39;s &lt;strong&gt;High Performance Browser Networking&lt;/strong&gt;.)&lt;/p&gt;
&lt;p&gt;It is also possible to handle signaling by getting WebRTC clients to poll a messaging server repeatedly through Ajax, but that leads to a lot of redundant network requests, which is especially problematic for mobile devices. Even after a session has been established, peers need to poll for signaling messages in case of changes or session termination by other peers. The &lt;a href=&quot;https://webrtcbook.com/&quot; rel=&quot;noopener&quot;&gt;WebRTC Book&lt;/a&gt; app example takes this option with some optimizations for polling frequency.&lt;/p&gt;
&lt;h3 id=&quot;scale-signaling&quot;&gt;Scale signaling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#scale-signaling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although a signaling service consumes relatively little bandwidth and CPU per client, signaling servers for a popular app may have to handle a lot of messages from different locations with high levels of concurrency. WebRTC apps that get a lot of traffic need signaling servers able to handle considerable load.
You don&#39;t go into detail here, but there are a number of options for high-volume, high-performance messaging, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Xmpp&quot; rel=&quot;noopener&quot;&gt;eXtensible Messaging and Presence Protocol&lt;/a&gt; (XMPP), originally known as Jabber-a protocol developed for instant messaging that can be used for signaling (Server implementations include &lt;a href=&quot;https://en.wikipedia.org/wiki/Ejabberd&quot; rel=&quot;noopener&quot;&gt;ejabberd&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Openfire&quot; rel=&quot;noopener&quot;&gt;Openfire&lt;/a&gt;. JavaScript clients, such as &lt;a href=&quot;https://strophe.im/strophejs/&quot; rel=&quot;noopener&quot;&gt;Strophe.js&lt;/a&gt;, use &lt;a href=&quot;https://en.wikipedia.org/wiki/BOSH&quot; rel=&quot;noopener&quot;&gt;BOSH&lt;/a&gt; to emulate bidirectional streaming, but for &lt;a href=&quot;https://stackoverflow.com/questions/7327153/xmpp-bosh-vs-comet&quot; rel=&quot;noopener&quot;&gt;various reasons&lt;/a&gt;, BOSH may not be as efficient as WebSocket and, for the same reasons, may not scale well.) (On a tangent, &lt;a href=&quot;https://en.wikipedia.org/wiki/Jingle_(protocol)&quot; rel=&quot;noopener&quot;&gt;Jingle&lt;/a&gt; is an XMPP extension to enable voice and video. The WebRTC project uses network and transport components from the &lt;a href=&quot;https://developers.google.com/talk/libjingle/developer_guide&quot; rel=&quot;noopener&quot;&gt;libjingle&lt;/a&gt; library - a C++ implementation of Jingle.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open source libraries, such as &lt;a href=&quot;https://zeromq.org/&quot; rel=&quot;noopener&quot;&gt;ZeroMQ&lt;/a&gt; (as used by TokBox for their &lt;a href=&quot;https://www.tokbox.com/blog/tokbox-builds-it%E2%80%99s-own-internal-messaging-infrastructure/&quot; rel=&quot;noopener&quot;&gt;Rumour&lt;/a&gt; service) and &lt;a href=&quot;https://en.wikipedia.org/wiki/Open_Message_Queue&quot; rel=&quot;noopener&quot;&gt;OpenMQ&lt;/a&gt; (&lt;a href=&quot;https://avalanche123.com/blog/2012/02/25/interacting-with-zeromq-from-the-browser/&quot; rel=&quot;noopener&quot;&gt;NullMQ&lt;/a&gt; applies ZeroMQ concepts to web platforms using the &lt;a href=&quot;https://stomp.github.io/&quot; rel=&quot;noopener&quot;&gt;STOMP protocol&lt;/a&gt; over WebSocket.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commercial cloud-messaging platforms that use WebSocket (though they may fall back to long polling), such as &lt;a href=&quot;https://pusher.com/&quot; rel=&quot;noopener&quot;&gt;Pusher&lt;/a&gt;, &lt;a href=&quot;https://kaazing.com/&quot; rel=&quot;noopener&quot;&gt;Kaazing&lt;/a&gt;, and &lt;a href=&quot;https://pubnub.com/&quot; rel=&quot;noopener&quot;&gt;PubNub&lt;/a&gt; (PubNub also has an &lt;a href=&quot;https://github.com/pubnub/webrtc&quot; rel=&quot;noopener&quot;&gt;API for WebRTC&lt;/a&gt;.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commercial WebRTC platforms, such as &lt;a href=&quot;https://vline.com/&quot; rel=&quot;noopener&quot;&gt;vLine&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(Developer Phil Leggetter&#39;s &lt;a href=&quot;https://www.leggetter.co.uk/real-time-web-technologies-guide&quot; rel=&quot;noopener&quot;&gt;Real-Time Web Technologies Guide&lt;/a&gt; provides a comprehensive list of messaging services and libraries.)&lt;/p&gt;
&lt;h3 id=&quot;build-a-signaling-service-with-socketio-on-node&quot;&gt;Build a signaling service with Socket.io on Node &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#build-a-signaling-service-with-socketio-on-node&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following is code for a simple web app that uses a signaling service built with &lt;a href=&quot;https://socket.io/&quot; rel=&quot;noopener&quot;&gt;Socket.io&lt;/a&gt; on &lt;a href=&quot;https://nodejs.org/&quot; rel=&quot;noopener&quot;&gt;Node&lt;/a&gt;. The design of Socket.io makes it simple to build a service to exchange messages and Socket.io is particularly suited to WebRTC signaling because of its built-in concept of rooms. This example is not designed to scale as a production-grade signaling service, but is simple to understand for a relatively small number of users.&lt;/p&gt;
&lt;p&gt;Socket.io uses WebSocket with fallbacks: AJAX long polling, AJAX multipart streaming, Forever Iframe, and JSONP polling. It has been ported to various backends, but is perhaps best known for its Node version used in this example.&lt;/p&gt;
&lt;p&gt;There&#39;s no WebRTC in this example. It&#39;s designed only to show how to build signaling into a web app. View the console log to see what&#39;s happening as clients join a room and exchange messages. This &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/#0&quot; rel=&quot;noopener&quot;&gt;WebRTC codelab&lt;/a&gt; gives step-by-step instructions for how to integrate this into a complete WebRTC video chat app.&lt;/p&gt;
&lt;p&gt;Here is the client &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;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;WebRTC client&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;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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;&#39;&lt;/span&gt;/socket.io/socket.io.js&lt;span class=&quot;token punctuation&quot;&gt;&#39;&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 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;&#39;&lt;/span&gt;js/main.js&lt;span class=&quot;token punctuation&quot;&gt;&#39;&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here&#39;s the JavaScript file &lt;code&gt;main.js&lt;/code&gt; referenced in the client:&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; isInitiator&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;room &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Enter room name:&#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;const&lt;/span&gt; socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room &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;  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;Joining room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;create or join&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; room&lt;span class=&quot;token punctuation&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;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;full&#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 parameter&quot;&gt;room&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;  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;Room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; is full&#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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;empty&#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 parameter&quot;&gt;room&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;  isInitiator &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;  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;Room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; is empty&#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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;join&#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 parameter&quot;&gt;room&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;  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;Making request to join room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room&lt;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 string&quot;&gt;&#39;You are the initiator!&#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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;log&#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 parameter&quot;&gt;array&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;  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;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; array&lt;span class=&quot;token punctuation&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;Here&#39;s the complete server app:&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 keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;node-static&#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; http &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http&#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; file &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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Server&lt;span class=&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; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&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;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2013&lt;/span&gt;&lt;span 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; io &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;socket.io&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span 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;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;connection&#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 parameter&quot;&gt;socket&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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Convenience function to log server messages to the client&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&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; array &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;&gt;&gt;&gt; Message from server: &#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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&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; arguments&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arguments&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;log&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; array&lt;span class=&quot;token punctuation&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;  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Got message:&#39;&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 comment&quot;&gt;// For a real app, would be room only (not broadcast)&lt;/span&gt;&lt;br /&gt;    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 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 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;  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;create or join&#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 parameter&quot;&gt;room&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; numClients &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; has &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; numClients &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; client(s)&#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 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;Request to create or join room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room&lt;span 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;numClients &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;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;created&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numClients &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;      io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room&lt;span class=&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;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;join&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;joined&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; room&lt;span class=&quot;token punctuation&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;span class=&quot;token comment&quot;&gt;// max two clients&lt;/span&gt;&lt;br /&gt;      socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;full&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; room&lt;span class=&quot;token punctuation&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;    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;emit(): client &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39; joined room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;broadcast(): client &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39; joined room &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;(You don&#39;t need to learn about node-static for this. It just happens to be used in this example.)&lt;/p&gt;
&lt;p&gt;To run this app on localhost, you need to have Node, Socket.IO, and &lt;a href=&quot;https://github.com/cloudhead/node-static&quot; rel=&quot;noopener&quot;&gt;node-static&lt;/a&gt; installed. Node can be downloaded from &lt;a href=&quot;https://nodejs.org/&quot; rel=&quot;noopener&quot;&gt;Node.js&lt;/a&gt; (installation is straightforward and quick). To install Socket.IO and node-static, run Node Package Manager from a terminal in your app directory:&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; socket.io&lt;br /&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; node-static&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To start the server, run the following command from a terminal in your app directory:&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;node&lt;/span&gt; server.js&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;From your browser, open &lt;code&gt;localhost:2013&lt;/code&gt;. Open a new tab or window in any browser and open &lt;code&gt;localhost:2013&lt;/code&gt; again. To see what&#39;s happening, check the console. In Chrome and Opera, you can access the console through Google Chrome Developer Tools with &lt;code&gt;Ctrl+Shift+J&lt;/code&gt; (or &lt;code&gt;Command+Option+J&lt;/code&gt; on Mac).&lt;/p&gt;
&lt;p&gt;Whatever approach you choose for signaling, your backend and client app - at the very least - need to provide services similar to this example.&lt;/p&gt;
&lt;h3 id=&quot;signaling-gotchas&quot;&gt;Signaling gotchas &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#signaling-gotchas&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; won&#39;t start gathering candidates until &lt;code&gt;setLocalDescription()&lt;/code&gt; is called. This is mandated in the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-03#section-4.2.4&quot; rel=&quot;noopener&quot;&gt;JSEP IETF draft&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Take advantage of Trickle ICE. Call &lt;code&gt;addIceCandidate()&lt;/code&gt; as soon as candidates arrive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;readymade-signaling-servers&quot;&gt;Readymade signaling servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#readymade-signaling-servers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you don&#39;t want to roll your own, there are several WebRTC signaling servers available, which use Socket.IO like the previous example and are integrated with WebRTC client JavaScript libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webRTC/webRTC.io&quot; rel=&quot;noopener&quot;&gt;webRTC.io&lt;/a&gt; is one of the first abstraction libraries for WebRTC.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/andyet/signalmaster&quot; rel=&quot;noopener&quot;&gt;Signalmaster&lt;/a&gt; is a signaling server created for use with the &lt;a href=&quot;https://github.com/HenrikJoreteg/SimpleWebRTC&quot; rel=&quot;noopener&quot;&gt;SimpleWebRTC&lt;/a&gt; JavaScript client library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&#39;t want to write any code at all, complete commercial WebRTC platforms are available from companies, such as &lt;a href=&quot;https://www.vline.com/&quot; rel=&quot;noopener&quot;&gt;vLine&lt;/a&gt;, &lt;a href=&quot;https://tokbox.com/opentok&quot; rel=&quot;noopener&quot;&gt;OpenTok&lt;/a&gt;, and &lt;a href=&quot;https://wiki.asterisk.org/wiki/display/AST/Asterisk+WebRTC+Support&quot; rel=&quot;noopener&quot;&gt;Asterisk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the record, Ericsson built a &lt;a href=&quot;https://labs.ericsson.com/blog/a-web-rtc-tutorial&quot; rel=&quot;noopener&quot;&gt;signaling server using PHP on Apache&lt;/a&gt; in the early days of WebRTC. This is now somewhat obsolete, but it&#39;s worth looking at the code if you&#39;re considering something similar.&lt;/p&gt;
&lt;h3 id=&quot;signaling-security&quot;&gt;Signaling security &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#signaling-security&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Security is the art of making nothing happen.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Salman Rushdie&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Encryption is &lt;a href=&quot;https://www.ietf.org/proceedings/82/slides/rtcweb-13.pdf&quot; rel=&quot;noopener&quot;&gt;mandatory&lt;/a&gt; for all WebRTC components.&lt;/p&gt;
&lt;p&gt;However, signaling mechanisms aren&#39;t defined by WebRTC standards, so it&#39;s up to you to make signaling secure. If an attacker manages to hijack signaling, they can stop sessions, redirect connections, and record, alter, or inject content.&lt;/p&gt;
&lt;p&gt;The most important factor in securing signaling is to use secure protocols - HTTPS and WSS (for example, TLS) - which ensure that messages cannot be intercepted unencrypted. Also, be careful not to broadcast signaling messages in a way that they can be accessed by other callers using the same signaling 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; To secure a WebRTC app, it is absolutely imperative that signaling uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security&quot;&gt;TLS&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;after-signaling-use-ice-to-cope-with-nats-and-firewalls&quot;&gt;After signaling: Use ICE to cope with NATs and firewalls &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#after-signaling-use-ice-to-cope-with-nats-and-firewalls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For metadata signaling, WebRTC apps use an intermediary server, but for actual media and data streaming once a session is established, &lt;code&gt;RTCPeerConnection&lt;/code&gt; attempts to connect clients directly or peer-to-peer.&lt;/p&gt;
&lt;p&gt;In a simpler world, every WebRTC endpoint would have a unique address that it could exchange with other peers in order to communicate directly.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Simple peer to peer connection&quot; decoding=&quot;async&quot; height=&quot;188&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/LrErdUdxSBf8XaSJ1XhO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A world without NATs and firewalls&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In reality, most devices live behind one or more layers of &lt;a href=&quot;https://www.howstuffworks.com/nat.htm&quot; rel=&quot;noopener&quot;&gt;NAT&lt;/a&gt;, some have antivirus software that blocks certain ports and protocols, and many are behind proxies and corporate firewalls. A firewall and NAT may in fact be implemented by the same device, such as a home WIFI router.&lt;/p&gt;
&lt;figure&gt;   
  &lt;img alt=&quot;Peers behind NATs and firewalls&quot; decoding=&quot;async&quot; height=&quot;494&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVsek1SKMsrPTLQcRA63.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The real world&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;WebRTC apps can use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment&quot; rel=&quot;noopener&quot;&gt;ICE&lt;/a&gt; framework to overcome the complexities of real-world networking. To enable this to happen, your app must pass ICE server URLs to &lt;code&gt;RTCPeerConnection&lt;/code&gt;, as described in this article.&lt;/p&gt;
&lt;p&gt;ICE tries to find the best path to connect peers. It tries all possibilities in parallel and chooses the most efficient option that works. ICE first tries to make a connection using the host address obtained from a device&#39;s operating system and network card. If that fails (which it will for devices behind NATs), ICE obtains an external address using a STUN server and, if that fails, traffic is routed through a TURN relay server.&lt;/p&gt;
&lt;p&gt;In other words, a STUN server is used to get an external network address and TURN servers are used to relay traffic if direct (peer-to-peer) connection fails.&lt;/p&gt;
&lt;p&gt;Every TURN server supports STUN. A TURN server is a STUN server with additional built-in relaying functionality. ICE also copes with the complexities of NAT setups. In reality, NAT hole-punching may require more than just a public IP:port address.&lt;/p&gt;
&lt;p&gt;URLs for STUN and/or TURN servers are (optionally) specified by a WebRTC app in the &lt;code&gt;iceServers&lt;/code&gt; configuration object that is the first argument to the &lt;code&gt;RTCPeerConnection&lt;/code&gt; constructor. For &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;, that value looks like this:&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;  &#39;iceServers&#39;&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;      &#39;urls&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;stun&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;stun.l.google.com&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;19302&lt;/span&gt;&#39;&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;br /&gt;      &#39;urls&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;turn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;192.158&lt;/span&gt;.&lt;span class=&quot;token number&quot;&gt;29.39&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3478&lt;/span&gt;?transport=udp&#39;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &#39;credential&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;JZEOEt2V3Qb0y27GRntt2u2PAYA=&#39;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &#39;username&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;&lt;span class=&quot;token number&quot;&gt;28224511&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1379330808&lt;/span&gt;&#39;&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;br /&gt;      &#39;urls&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;turn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;192.158&lt;/span&gt;.&lt;span class=&quot;token number&quot;&gt;29.39&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3478&lt;/span&gt;?transport=tcp&#39;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &#39;credential&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;JZEOEt2V3Qb0y27GRntt2u2PAYA=&#39;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &#39;username&#39;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &#39;&lt;span class=&quot;token number&quot;&gt;28224511&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1379330808&lt;/span&gt;&#39;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span 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; The TURN credentials example was time-limited and expired in September 2013. TURN servers are expensive to run and you need to pay for your own servers or find a service provider. To test credentials, you can use the &lt;a href=&quot;https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice&quot;&gt;candidate gathering sample&lt;/a&gt; and check if you get a candidate with type &lt;code&gt;relay&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Once &lt;code&gt;RTCPeerConnection&lt;/code&gt; has that information, the ICE magic happens automatically. &lt;code&gt;RTCPeerConnection&lt;/code&gt; uses the ICE framework to work out the best path between peers, working with STUN and TURN servers as necessary.&lt;/p&gt;
&lt;h3 id=&quot;stun&quot;&gt;STUN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#stun&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.howstuffworks.com/nat.htm&quot; rel=&quot;noopener&quot;&gt;NATs&lt;/a&gt; provide a device with an IP address for use within a private local network, but this address can&#39;t be used externally. Without a public address, there&#39;s no way for WebRTC peers to communicate. To get around this problem, WebRTC uses &lt;a href=&quot;https://en.wikipedia.org/wiki/STUN&quot; rel=&quot;noopener&quot;&gt;STUN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;STUN servers live on the public internet and have one simple task - check the IP:port address of an incoming request (from an app running behind a NAT) and send that address back as a response. In other words, the app uses a STUN server to discover its IP:port from a public perspective. This process enables a WebRTC peer to get a publicly accessible address for itself and then pass it to another peer through a signaling mechanism in order to set up a direct link. (In practice, different NATs work in different ways and there may be multiple NAT layers, but the principle is still the same.)&lt;/p&gt;
&lt;p&gt;STUN servers don&#39;t have to do much or remember much, so relatively low-spec STUN servers can handle a large number of requests.&lt;/p&gt;
&lt;p&gt;Most WebRTC calls successfully make a connection using STUN - 86% according to &lt;a href=&quot;https://webrtcstats.com/&quot; rel=&quot;noopener&quot;&gt;Webrtcstats.com&lt;/a&gt;, though this can be less for calls between peers behind firewalls and complex NAT configurations.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Peer to peer connection using a STUN server&quot; decoding=&quot;async&quot; height=&quot;545&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/YGV8R7cI2RxpDtC0KMhl.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Using STUN servers to get public IP:port addresses&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;turn&quot;&gt;TURN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#turn&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; tries to set up direct communication between peers over UDP. If that fails, &lt;code&gt;RTCPeerConnection&lt;/code&gt; resorts to TCP. If that fails, TURN servers can be used as a fallback, relaying data between endpoints.&lt;/p&gt;
&lt;p&gt;Just to reiterate, TURN is used to relay audio, video, and data streaming between peers, not signaling data!&lt;/p&gt;
&lt;p&gt;TURN servers have public addresses, so they can be contacted by peers even if the peers are behind firewalls or proxies. TURN servers have a conceptually simple task - to relay a stream. However, unlike STUN servers, they inherently consume a lot of bandwidth. In other words, TURN servers need to be beefier.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Peer to peer connection using a STUN server&quot; decoding=&quot;async&quot; height=&quot;545&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/sXMerfYKQmlGTWOwUCe1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The full Monty: STUN, TURN, and signaling&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This diagram shows TURN in action. Pure STUN didn&#39;t succeed, so each peer resorts to using a TURN server.&lt;/p&gt;
&lt;h3 id=&quot;deploying-stun-and-turn-servers&quot;&gt;Deploying STUN and TURN servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#deploying-stun-and-turn-servers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For testing, Google runs a public STUN server, stun.l.google.com:19302, as used by &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;. For a production STUN/TURN service, use the rfc5766-turn-server. Source code for STUN and TURN servers is available on &lt;a href=&quot;https://github.com/coturn/rfc5766-turn-server/&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;, where you can also find links to several sources of information about server installation. A &lt;a href=&quot;https://groups.google.com/forum/#!msg/discuss-webrtc/X-OeIUC0efs/XW5Wf7Tt1vMJ&quot; rel=&quot;noopener&quot;&gt;VM image for Amazon Web Services&lt;/a&gt; is also available.&lt;/p&gt;
&lt;p&gt;An alternative TURN server is restund, available as &lt;a href=&quot;https://www.creytiv.com/restund.html&quot; rel=&quot;noopener&quot;&gt;source code&lt;/a&gt; and also for AWS. Here are instructions for how to set up restund on Compute Engine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open firewall as necessary for tcp=443, udp/tcp=3478.&lt;/li&gt;
&lt;li&gt;Create four instances, one for each public IP, Standard Ubuntu 12.06 image.&lt;/li&gt;
&lt;li&gt;Set up local firewall config (allow ANY from ANY).&lt;/li&gt;
&lt;li&gt;Install tools:&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;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gcc&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;Install libre from &lt;a href=&quot;https://creytiv.com/re.html&quot; rel=&quot;noopener&quot;&gt;creytiv.com/re.html&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fetch restund from &lt;a href=&quot;https://creytiv.com/restund.html&quot; rel=&quot;noopener&quot;&gt;creytiv.com/restund.html&lt;/a&gt; and unpack./&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wget&lt;/code&gt; &lt;a href=&quot;https://hancke.name/restund-auth.patch&quot; rel=&quot;noopener&quot;&gt;hancke.name/restund-auth.patch&lt;/a&gt; and apply with &lt;code&gt;patch -p1 &amp;lt; restund-auth.patch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make&lt;/code&gt;, &lt;code&gt;sudo make install&lt;/code&gt; for libre and restund.&lt;/li&gt;
&lt;li&gt;Adapt &lt;code&gt;restund.conf&lt;/code&gt; to your needs (replace IP addresses and make sure it contains the same shared secret) and copy to &lt;code&gt;/etc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;restund/etc/restund&lt;/code&gt; to &lt;code&gt;/etc/init.d/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configure restund:
&lt;ol&gt;
&lt;li&gt;Set &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;restund.conf&lt;/code&gt; to &lt;code&gt;/etc/restund.conf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;restund.conf&lt;/code&gt; to use the right 10. IP address.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Run restund&lt;/li&gt;
&lt;li&gt;Test using stund client from remote machine: &lt;code&gt;./client IP:port&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;beyond-one-to-one-multiparty-webrtc&quot;&gt;Beyond one-to-one: Multiparty WebRTC &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#beyond-one-to-one-multiparty-webrtc&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You may also want to take a look at Justin Uberti&#39;s proposed IETF standard for a &lt;a href=&quot;https://tools.ietf.org/html/draft-uberti-rtcweb-turn-rest-00&quot; rel=&quot;noopener&quot;&gt;REST API for access to TURN Services&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s easy to imagine use cases for media streaming that go beyond a simple one-to-one call. For example, video conferencing between a group of colleagues or a public event with one speaker and hundreds or millions of viewers.&lt;/p&gt;
&lt;p&gt;A WebRTC app can use multiple RTCPeerConnections so that every endpoint connects to every other endpoint in a mesh configuration. This is the approach taken by apps, such as &lt;a href=&quot;https://talky.io/&quot; rel=&quot;noopener&quot;&gt;talky.io&lt;/a&gt;, and works remarkably well for a small handful of peers. Beyond that, processing and bandwidth consumption becomes excessive, especially for mobile clients.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Mesh: small N-way call&quot; decoding=&quot;async&quot; height=&quot;288&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/wnlggGjq35kOISPOfV0a.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;Full mesh topology: Everyone connected to everyone&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Alternatively, a WebRTC app could choose one endpoint to distribute streams to all others in a star configuration. It would also be possible to run a WebRTC endpoint on a server and construct your own redistribution mechanism (a &lt;a href=&quot;https://code.google.com/p/webrtc/source/browse/#svn%2Ftrunk%2Ftalk&quot; rel=&quot;noopener&quot;&gt;sample client app&lt;/a&gt; is provided by webrtc.org).&lt;/p&gt;
&lt;p&gt;Since Chrome 31 and Opera 18, a &lt;code&gt;MediaStream&lt;/code&gt; from one &lt;code&gt;RTCPeerConnection&lt;/code&gt; can be used as the input for another. This can enable more flexible architectures because it enables a web app to handle call-routing by choosing which other peer to connect to. To see this in action, see &lt;a href=&quot;https://webrtc.github.io/samples/src/content/peerconnection/multiple-relay/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Peer connection relay&lt;/a&gt; and &lt;a href=&quot;https://webrtc.github.io/samples/src/content/peerconnection/multiple/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Multiple peer connections&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;multipoint-control-unit&quot;&gt;Multipoint Control Unit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#multipoint-control-unit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A better option for a large number of endpoints is to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Multipoint_control_unit&quot; rel=&quot;noopener&quot;&gt;Multipoint Control Unit&lt;/a&gt; (MCU). This is a server that works as a bridge to distribute media between a large number of participants. MCUs can cope with different resolutions, codecs, and frame rates in a video conference; handle transcoding; do selective stream forwarding; and mix or record audio and video. For multiparty calls, there are a number of issues to consider, particularly how to display multiple video inputs and mix audio from multiple sources. Cloud platforms, such as &lt;a href=&quot;https://www.vline.com/&quot; rel=&quot;noopener&quot;&gt;vLine&lt;/a&gt;, also attempt to optimize traffic routing.&lt;/p&gt;
&lt;p&gt;It&#39;s possible to buy a complete MCU hardware package or build your own.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Rear view of Cisco MCU5300&quot; decoding=&quot;async&quot; height=&quot;51&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/d8MN1t22X6P7lD5ql9AZ.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;The back of a &lt;a href=&quot;https://cisco.com/en/US/products/ps12283&quot;&gt;Cisco MCU&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Several open source MCU software options are available. For example, &lt;a href=&quot;https://lynckia.com/&quot; rel=&quot;noopener&quot;&gt;Licode&lt;/a&gt; (previously known as Lynckia) produces an open source MCU for WebRTC. OpenTok has &lt;a href=&quot;https://www.tokbox.com/blog/mantis-next-generation-cloud-technology-for-webrtc/&quot; rel=&quot;noopener&quot;&gt;Mantis&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;beyond-browsers-voip,-telephones,-and-messaging&quot;&gt;Beyond browsers: VoIP, telephones, and messaging &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#beyond-browsers-voip,-telephones,-and-messaging&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The standardized nature of WebRTC makes it possible to establish communication between a WebRTC app running in a browser and a device or platform running on another communication platform, such as a telephone or a video-conferencing system.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Session_Initiation_Protocol&quot; rel=&quot;noopener&quot;&gt;SIP&lt;/a&gt; is a signaling protocol used by VoIP and video-conferencing systems. To enable communication between a WebRTC web app and a SIP client, such as a video-conferencing system, WebRTC needs a proxy server to mediate signaling. Signaling must flow through the gateway but, once communication has been established, SRTP traffic (video and audio) can flow directly peer to peer.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Public_switched_telephone_network&quot; rel=&quot;noopener&quot;&gt;Public Switched Telephone Network (PSTN)&lt;/a&gt; is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Circuit_switching&quot; rel=&quot;noopener&quot;&gt;circuit-switched&lt;/a&gt; network of all &amp;quot;plain old&amp;quot; analog telephones. For calls between WebRTC web apps and telephones, traffic must go through a PSTN gateway. Likewise, WebRTC web apps need an intermediary XMPP server to communicate with &lt;a href=&quot;https://en.wikipedia.org/wiki/Jingle_(protocol)&quot; rel=&quot;noopener&quot;&gt;Jingle&lt;/a&gt; endpoints such as IM clients. Jingle was developed by Google as an extension to XMPP to enable voice and video for messaging services. Current WebRTC implementations are based on the C++ &lt;a href=&quot;https://developers.google.com/talk/libjingle/developer_guide&quot; rel=&quot;noopener&quot;&gt;libjingle&lt;/a&gt; library, an implementation of Jingle initially developed for Talk.&lt;/p&gt;
&lt;p&gt;A number of apps, libraries, and platforms make use of WebRTC&#39;s ability to communicate with the outside world:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.google.com/p/sipml5/&quot; rel=&quot;noopener&quot;&gt;sipML5&lt;/a&gt;: an open source JavaScript SIP client&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jssip.net/&quot; rel=&quot;noopener&quot;&gt;jsSIP&lt;/a&gt;: JavaScript SIP library&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://phono.com/&quot; rel=&quot;noopener&quot;&gt;Phono&lt;/a&gt;: open source JavaScript phone API built as a plugin&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zingaya.com/product/&quot; rel=&quot;noopener&quot;&gt;Zingaya&lt;/a&gt;: an embeddable phone widget&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.twilio.com/&quot; rel=&quot;noopener&quot;&gt;Twilio&lt;/a&gt;: voice and messaging&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.uberconference.com/&quot; rel=&quot;noopener&quot;&gt;Uberconference&lt;/a&gt;: conferencing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The sipML5 developers have also built the &lt;a href=&quot;https://code.google.com/p/webrtc2sip/&quot; rel=&quot;noopener&quot;&gt;webrtc2sip&lt;/a&gt; gateway. Tethr and Tropo have demonstrated &lt;a href=&quot;https://tethr.tumblr.com/&quot; rel=&quot;noopener&quot;&gt;a framework for disaster communications&lt;/a&gt; &amp;quot;in a briefcase&amp;quot; using an &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenBTS&quot; rel=&quot;noopener&quot;&gt;OpenBTS cell&lt;/a&gt; to enable communications between feature phones and computers through WebRTC. That&#39;s telephone communication without a carrier!&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-infrastructure/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://bitbucket.org/webrtc/codelab&quot; rel=&quot;noopener&quot;&gt;WebRTC codelab&lt;/a&gt; provides step-by-step instructions for how to build a video and text chat app using a Socket.io signaling service running on Node.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=p2HzZkd2A40&quot; rel=&quot;noopener&quot;&gt;Google I/O WebRTC presentation from 2013&lt;/a&gt; with WebRTC tech lead, Justin Uberti&lt;/p&gt;
&lt;p&gt;Chris Wilson&#39;s SFHTML5 presentation - &lt;a href=&quot;https://www.youtube.com/watch?v=3Ifbqaw5l_I&quot; rel=&quot;noopener&quot;&gt;Introduction to WebRTC Apps&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The 350-page book &lt;a href=&quot;https://webrtcbook.com/&quot; rel=&quot;noopener&quot;&gt;WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web&lt;/a&gt; provides a lot of detail about data and signaling pathways, and includes a number of detailed network topology diagrams.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tokbox.com/blog/webrtc-and-signaling-what-two-years-has-taught-us/&quot; rel=&quot;noopener&quot;&gt;WebRTC and Signaling: What Two Years Has Taught Us&lt;/a&gt; - TokBox blog post about why leaving signaling out of the spec was a good idea&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/in/strongben&quot; rel=&quot;noopener&quot;&gt;Ben Strong&#39;s&lt;/a&gt; &lt;a href=&quot;https://thenewcircle.com/s/post/1548/a_practical_guide_to_building_webrtc_apps_ben_strong_video&quot; rel=&quot;noopener&quot;&gt;A Practical Guide to Building WebRTC Apps&lt;/a&gt; provides a lot of information about WebRTC topologies and infrastructure.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://chimera.labs.oreilly.com/books/1230000000545/ch18.html&quot; rel=&quot;noopener&quot;&gt;WebRTC chapter&lt;/a&gt; in &lt;a href=&quot;https://www.igvita.com/&quot; rel=&quot;noopener&quot;&gt;Ilya Grigorik&#39;s&lt;/a&gt; &lt;strong&gt;High Performance Browser Networking&lt;/strong&gt; goes deep into WebRTC architecture, use cases, and performance.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Get started with WebRTC</title>
    <link href="https://web.dev/webrtc-basics/"/>
    <updated>2012-07-23T00:00:00Z</updated>
    <id>https://web.dev/webrtc-basics/</id>
    <content type="html" mode="escaped">&lt;blockquote&gt;
&lt;p&gt;WebRTC is a new front in the long war for an open and unencumbered web.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;Brendan Eich, inventor of JavaScript&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;real-time-communication-without-plugins&quot;&gt;Real-time communication without plugins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#real-time-communication-without-plugins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine a world where your phone, TV, and computer could communicate on a common platform. Imagine it was easy to add video chat and peer-to-peer data sharing to your web app. That&#39;s the vision of WebRTC.&lt;/p&gt;
&lt;p&gt;Want to try it out? WebRTC is available on desktop and mobile in Google Chrome, Safari, Firefox, and Opera. A good place to start is the simple video chat app at &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt; in your browser.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Join&lt;/strong&gt; to join a chat room and let the app use your webcam.&lt;/li&gt;
&lt;li&gt;Open the URL displayed at the end of the page in a new tab or, better still, on a different computer.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick start &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#quick-start&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Haven&#39;t got time to read this article or only want code?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To get an overview of WebRTC, watch the following Google I/O video or view &lt;a href=&quot;https://io13webrtc.appspot.com/&quot; rel=&quot;noopener&quot;&gt;these slides&lt;/a&gt;:  &lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;p2HzZkd2A40&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;If you haven&#39;t used the &lt;code&gt;getUserMedia&lt;/code&gt; API, see &lt;a href=&quot;https://www.html5rocks.com/en/tutorials/getusermedia/intro&quot; rel=&quot;noopener&quot;&gt;Capture audio and video in HTML5&lt;/a&gt; and &lt;a href=&quot;https://www.simpl.info/getusermedia&quot; rel=&quot;noopener&quot;&gt;simpl.info getUserMedia&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To learn about the &lt;code&gt;RTCPeerConnection&lt;/code&gt; API, see the following example and &lt;a href=&quot;https://simpl.info/rtcpeerconnection&quot; rel=&quot;noopener&quot;&gt;&#39;simpl.info RTCPeerConnection&#39;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To learn how WebRTC uses servers for signaling, and firewall and NAT traversal, see the code and console logs from &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Can’t wait and just want to try WebRTC right now? Try some of the &lt;a href=&quot;https://webrtc.github.io/samples&quot; rel=&quot;noopener&quot;&gt;more-than 20 demos&lt;/a&gt; that exercise the WebRTC JavaScript APIs.&lt;/li&gt;
&lt;li&gt;Having trouble with your machine and WebRTC? Visit the &lt;a href=&quot;https://test.webrtc.org/&quot; rel=&quot;noopener&quot;&gt;WebRTC Troubleshooter&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alternatively, jump straight into the &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/&quot; rel=&quot;noopener&quot;&gt;WebRTC codelab&lt;/a&gt;, a step-by-step guide that explains how to build a complete video chat app, including a simple signaling server.&lt;/p&gt;
&lt;h2 id=&quot;a-very-short-history-of-webrtc&quot;&gt;A very short history of WebRTC &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#a-very-short-history-of-webrtc&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the last major challenges for the web is to enable human communication through voice and video: real-time communication or RTC for short. RTC should be as natural in a web app as entering text in a text input. Without it, you&#39;re limited in your ability to innovate and develop new ways for people to interact.&lt;/p&gt;
&lt;p&gt;Historically, RTC has been corporate and complex, requiring expensive audio and video technologies to be licensed or developed in house. Integrating RTC technology with existing content, data, and services has been difficult and time-consuming, particularly on the web.&lt;/p&gt;
&lt;p&gt;Gmail video chat became popular in 2008 and, in 2011, Google introduced Hangouts, which uses Talk (as did Gmail). Google bought GIPS, a company that developed many components required for RTC, such as codecs and echo cancellation techniques. Google open sourced the technologies developed by GIPS and engaged with relevant standards bodies at the Internet Engineering Task Force (IETF) and World Wide Web Consortium (W3C) to ensure industry consensus. In May 2011, Ericsson built &lt;a href=&quot;https://labs.ericsson.com/developer-community/blog/beyond-html5-peer-peer-conversational-video&quot; rel=&quot;noopener&quot;&gt;the first implementation of WebRTC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;WebRTC implemented open standards for real-time, plugin-free video, audio, and data communication. The need was real:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many web services used RTC, but needed downloads, native apps, or plugins. These included Skype, Facebook, and Hangouts.&lt;/li&gt;
&lt;li&gt;Downloading, installing, and updating plugins is complex, error prone, and annoying.&lt;/li&gt;
&lt;li&gt;Plugins are difficult to deploy, debug, troubleshoot, test, and maintain - and may require licensing and integration with complex, expensive technology. It&#39;s often difficult to persuade people to install plugins in the first place!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The guiding principles of the WebRTC project are that its APIs should be open source, free, standardized, built into web browsers, and more efficient than existing technologies.&lt;/p&gt;
&lt;h2 id=&quot;where-are-we-now&quot;&gt;Where are we now? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#where-are-we-now&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebRTC is used in various apps, such as Google Meet. WebRTC has also been integrated with &lt;a href=&quot;https://labs.ericsson.com/developer-community/blog/beyond-html5-conversational-voice-and-video-implemented-webkit-gtk&quot; rel=&quot;noopener&quot;&gt;WebKitGTK+&lt;/a&gt; and Qt native apps.&lt;/p&gt;
&lt;p&gt;WebRTC implements these three APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MediaStream&lt;/code&gt; (also known as &lt;code&gt;getUserMedia&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The APIs are defined in these two specs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/webrtc-pc/&quot; rel=&quot;noopener&quot;&gt;WebRTC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/mediacapture-streams&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getUserMedia&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three APIs are supported on mobile and desktop by Chrome, Safari, Firefox, Edge, and Opera.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getUserMedia&lt;/code&gt;: For demos and code, see &lt;a href=&quot;https://webrtc.github.io/samples&quot; rel=&quot;noopener&quot;&gt;WebRTC samples&lt;/a&gt; or try Chris Wilson&#39;s &lt;a href=&quot;https://webaudiodemos.appspot.com/&quot; rel=&quot;noopener&quot;&gt;amazing examples&lt;/a&gt; that use &lt;code&gt;getUserMedia&lt;/code&gt; as input for web audio.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt;: For a simple demo and a fully functional video-chat app, see &lt;a href=&quot;https://webrtc.github.io/samples/src/content/peerconnection/pc1/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Peer connection&lt;/a&gt; and &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;, respectively. This app uses &lt;a href=&quot;https://github.com/webrtc/adapter&quot; rel=&quot;noopener&quot;&gt;adapter.js&lt;/a&gt;, a JavaScript shim maintained by Google with help from the &lt;a href=&quot;https://github.com/webrtc/adapter/graphs/contributors&quot; rel=&quot;noopener&quot;&gt;WebRTC community&lt;/a&gt;, to abstract away browser differences and spec changes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt;: To see this in action, see &lt;a href=&quot;https://webrtc.github.io/samples/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples&lt;/a&gt; to check out one of the data-channel demos.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/#0&quot; rel=&quot;noopener&quot;&gt;WebRTC codelab&lt;/a&gt; shows how to use all three APIs to build a simple app for video chat and file sharing.&lt;/p&gt;
&lt;h2 id=&quot;your-first-webrtc&quot;&gt;Your first WebRTC &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#your-first-webrtc&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebRTC apps need to do several things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get streaming audio, video, or other data.&lt;/li&gt;
&lt;li&gt;Get network information, such as IP addresses and ports, and exchange it with other WebRTC clients (known as &lt;strong&gt;peers&lt;/strong&gt;) to enable connection, even through &lt;a href=&quot;https://en.wikipedia.org/wiki/NAT_traversal&quot; rel=&quot;noopener&quot;&gt;NATs&lt;/a&gt; and firewalls.&lt;/li&gt;
&lt;li&gt;Coordinate signaling communication to report errors and initiate or close sessions.&lt;/li&gt;
&lt;li&gt;Exchange information about media and client capability, such as resolution and codecs.&lt;/li&gt;
&lt;li&gt;Communicate streaming audio, video, or data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To acquire and communicate streaming data, WebRTC implements the following APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dvcs.w3.org/hg/audio/raw-file/tip/streams/StreamProcessing.html&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;MediaStream&lt;/code&gt;&lt;/a&gt; gets access to data streams, such as from the user&#39;s camera and microphone.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcpeerconnection-interface&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt;&lt;/a&gt; enables audio or video calling with facilities for encryption and bandwidth management.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcdatachannel&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt;&lt;/a&gt; enables peer-to-peer communication of generic data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(There is detailed discussion of the network and signaling aspects of WebRTC later.)&lt;/p&gt;
&lt;h2 id=&quot;mediastream-api-also-known-as-getusermedia-api&quot;&gt;&lt;code&gt;MediaStream&lt;/code&gt; API (also known as &lt;code&gt;getUserMedia&lt;/code&gt; API) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#mediastream-api-also-known-as-getusermedia-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://dev.w3.org/2011/webrtc/editor/getusermedia.html&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;MediaStream&lt;/code&gt; API&lt;/a&gt; represents synchronized streams of media. For example, a stream taken from camera and microphone input has synchronized video and audio tracks. (Don&#39;t confuse &lt;code&gt;MediaStreamTrack&lt;/code&gt; with the &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; element, which is something &lt;a href=&quot;https://www.html5rocks.com/en/tutorials/track/basics/&quot; rel=&quot;noopener&quot;&gt;entirely different&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Probably the easiest way to understand the &lt;code&gt;MediaStream&lt;/code&gt; API is to look at it in the wild:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In your browser, navigate to &lt;a href=&quot;https://webrtc.github.io/samples/src/content/getusermedia/gum/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples &lt;code&gt;getUserMedia&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open the console.&lt;/li&gt;
&lt;li&gt;Inspect the &lt;code&gt;stream&lt;/code&gt; variable, which is in global scope.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each &lt;code&gt;MediaStream&lt;/code&gt; has an input, which might be a &lt;code&gt;MediaStream&lt;/code&gt; generated by &lt;code&gt;getUserMedia()&lt;/code&gt;, and an output, which might be passed to a video element or an &lt;code&gt;RTCPeerConnection&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;getUserMedia()&lt;/code&gt; method takes a &lt;code&gt;MediaStreamConstraints&lt;/code&gt; object parameter and returns a &lt;code&gt;Promise&lt;/code&gt; that resolves to a &lt;code&gt;MediaStream&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;MediaStream&lt;/code&gt; has a &lt;code&gt;label&lt;/code&gt;, such as &lt;code&gt;&#39;Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ&#39;&lt;/code&gt;. An array of &lt;code&gt;MediaStreamTrack&lt;/code&gt;s is returned by the &lt;code&gt;getAudioTracks()&lt;/code&gt; and &lt;code&gt;getVideoTracks()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;For the &lt;a href=&quot;https://webrtc.github.io/samples/src/content/getusermedia/gum/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getUserMedia&lt;/code&gt;&lt;/a&gt; example, &lt;code&gt;stream.getAudioTracks()&lt;/code&gt; returns an empty array (because there&#39;s no audio) and, assuming a working webcam is connected, &lt;code&gt;stream.getVideoTracks()&lt;/code&gt; returns an array of one &lt;code&gt;MediaStreamTrack&lt;/code&gt; representing the stream from the webcam. Each &lt;code&gt;MediaStreamTrack&lt;/code&gt; has a kind (&lt;code&gt;&#39;video&#39;&lt;/code&gt; or &lt;code&gt;&#39;audio&#39;&lt;/code&gt;), a &lt;code&gt;label&lt;/code&gt; (something like &lt;code&gt;&#39;FaceTime HD Camera (Built-in)&#39;&lt;/code&gt;), and represents one or more channels of either audio or video. In this case, there is only one video track and no audio, but it is easy to imagine use cases where there are more, such as a chat app that gets streams from the front camera, rear camera, microphone, and an app sharing its screen.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;MediaStream&lt;/code&gt; can be attached to a video element by setting the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/srcObject&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;srcObject&lt;/code&gt; attribute&lt;/a&gt;. Previously, this was done by setting the  &lt;code&gt;src&lt;/code&gt; attribute to an object URL created with &lt;code&gt;URL.createObjectURL()&lt;/code&gt;, but &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/URL/createObjectURL&quot; rel=&quot;noopener&quot;&gt;this has been deprecated&lt;/a&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; The &lt;code&gt;MediaStreamTrack&lt;/code&gt; is actively using the camera, which takes resources, and keeps the camera open and camera light on. When you are no longer using a track, make sure to call &lt;code&gt;track.stop()&lt;/code&gt; so that the camera can be closed. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;code&gt;getUserMedia&lt;/code&gt; can also be used &lt;a href=&quot;https://developer.chrome.com/blog/live-web-audio-input-enabled/&quot; rel=&quot;noopener&quot;&gt;as an input node for the Web Audio API&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Cope with browser differences.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; audioContext&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;typeof&lt;/span&gt; AudioContext &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;function&#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;  audioContext &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;AudioContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; webkitAudioContext &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;function&#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;  audioContext &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;webkitAudioContext&lt;/span&gt;&lt;span class=&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;// eslint-disable-line new-cap&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;  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;Sorry! Web Audio not supported.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a filter node.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; filterNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createBiquadFilter&lt;/span&gt;&lt;span class=&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;// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section&lt;/span&gt;&lt;br /&gt;filterNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;highpass&#39;&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;// Cutoff frequency. For highpass, audio is attenuated below this frequency.&lt;/span&gt;&lt;br /&gt;filterNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frequency&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;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a gain node to change audio volume.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; gainNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createGain&lt;/span&gt;&lt;span class=&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;// Default is 1 (no change). Less than 1 means audio is attenuated&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// and vice versa.&lt;/span&gt;&lt;br /&gt;gainNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain&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.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&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;audio&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;span class=&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;stream&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 comment&quot;&gt;// Create an AudioNode from the stream.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mediaStreamSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;    audioContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaStreamSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stream&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  mediaStreamSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filterNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  filterNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gainNode&lt;span class=&quot;token punctuation&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;// Connect the gain node to the destination. For example, play the sound.&lt;/span&gt;&lt;br /&gt;  gainNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;audioContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination&lt;span class=&quot;token punctuation&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;Chromium-based apps and extensions can also incorporate &lt;code&gt;getUserMedia&lt;/code&gt;. Adding &lt;code&gt;audioCapture&lt;/code&gt; and/or &lt;code&gt;videoCapture&lt;/code&gt; &lt;a href=&quot;https://developer.chrome.com/extensions/permission_warnings&quot; rel=&quot;noopener&quot;&gt;permissions&lt;/a&gt; to the manifest enables permission to be requested and granted only once upon installation. Thereafter, the user is not asked for permission for camera or microphone access.&lt;/p&gt;
&lt;p&gt;Permission only has to be granted once for &lt;code&gt;getUserMedia()&lt;/code&gt;. First time around, an Allow button is displayed in the browser&#39;s &lt;a href=&quot;https://dev.chromium.org/user-experience/infobars&quot; rel=&quot;noopener&quot;&gt;infobar&lt;/a&gt;. HTTP access for &lt;code&gt;getUserMedia()&lt;/code&gt; was deprecated by Chrome at the end of 2015 due to it being classified as a &lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins&quot; rel=&quot;noopener&quot;&gt;Powerful feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The intention is potentially to enable a &lt;code&gt;MediaStream&lt;/code&gt; for any streaming data source, not only a camera or microphone. This would enable streaming from stored data or arbitrary data sources, such as sensors or other inputs.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getUserMedia()&lt;/code&gt; really comes to life in combination with other JavaScript APIs and libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webcamtoy.com/app/&quot; rel=&quot;noopener&quot;&gt;Webcam Toy&lt;/a&gt; is a photobooth app that uses WebGL to add weird and wonderful effects to photos that can be shared or saved locally.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.auduno.com/2012/06/15/head-tracking-with-webrtc/&quot; rel=&quot;noopener&quot;&gt;FaceKat&lt;/a&gt; is a face-tracking game built with &lt;a href=&quot;https://github.com/auduno/headtrackr&quot; rel=&quot;noopener&quot;&gt;headtrackr.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://idevelop.ro/ascii-camera/&quot; rel=&quot;noopener&quot;&gt;ASCII Camera&lt;/a&gt; uses the Canvas API to generate ASCII images.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;ASCII image generated by idevelop.ro/ascii-camera&quot; decoding=&quot;async&quot; height=&quot;661&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/B99U1vBIpQofujY67roL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;gUM ASCII art!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;constraints&quot;&gt;Constraints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#constraints&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/draft-alvestrand-constraints-resolution-00#page-4&quot; rel=&quot;noopener&quot;&gt;Constraints&lt;/a&gt; can be used to set values for video resolution for &lt;code&gt;getUserMedia()&lt;/code&gt;. This also allows &lt;a href=&quot;https://w3c.github.io/mediacapture-main/getusermedia.html#the-model-sources-sinks-constraints-and-settings&quot; rel=&quot;noopener&quot;&gt;support for other constraints&lt;/a&gt;, such as aspect ratio; facing mode (front or back camera); frame rate, height and width; and an &lt;a href=&quot;https://w3c.github.io/mediacapture-main/getusermedia.html#dom-mediastreamtrack-applyconstraints&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;applyConstraints()&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;
&lt;p&gt;For an example, see &lt;a href=&quot;https://webrtc.github.io/samples/src/content/getusermedia/resolution&quot; rel=&quot;noopener&quot;&gt;WebRTC samples &lt;code&gt;getUserMedia&lt;/code&gt;: select resolution&lt;/a&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; &lt;code&gt;getUserMedia&lt;/code&gt; constraints may affect the available configurations of a shared resource. For example, if a camera was opened in 640 x 480 mode by one tab, another tab will not be able to use constraints to open it in a higher-resolution mode because it can only be opened in one mode. Note that this is an implementation detail. It would be possible to let the second tab reopen the camera in a higher resolution mode and use video processing to downscale the video track to 640 x 480 for the first tab, but this has not been implemented. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Setting a disallowed constraint value gives a &lt;code&gt;DOMException&lt;/code&gt; or an &lt;code&gt;OverconstrainedError&lt;/code&gt; if, for example, a resolution requested is not available. To see this in action, see &lt;a href=&quot;https://webrtc.github.io/samples/src/content/getusermedia/resolution/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples &lt;code&gt;getUserMedia&lt;/code&gt;: select resolution&lt;/a&gt; for a demo.&lt;/p&gt;
&lt;h4 id=&quot;screen-and-tab-capture&quot;&gt;Screen and tab capture &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#screen-and-tab-capture&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Chrome apps also make it possible to share a live video of a single browser tab or the entire desktop through &lt;a href=&quot;https://developer.chrome.com/dev/extensions/tabCapture&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;chrome.tabCapture&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/extensions/desktopCapture&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;chrome.desktopCapture&lt;/code&gt;&lt;/a&gt; APIs. (For a demo and more information, see &lt;a href=&quot;https://developer.chrome.com/blog/screensharing-with-webrtc&quot; rel=&quot;noopener&quot;&gt;Screensharing with WebRTC&lt;/a&gt;. The article is a few years old, but it&#39;s still interesting.)&lt;/p&gt;
&lt;p&gt;It&#39;s also possible to use screen capture as a &lt;code&gt;MediaStream&lt;/code&gt; source in Chrome using the experimental &lt;code&gt;chromeMediaSource&lt;/code&gt; constraint. Note that screen capture requires HTTPS and should only be used for development due to it being enabled through a command-line flag as explained in this &lt;a href=&quot;https://groups.google.com/forum/#!msg/discuss-webrtc/TPQVKZnsF5g/Hlpy8kqaLnEJ&quot; rel=&quot;noopener&quot;&gt;post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;signaling-session-control,-network,-and-media-information&quot;&gt;Signaling: Session control, network, and media information &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#signaling-session-control,-network,-and-media-information&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebRTC uses &lt;code&gt;RTCPeerConnection&lt;/code&gt; to communicate streaming data between browsers (also known as peers), but also needs a mechanism to coordinate communication and to send control messages, a process known as signaling. Signaling methods and protocols are &lt;strong&gt;not&lt;/strong&gt; specified by WebRTC. Signaling is not part of the &lt;code&gt;RTCPeerConnection&lt;/code&gt; API.&lt;/p&gt;
&lt;p&gt;Instead, WebRTC app developers can choose whatever messaging protocol they prefer, such as SIP or XMPP, and any appropriate duplex (two-way) communication channel. The &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt; example uses XHR and the Channel API as the signaling mechanism. The &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/#0&quot; rel=&quot;noopener&quot;&gt;codelab&lt;/a&gt; uses &lt;a href=&quot;https://socket.io/&quot; rel=&quot;noopener&quot;&gt;Socket.io&lt;/a&gt; running on a &lt;a href=&quot;https://nodejs.org/&quot; rel=&quot;noopener&quot;&gt;Node server&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Signaling is used to exchange three types of information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Session control messages: to initialize or close communication and report errors.&lt;/li&gt;
&lt;li&gt;Network configuration: to the outside world, what&#39;s your computer&#39;s IP address and port?&lt;/li&gt;
&lt;li&gt;Media capabilities: what codecs and resolutions can be handled by your browser and the browser it wants to communicate with?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The exchange of information through signaling must have completed successfully before peer-to-peer streaming can begin.&lt;/p&gt;
&lt;p&gt;For example, imagine Alice wants to communicate with Bob. Here&#39;s a code sample from the &lt;a href=&quot;https://w3c.github.io/webrtc-pc/#simple-peer-to-peer-example&quot; rel=&quot;noopener&quot;&gt;W3C WebRTC spec&lt;/a&gt;, which shows the signaling process in action. The code assumes the existence of some signaling mechanism created in the &lt;code&gt;createSignalingChannel()&lt;/code&gt; method. Also note that on Chrome and Opera, &lt;code&gt;RTCPeerConnection&lt;/code&gt; is currently prefixed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// handles JSON.stringify/parse&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; signaling &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;SignalingChannel&lt;/span&gt;&lt;span class=&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; constraints &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 literal-property property&quot;&gt;audio&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;span class=&quot;token literal-property property&quot;&gt;video&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;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; configuration &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 literal-property property&quot;&gt;iceServers&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 literal-property property&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;stun:stun.example.org&#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 keyword&quot;&gt;const&lt;/span&gt; pc &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;configuration&lt;span 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;// Send any ice candidates to the other peer.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onicecandidate&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 parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;candidate&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 operator&quot;&gt;=&gt;&lt;/span&gt; signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;candidate&lt;span class=&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;// Let the &quot;negotiationneeded&quot; event trigger offer generation.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onnegotiationneeded&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 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;try&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocalDescription&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createOffer&lt;/span&gt;&lt;span class=&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;// Send the offer to the other peer.&lt;/span&gt;&lt;br /&gt;    signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&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;desc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localDescription&lt;span class=&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Once remote track media arrives, show it in remote video element.&lt;/span&gt;&lt;br /&gt;pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;ontrack&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 parameter&quot;&gt;event&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 comment&quot;&gt;// Don&#39;t set srcObject again if it is already set.&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;remoteView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&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;  remoteView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;streams&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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Call start() to initiate.&lt;/span&gt;&lt;br /&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;start&lt;/span&gt;&lt;span class=&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;try&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 local stream, show it in self-view, and add it to be sent.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracks&lt;/span&gt;&lt;span class=&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;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;track&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;br /&gt;      pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;track&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stream&lt;span class=&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;    selfView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stream&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;desc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; candidate&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 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;try&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;desc&lt;span class=&quot;token punctuation&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;// If you get an offer, you need to reply with an answer.&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;desc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;offer&#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;await&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRemoteDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&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; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracks&lt;/span&gt;&lt;span class=&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;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;track&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;br /&gt;          pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;track&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stream&lt;span class=&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocalDescription&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAnswer&lt;/span&gt;&lt;span class=&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;        signaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&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;desc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localDescription&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;answer&#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;await&lt;/span&gt; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRemoteDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&quot;token punctuation&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;        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;Unsupported SDP type.&#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;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;candidate&lt;span class=&quot;token punctuation&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; pc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addIceCandidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;candidate&lt;span class=&quot;token punctuation&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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;err&lt;span class=&quot;token punctuation&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;First, Alice and Bob exchange network information. (The expression &lt;strong&gt;finding candidates&lt;/strong&gt; refers to the process of finding network interfaces and ports using the ICE framework.)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice creates an &lt;code&gt;RTCPeerConnection&lt;/code&gt; object with an &lt;code&gt;onicecandidate&lt;/code&gt; handler, which runs when network candidates become available.&lt;/li&gt;
&lt;li&gt;Alice sends serialized candidate data to Bob through whatever signaling channel they are using, such as WebSocket or some other mechanism.&lt;/li&gt;
&lt;li&gt;When Bob gets a candidate message from Alice, he calls &lt;code&gt;addIceCandidate&lt;/code&gt; to add the candidate to the remote peer description.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;WebRTC clients (also known as &lt;strong&gt;peers&lt;/strong&gt;, or Alice and Bob in this example) also need to ascertain and exchange local and remote audio and video media information, such as resolution and codec capabilities. Signaling to exchange media configuration information proceeds by exchanging an &lt;strong&gt;offer&lt;/strong&gt; and an &lt;strong&gt;answer&lt;/strong&gt; using the Session Description Protocol (SDP):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice runs the &lt;code&gt;RTCPeerConnection&lt;/code&gt; &lt;code&gt;createOffer()&lt;/code&gt; method. The return from this is passed an &lt;code&gt;RTCSessionDescription&lt;/code&gt; - Alice&#39;s local session description.&lt;/li&gt;
&lt;li&gt;In the callback, Alice sets the local description using &lt;code&gt;setLocalDescription()&lt;/code&gt; and then sends this session description to Bob through their signaling channel. Note that &lt;code&gt;RTCPeerConnection&lt;/code&gt; won&#39;t start gathering candidates until &lt;code&gt;setLocalDescription()&lt;/code&gt; is called. This is codified in the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-03#section-4.2.4&quot; rel=&quot;noopener&quot;&gt;JSEP IETF draft&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Bob sets the description Alice sent him as the remote description using &lt;code&gt;setRemoteDescription()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Bob runs the &lt;code&gt;RTCPeerConnection&lt;/code&gt; &lt;code&gt;createAnswer()&lt;/code&gt; method, passing it the remote description he got from Alice so a local session can be generated that is compatible with hers. The &lt;code&gt;createAnswer()&lt;/code&gt; callback is passed an &lt;code&gt;RTCSessionDescription&lt;/code&gt;. Bob sets that as the local description and sends it to Alice.&lt;/li&gt;
&lt;li&gt;When Alice gets Bob&#39;s session description, she sets that as the remote description with &lt;code&gt;setRemoteDescription&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ping!&lt;/li&gt;
&lt;/ol&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 to allow the &lt;code&gt;RTCPeerConnection&lt;/code&gt; to be garbage collected by calling &lt;code&gt;close()&lt;/code&gt; when it&#39;s no longer needed. Otherwise, threads and connections are kept alive. It&#39;s possible to leak heavy resources in WebRTC! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;code&gt;RTCSessionDescription&lt;/code&gt; objects are blobs that conform to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Session_Description_Protocol&quot; rel=&quot;noopener&quot;&gt;Session Description Protocol&lt;/a&gt;, SDP. Serialized, an SDP object looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;v&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;o&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;3883943731&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IP4&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;&lt;br /&gt;s&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;t&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 number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;a&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;group&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BUNDLE&lt;/span&gt; audio video&lt;br /&gt;m&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RTP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SAVPF&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;103&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;104&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;106&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;105&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;126&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;a&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ssrc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2223794119&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The acquisition and exchange of network and media information can be done simultaneously, but both processes must have completed before audio and video streaming between peers can begin.&lt;/p&gt;
&lt;p&gt;The offer/answer architecture previously described is called &lt;a href=&quot;https://rtcweb-wg.github.io/jsep/&quot; rel=&quot;noopener&quot;&gt;JavaScript Session Establishment Protocol&lt;/a&gt;, or JSEP. (There&#39;s an excellent animation explaining the process of signaling and streaming in &lt;a href=&quot;https://www.ericsson.com/research-blog/context-aware-communication/beyond-html5-peer-peer-conversational-video/&quot; rel=&quot;noopener&quot;&gt;Ericsson&#39;s demo video&lt;/a&gt; for its first WebRTC implementation.)&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;JSEP architecture diagram&quot; decoding=&quot;async&quot; height=&quot;499&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/n9vVzOGhttG2TljRbdxN.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;JSEP architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once the signaling process has completed successfully, data can be streamed directly peer to peer, between the caller and callee - or, if that fails, through an intermediary relay server (more about that later). Streaming is the job of &lt;code&gt;RTCPeerConnection&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;rtcpeerconnection&quot;&gt;RTCPeerConnection &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcpeerconnection&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; is the WebRTC component that handles stable and efficient communication of streaming data between peers.&lt;/p&gt;
&lt;p&gt;The following is a WebRTC architecture diagram showing the role of &lt;code&gt;RTCPeerConnection&lt;/code&gt;. As you will notice, the green parts are complex!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;WebRTC architecture diagram&quot; decoding=&quot;async&quot; height=&quot;482&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 740px) 740px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/q28219742nq0IXphvneQ.png?auto=format&amp;w=1480 1480w&quot; width=&quot;740&quot; /&gt;
&lt;figcaption&gt;WebRTC architecture (from &lt;a href=&quot;https://webrtc.github.io/webrtc-org/architecture/&quot; title=&quot;webrtc.org: architecture diagram&quot;&gt;webrtc.org&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;From a JavaScript perspective, the main thing to understand from this diagram is that &lt;code&gt;RTCPeerConnection&lt;/code&gt; shields web developers from the myriad complexities that lurk beneath. The codecs and protocols used by WebRTC do a huge amount of work to make real-time communication possible, even over unreliable networks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Packet-loss concealment&lt;/li&gt;
&lt;li&gt;Echo cancellation&lt;/li&gt;
&lt;li&gt;Bandwidth adaptivity&lt;/li&gt;
&lt;li&gt;Dynamic jitter buffering&lt;/li&gt;
&lt;li&gt;Automatic gain control&lt;/li&gt;
&lt;li&gt;Noise reduction and suppression&lt;/li&gt;
&lt;li&gt;Image-cleaning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The previous W3C code shows a simplified example of WebRTC from a signaling perspective. The following are walkthroughs of two working WebRTC apps. The first is a simple example to demonstrate &lt;code&gt;RTCPeerConnection&lt;/code&gt; and the second is a fully operational video chat client.&lt;/p&gt;
&lt;h3 id=&quot;rtcpeerconnection-without-servers&quot;&gt;RTCPeerConnection without servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcpeerconnection-without-servers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following code is taken from &lt;a href=&quot;https://webrtc.github.io/samples/src/content/peerconnection/pc1/&quot; rel=&quot;noopener&quot;&gt;WebRTC samples Peer connection&lt;/a&gt;, which has local &lt;strong&gt;and&lt;/strong&gt; remote &lt;code&gt;RTCPeerConnection&lt;/code&gt; (and local and remote video) on one web page. This doesn&#39;t constitute anything very useful - caller and callee are on the same page - but it does make the workings of the &lt;code&gt;RTCPeerConnection&lt;/code&gt; API a little clearer because the &lt;code&gt;RTCPeerConnection&lt;/code&gt; objects on the page can exchange data and messages directly without having to use intermediary signaling mechanisms.&lt;/p&gt;
&lt;p&gt;In this example, &lt;code&gt;pc1&lt;/code&gt; represents the local peer (caller) and &lt;code&gt;pc2&lt;/code&gt; represents the remote peer (callee).&lt;/p&gt;
&lt;h3 id=&quot;caller&quot;&gt;Caller &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#caller&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new &lt;code&gt;RTCPeerConnection&lt;/code&gt; and add the stream from &lt;code&gt;getUserMedia()&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Servers is an optional configuration file. (See TURN and STUN discussion later.)&lt;/span&gt;&lt;br /&gt;pc1 &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servers&lt;span class=&quot;token punctuation&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;localStream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracks&lt;/span&gt;&lt;span class=&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;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;track&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;pc1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;track&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; localStream&lt;span class=&quot;token punctuation&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an offer and set it as the local description for &lt;code&gt;pc1&lt;/code&gt; and as the remote description for &lt;code&gt;pc2&lt;/code&gt;. This can be done directly in the code without using signaling because both caller and callee are on the same page:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;pc1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocalDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&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 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;onSetLocalSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pc1&lt;span class=&quot;token punctuation&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;    onSetSessionDescriptionError&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 function&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pc2 setRemoteDescription start&#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;pc2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRemoteDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desc&lt;span class=&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 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;onSetRemoteSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pc2&lt;span class=&quot;token punctuation&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;    onSetSessionDescriptionError&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;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;callee&quot;&gt;Callee &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#callee&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;pc2&lt;/code&gt; and, when the stream from &lt;code&gt;pc1&lt;/code&gt; is added, display it in a video element:&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;pc2 &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;pc2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ontrack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gotRemoteStream&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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;gotRemoteStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;vid2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stream&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;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;rtcpeerconnection-api-plus-servers&quot;&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; API plus servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcpeerconnection-api-plus-servers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the real world, WebRTC needs servers, however simple, so the following can happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users discover each other and exchange real-world details, such as names.&lt;/li&gt;
&lt;li&gt;WebRTC client apps (peers) exchange network information.&lt;/li&gt;
&lt;li&gt;Peers exchange data about media, such as video format and resolution.&lt;/li&gt;
&lt;li&gt;WebRTC client apps traverse &lt;a href=&quot;https://en.wikipedia.org/wiki/NAT_traversal&quot; rel=&quot;noopener&quot;&gt;NAT gateways&lt;/a&gt; and firewalls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, WebRTC needs four types of server-side functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User discovery and communication&lt;/li&gt;
&lt;li&gt;Signaling&lt;/li&gt;
&lt;li&gt;NAT/firewall traversal&lt;/li&gt;
&lt;li&gt;Relay servers in case peer-to-peer communication fails&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NAT traversal, peer-to-peer networking, and the requirements for building a server app for user discovery and signaling are beyond the scope of this article. Suffice to say that the &lt;a href=&quot;https://en.wikipedia.org/wiki/STUN&quot; rel=&quot;noopener&quot;&gt;STUN&lt;/a&gt; protocol and its extension, &lt;a href=&quot;https://en.wikipedia.org/wiki/Traversal_Using_Relay_NAT&quot; rel=&quot;noopener&quot;&gt;TURN&lt;/a&gt;, are used by the &lt;a href=&quot;https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment&quot; rel=&quot;noopener&quot;&gt;ICE&lt;/a&gt; framework to enable &lt;code&gt;RTCPeerConnection&lt;/code&gt; to cope with NAT traversal and other network vagaries.&lt;/p&gt;
&lt;p&gt;ICE is a framework for connecting peers, such as two video chat clients. Initially, ICE tries to connect peers &lt;strong&gt;directly&lt;/strong&gt; with the lowest possible latency through UDP. In this process, STUN servers have a single task: to enable a peer behind a NAT to find out its public address and port. (For more information about STUN and TURN, see &lt;a href=&quot;https://www.html5rocks.com/tutorials/webrtc/infrastructure/&quot; rel=&quot;noopener&quot;&gt;Build the backend services needed for a WebRTC app&lt;/a&gt;.)&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Finding connection candidates&quot; decoding=&quot;async&quot; height=&quot;499&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/9ECRhjzepzfBJ9FdsKpX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Finding connection candidates&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If UDP fails, ICE tries TCP. If direct connection fails - in particular because of enterprise NAT traversal and firewalls - ICE uses an intermediary (relay) TURN server. In other words, ICE first uses STUN with UDP to directly connect peers and, if that fails, falls back to a TURN relay server. The expression &lt;strong&gt;finding candidates&lt;/strong&gt; refers to the process of finding network interfaces and ports.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;WebRTC data pathways&quot; decoding=&quot;async&quot; height=&quot;499&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1RuU3YjeG2QvudekRJcG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;WebRTC data pathways&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;WebRTC engineer Justin Uberti provides more information about ICE, STUN, and TURN in the &lt;a href=&quot;https://www.youtube.com/watch?v=p2HzZkd2A40&amp;amp;t=21m12s&quot; rel=&quot;noopener&quot;&gt;2013 Google I/O WebRTC presentation&lt;/a&gt;. (The presentation &lt;a href=&quot;https://io13webrtc.appspot.com/#52&quot; rel=&quot;noopener&quot;&gt;slides&lt;/a&gt; give examples of TURN and STUN server implementations.)&lt;/p&gt;
&lt;h4 id=&quot;a-simple-video-chat-client&quot;&gt;A simple video-chat client &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#a-simple-video-chat-client&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A good place to try WebRTC, complete with signaling and NAT/firewall traversal using a STUN server, is the video-chat demo at &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt;. This app uses &lt;a href=&quot;https://github.com/webrtc/adapter&quot; rel=&quot;noopener&quot;&gt;adapter.js&lt;/a&gt;, a shim to insulate apps from spec changes and prefix differences.&lt;/p&gt;
&lt;p&gt;The code is deliberately verbose in its logging. Check the console to understand the order of events. The following is a detailed walkthrough of the code.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you find this somewhat baffling, you may prefer the &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/&quot;&gt;WebRTC codelab&lt;/a&gt;. This step-by-step guide explains how to build a complete video-chat app, including a simple signaling server running on a &lt;a href=&quot;https://nodejs.org/&quot;&gt;Node server&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;network-topologies&quot;&gt;Network topologies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#network-topologies&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;WebRTC, as currently implemented, only supports one-to-one communication, but could be used in more complex network scenarios, such as with multiple peers each communicating with each other directly or through a &lt;a href=&quot;https://en.wikipedia.org/wiki/Multipoint_control_unit&quot; rel=&quot;noopener&quot;&gt;Multipoint Control Unit&lt;/a&gt; (MCU), a server that can handle large numbers of participants and do selective stream forwarding, and mixing or recording of audio and video.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Multipoint Control Unit topology diagram&quot; decoding=&quot;async&quot; height=&quot;585&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Bhswo1WPoUT0QK9qKPmo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Multipoint Control Unit topology example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Many existing WebRTC apps only demonstrate communication between web browsers, but gateway servers can enable a WebRTC app running on a browser to interact with devices, such as  &lt;a href=&quot;https://en.wikipedia.org/wiki/Public_switched_telephone_network&quot; rel=&quot;noopener&quot;&gt;telephones&lt;/a&gt; (also known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Public_switched_telephone_network&quot; rel=&quot;noopener&quot;&gt;PSTN&lt;/a&gt;) and with &lt;a href=&quot;https://en.wikipedia.org/wiki/Voice_over_IP&quot; rel=&quot;noopener&quot;&gt;VOIP&lt;/a&gt; systems. In May 2012, Doubango Telecom open sourced the &lt;a href=&quot;https://sipml5.org/&quot; rel=&quot;noopener&quot;&gt;sipml5 SIP client&lt;/a&gt; built with WebRTC and WebSocket, which (among other potential uses) enables video calls between browsers and apps running on iOS and Android. At Google I/O, Tethr and Tropo demonstrated &lt;a href=&quot;https://tethr.tumblr.com/&quot; rel=&quot;noopener&quot;&gt;a framework for disaster communications&lt;/a&gt; &lt;strong&gt;in a briefcase&lt;/strong&gt; using an &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenBTS&quot; rel=&quot;noopener&quot;&gt;OpenBTS cell&lt;/a&gt; to enable communications between feature phones and computers through WebRTC. Telephone communication without a carrier!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Tethr/Tropo demo at Google I/O 2012&quot; decoding=&quot;async&quot; height=&quot;598&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oc1xnYE727oqMPYfWnt9.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Tethr/Tropo: Disaster communications in a briefcase&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;rtcdatachannel-apiless&quot;&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; API&amp;lt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcdatachannel-apiless&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As well as audio and video, WebRTC supports real-time communication for other types of data.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;RTCDataChannel&lt;/code&gt; API enables peer-to-peer exchange of arbitrary data with low latency and high throughput. For single-page demos and to learn how to build a simple file-transfer app, see &lt;a href=&quot;https://webrtc.github.io/samples/#datachannel&quot; rel=&quot;noopener&quot;&gt;WebRTC samples&lt;/a&gt; and the &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/webrtc-web/#0&quot; rel=&quot;noopener&quot;&gt;WebRTC codelab&lt;/a&gt;, respectively.&lt;/p&gt;
&lt;p&gt;There are many potential use cases for the API, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gaming&lt;/li&gt;
&lt;li&gt;Remote desktop apps&lt;/li&gt;
&lt;li&gt;Real-time text chat&lt;/li&gt;
&lt;li&gt;File transfer&lt;/li&gt;
&lt;li&gt;Decentralized networks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The API has several features to make the most of &lt;code&gt;RTCPeerConnection&lt;/code&gt; and enable powerful and flexible peer-to-peer communication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leveraging of &lt;code&gt;RTCPeerConnection&lt;/code&gt; session setup&lt;/li&gt;
&lt;li&gt;Multiple simultaneous channels with prioritization&lt;/li&gt;
&lt;li&gt;Reliable and unreliable delivery semantics&lt;/li&gt;
&lt;li&gt;Built-in security (DTLS) and congestion control&lt;/li&gt;
&lt;li&gt;Ability to use with or without audio or video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The syntax is deliberately similar to WebSocket with a &lt;code&gt;send()&lt;/code&gt; method and a &lt;code&gt;message&lt;/code&gt; event:&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; localConnection &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servers&lt;span class=&quot;token punctuation&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; remoteConnection &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;RTCPeerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;servers&lt;span class=&quot;token punctuation&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; sendChannel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;  localConnection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createDataChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sendDataChannel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;remoteConnection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;ondatachannel&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 parameter&quot;&gt;event&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;  receiveChannel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;channel&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  receiveChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onmessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onReceiveMessage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  receiveChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onopen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onReceiveChannelStateChange&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  receiveChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onclose &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onReceiveChannelStateChange&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onReceiveMessage&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;  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;&quot;textarea#send&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;value &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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;button#send&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-variable function&quot;&gt;onclick&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 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;var&lt;/span&gt; data &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;&quot;textarea#send&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;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  sendChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&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;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;Communication occurs directly between browsers, so &lt;code&gt;RTCDataChannel&lt;/code&gt; can be much faster than WebSocket even if a relay (TURN) server is required when hole-punching to cope with firewalls and NATs fails.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; is available in Chrome, Safari, Firefox, Opera, and Samsung Internet. The &lt;a href=&quot;https://experiments.withgoogle.com/cube-slam&quot; rel=&quot;noopener&quot;&gt;Cube Slam&lt;/a&gt; game uses the API to communicate game state. Play a friend or play the bear! The innovative platform &lt;a href=&quot;https://github.com/Peer5/ShareFest&quot; rel=&quot;noopener&quot;&gt;Sharefest&lt;/a&gt; enabled file sharing through &lt;code&gt;RTCDataChannel&lt;/code&gt; and &lt;a href=&quot;https://techcrunch.com/2013/12/17/yahoo-acquires-peercdn/&quot; rel=&quot;noopener&quot;&gt;peerCDN&lt;/a&gt; offered a glimpse of how WebRTC could enable peer-to-peer content distribution.&lt;/p&gt;
&lt;p&gt;For more information about &lt;code&gt;RTCDataChannel&lt;/code&gt;, take a look at the IETF&#39;s &lt;a href=&quot;https://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-00&quot; rel=&quot;noopener&quot;&gt;draft protocol spec&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#security&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are several ways a real-time communication app or plugin might compromise security. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unencrypted media or data might be intercepted between browsers, or between a browser and a server.&lt;/li&gt;
&lt;li&gt;An app might record and distribute video or audio without the user knowing.&lt;/li&gt;
&lt;li&gt;Malware or viruses might be installed alongside an apparently innocuous plugin or app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WebRTC has several features to avoid these problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebRTC implementations use secure protocols, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Datagram_Transport_Layer_Security&quot; rel=&quot;noopener&quot;&gt;DTLS&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_Real-time_Transport_Protocol&quot; rel=&quot;noopener&quot;&gt;SRTP&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Encryption is mandatory for all WebRTC components, including signaling mechanisms.&lt;/li&gt;
&lt;li&gt;WebRTC is not a plugin. Its components run in the browser sandbox and not in a separate process. Components do not require separate installation and are updated whenever the browser is updated.&lt;/li&gt;
&lt;li&gt;Camera and microphone access must be granted explicitly and, when the camera or microphone are running, this is clearly shown by the user interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A full discussion of security for streaming media is out of scope for this article. For more information, see the &lt;a href=&quot;https://www.ietf.org/proceedings/82/slides/rtcweb-13.pdf&quot; rel=&quot;noopener&quot;&gt;Proposed WebRTC Security Architecture&lt;/a&gt; proposed by the IETF.&lt;/p&gt;
&lt;h2 id=&quot;in-conclusion&quot;&gt;In conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#in-conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The APIs and standards of WebRTC can democratize and decentralize tools for content creation and communication, including telephony, gaming, video production, music making, and news gathering.&lt;/p&gt;
&lt;p&gt;Technology doesn&#39;t get much more &lt;a href=&quot;https://en.wikipedia.org/wiki/Disruptive_innovation&quot; rel=&quot;noopener&quot;&gt;disruptive&lt;/a&gt; than this.&lt;/p&gt;
&lt;p&gt;As blogger Phil Edholm &lt;a href=&quot;https://www.nojitter.com/webrtc-it-game-changer&quot; rel=&quot;noopener&quot;&gt;put it&lt;/a&gt;, &amp;quot;Potentially, WebRTC and HTML5 could enable the same transformation for real-time communication that the original browser did for information.&amp;quot;&lt;/p&gt;
&lt;h2 id=&quot;developer-tools&quot;&gt;Developer tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#developer-tools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;WebRTC stats for an ongoing session can be found at:
&lt;ul&gt;
&lt;li&gt;about://webrtc-internals in Chrome&lt;/li&gt;
&lt;li&gt;opera://webrtc-internals in Opera&lt;/li&gt;
&lt;li&gt;about:webrtc in Firefox  &lt;figure&gt;
    &lt;img alt=&quot;chrome://webrtc-internals page&quot; decoding=&quot;async&quot; height=&quot;538&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/URO18HhhtphzDB2n8qu8.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;chrome://webrtc-internals screenshot&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cross browser &lt;a href=&quot;https://webrtc.github.io/webrtc-org/web-apis/interop/&quot; rel=&quot;noopener&quot;&gt;interop notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webrtc/adapter&quot; rel=&quot;noopener&quot;&gt;adapter.js&lt;/a&gt; is a JavaScript shim for WebRTC maintained by Google with help from the &lt;a href=&quot;https://github.com/webrtc/adapter/graphs/contributors&quot; rel=&quot;noopener&quot;&gt;WebRTC community&lt;/a&gt; that abstracts vendor prefixes, browser differences, and spec changes.&lt;/li&gt;
&lt;li&gt;To learn more about WebRTC signaling processes, check the &lt;a href=&quot;https://appr.tc/&quot; rel=&quot;noopener&quot;&gt;appr.tc&lt;/a&gt; log output to the console.&lt;/li&gt;
&lt;li&gt;If it&#39;s all too much, you may prefer to use a &lt;a href=&quot;https://io13webrtc.appspot.com/#69&quot; rel=&quot;noopener&quot;&gt;WebRTC framework&lt;/a&gt; or even a complete &lt;a href=&quot;https://io13webrtc.appspot.com/#72&quot; rel=&quot;noopener&quot;&gt;WebRTC service&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Bug reports and feature requests are always appreciated:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.google.com/p/webrtc/issues/entry&quot; rel=&quot;noopener&quot;&gt;WebRTC bugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.crbug.com/new&quot; rel=&quot;noopener&quot;&gt;Chrome bugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bugs.opera.com/wizard/&quot; rel=&quot;noopener&quot;&gt;Opera bugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bugzilla.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;Firefox bugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webrtc/samples/issues/new&quot; rel=&quot;noopener&quot;&gt;WebRTC demo bugs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webrtcHacks/adapter/issues/new&quot; rel=&quot;noopener&quot;&gt;Adapter.js bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;learn-more&quot;&gt;Learn more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#learn-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=E8C8ouiXHHk&quot; rel=&quot;noopener&quot;&gt;Justin Uberti&#39;s WebRTC session at Google I/O 2012&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Alan B. Johnston and Daniel C. Burnett maintain a WebRTC book now in its third edition in print and eBook formats at &lt;a href=&quot;https://www.webrtcbook.com/&quot; rel=&quot;noopener&quot;&gt;webrtcbook.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.webrtc.org/&quot; rel=&quot;noopener&quot;&gt;webrtc.org&lt;/a&gt; is home to all things WebRTC, including demos, documentation, and discussion.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://groups.google.com/forum/?fromgroups#!forum/discuss-webrtc&quot; rel=&quot;noopener&quot;&gt;discuss-webrtc&lt;/a&gt; is a Google Group for technical WebRTC discussion.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/webrtc&quot; rel=&quot;noopener&quot;&gt;@webrtc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Google Developers &lt;a href=&quot;https://developers.google.com/talk/talk_developers_home&quot; rel=&quot;noopener&quot;&gt;Talk documentation&lt;/a&gt; provides more information about NAT traversal, STUN, relay servers, and candidate gathering.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webrtc&quot; rel=&quot;noopener&quot;&gt;WebRTC on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/tagged/webrtc&quot; rel=&quot;noopener&quot;&gt;Stack Overflow&lt;/a&gt; is a good place to look for answers and ask questions about WebRTC.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;standards-and-protocols&quot;&gt;Standards and protocols &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#standards-and-protocols&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.w3.org/2011/webrtc/editor/webrtc.html&quot; rel=&quot;noopener&quot;&gt;The WebRTC W3C Editor&#39;s Draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.w3.org/2011/webrtc/editor/getusermedia.html&quot; rel=&quot;noopener&quot;&gt;W3C Editor&#39;s Draft: Media Capture and Streams&lt;/a&gt; (also known as &lt;code&gt;getUserMedia&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/wg/rtcweb/charters&quot; rel=&quot;noopener&quot;&gt;IETF Working Group Charter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-01&quot; rel=&quot;noopener&quot;&gt;IETF WebRTC Data Channel Protocol Draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/draft-uberti-rtcweb-jsep-02&quot; rel=&quot;noopener&quot;&gt;IETF JSEP Draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc5245&quot; rel=&quot;noopener&quot;&gt;IETF proposed standard for ICE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IETF RTCWEB Working Group Internet-Draft: &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-rtcweb-use-cases-and-requirements-10&quot; rel=&quot;noopener&quot;&gt;Web Real-Time Communication Use-cases and Requirements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;webrtc-support-summary&quot;&gt;WebRTC support summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#webrtc-support-summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;mediastream-and-getusermedia-apis&quot;&gt;&lt;code&gt;MediaStream&lt;/code&gt; and &lt;code&gt;getUserMedia&lt;/code&gt; APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#mediastream-and-getusermedia-apis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Chrome desktop 18.0.1008 and higher; Chrome for Android 29 and higher&lt;/li&gt;
&lt;li&gt;Opera 18 and higher; Opera for Android 20 and higher&lt;/li&gt;
&lt;li&gt;Opera 12, Opera Mobile 12 (based on the Presto engine)&lt;/li&gt;
&lt;li&gt;Firefox 17 and higher&lt;/li&gt;
&lt;li&gt;Microsoft Edge 16 and higher&lt;/li&gt;
&lt;li&gt;Safari 11.2 and higher on iOS, and 11.1 and higher on MacOS&lt;/li&gt;
&lt;li&gt;UC 11.8 and higher on Android&lt;/li&gt;
&lt;li&gt;Samsung Internet 4 and higher&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rtcpeerconnection-api&quot;&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcpeerconnection-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Chrome desktop 20 and higher; Chrome for Android 29 and higher (flagless)&lt;/li&gt;
&lt;li&gt;Opera 18 and higher (on by default); Opera for Android 20 and higher (on by default)&lt;/li&gt;
&lt;li&gt;Firefox 22 and higher (on by default)&lt;/li&gt;
&lt;li&gt;Microsoft Edge 16 and higher&lt;/li&gt;
&lt;li&gt;Safari 11.2 and higher on iOS, and 11.1 and higher on MacOS&lt;/li&gt;
&lt;li&gt;Samsung Internet 4 and higher&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rtcdatachannel-api&quot;&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt; API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webrtc-basics/#rtcdatachannel-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Experimental version in Chrome 25, but more stable (and with Firefox interoperability) in Chrome 26 and higher; Chrome for Android 29 and higher&lt;/li&gt;
&lt;li&gt;Stable version (and with Firefox interoperability) in Opera 18 and higher; Opera for Android 20 and higher&lt;/li&gt;
&lt;li&gt;Firefox 22 and higher (on by default)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more detailed information about cross-platform support for APIs, such as &lt;code&gt;getUserMedia&lt;/code&gt; and &lt;code&gt;RTCPeerConnection&lt;/code&gt;, see &lt;a href=&quot;https://caniuse.com/&quot; rel=&quot;noopener&quot;&gt;caniuse.com&lt;/a&gt; and &lt;a href=&quot;https://chromestatus.com/&quot; rel=&quot;noopener&quot;&gt;Chrome Platform Status&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Native APIs for &lt;code&gt;RTCPeerConnection&lt;/code&gt; are also available at &lt;a href=&quot;https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/index.md&quot; rel=&quot;noopener&quot;&gt;documentation on webrtc.org&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author>
  </entry>
  
  <entry>
    <title>Capture audio and video in HTML5</title>
    <link href="https://web.dev/getusermedia-intro/"/>
    <updated>2012-02-22T00:00:00Z</updated>
    <id>https://web.dev/getusermedia-intro/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Audio/Video capture has been &lt;em&gt;the&lt;/em&gt; &amp;quot;Holy Grail&amp;quot; of web development for a long time.
For many years we&#39;ve had to rely on browser plugins (&lt;a href=&quot;http://www.kevinmusselman.com/2009/02/access-webcam-with-flash/&quot; rel=&quot;noopener&quot;&gt;Flash&lt;/a&gt; or
&lt;a href=&quot;http://www.silverlightshow.net/items/Capturing-the-Webcam-in-Silverlight-4.aspx&quot; rel=&quot;noopener&quot;&gt;Silverlight&lt;/a&gt;)
to get the job done. &lt;a href=&quot;https://www.youtube.com/watch?v=SP_9zH9Q44o&quot; rel=&quot;noopener&quot;&gt;Come on!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;HTML5 to the rescue. It might not be apparent, but the rise of HTML5 has brought
a surge of access to device hardware. &lt;a href=&quot;https://www.html5rocks.com/entutorials/geolocation/trip_meter/&quot; rel=&quot;noopener&quot;&gt;Geolocation&lt;/a&gt; (GPS),
the &lt;a href=&quot;https://www.html5rocks.com/entutorials/device/orientation/&quot; rel=&quot;noopener&quot;&gt;Orientation API&lt;/a&gt; (accelerometer), &lt;a href=&quot;https://www.html5rocks.com/entutorials/webgl/shaders/&quot; rel=&quot;noopener&quot;&gt;WebGL&lt;/a&gt; (GPU),
and the &lt;a href=&quot;https://www.html5rocks.com/entutorials/webaudio/intro/&quot; rel=&quot;noopener&quot;&gt;Web Audio API&lt;/a&gt; (audio hardware) are perfect examples. These features
are ridiculously powerful, exposing high level JavaScript APIs that sit
on top of the system&#39;s underlying hardware capabilities.&lt;/p&gt;
&lt;p&gt;This tutorial introduces a new API, [&lt;code&gt;navigator.getUserMedia()&lt;/code&gt;][getusermedia-spec], which allows
web apps to access a user&#39;s camera and microphone.&lt;/p&gt;
&lt;h2 id=&quot;the-road-to-getusermedia&quot;&gt;The road to getUserMedia() &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#the-road-to-getusermedia&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;re not aware of its history, the way we arrived at the &lt;code&gt;getUserMedia()&lt;/code&gt; API is an interesting tale.&lt;/p&gt;
&lt;p&gt;Several variants of &amp;quot;Media Capture APIs&amp;quot; have evolved over the past few years.
Many folks recognized the need to be able to access native devices on the web, but
that led everyone and their mom to put together a new spec. Things got
so messy that the W3C finally decided to form a working group. Their sole purpose?
Make sense of the madness! The &lt;a href=&quot;http://www.w3.org/2009/dap/&quot; rel=&quot;noopener&quot;&gt;Device APIs Policy (DAP) Working Group&lt;/a&gt;
has been tasked to consolidate + standardize the plethora of proposals.&lt;/p&gt;
&lt;p&gt;I&#39;ll try to summarize what happened in 2011…&lt;/p&gt;
&lt;h3 id=&quot;round-1-html-media-capture&quot;&gt;Round 1: HTML Media Capture &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#round-1-html-media-capture&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://dev.w3.org/2009/dap/camera/&quot; rel=&quot;noopener&quot;&gt;HTML Media Capture&lt;/a&gt; was the DAP&#39;s first go at
standardizing media capture on the web. It works by overloading the &lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt;
and adding new values for the &lt;code&gt;accept&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;If you wanted to let users take a snapshot of themselves with the webcam,
that&#39;s possible with &lt;code&gt;capture=camera&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 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;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;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;accept&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;image/*;capture=camera&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Recording a video or audio is similar:&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;input&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;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;accept&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;video/*;capture=camcorder&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;input&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;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;accept&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;audio/*;capture=microphone&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Kinda nice right? I particularly like that it reuses a file input. Semantically,
it makes a lot of sense. Where this particular &amp;quot;API&amp;quot; falls short is the ability to do realtime effects
(e.g. render live webcam data to a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and apply WebGL filters).
HTML Media Capture only allows you to record a media file or take a snapshot in time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Support:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://developer.android.com/sdk/android-3.0.html&quot; rel=&quot;noopener&quot;&gt;Android 3.0 browser&lt;/a&gt; -
one of the first implementations. Check out &lt;a href=&quot;http://davidbcalhoun.com/2011/android-3-0-honeycomb-is-first-to-implement-the-device-api&quot; rel=&quot;noopener&quot;&gt;this video&lt;/a&gt; to see it in action.&lt;/li&gt;
&lt;li&gt;Chrome for Android (0.16)&lt;/li&gt;
&lt;li&gt;Firefox Mobile 10.0&lt;/li&gt;
&lt;li&gt;iOS6 Safari and Chrome (partial support)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;round-2-device-element&quot;&gt;Round 2: device element &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#round-2-device-element&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many thought HTML Media Capture was too limiting, so a new spec
emerged that supported any type of (future) device. Not surprisingly, the design called
for a new element, the &lt;a href=&quot;http://dev.w3.org/html5/html-device/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; element&lt;/a&gt;,
which became the predecessor to &lt;code&gt;getUserMedia()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Opera was among the first browsers to create &lt;a href=&quot;http://my.opera.com/core/blog/2011/03/14/web-meet-device&quot; rel=&quot;noopener&quot;&gt;initial implementations&lt;/a&gt;
of video capture based on the &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; element. Soon after
(&lt;a href=&quot;http://my.opera.com/core/blog/2011/03/23/webcam-orientation-preview&quot; rel=&quot;noopener&quot;&gt;the same day&lt;/a&gt; to be precise),
the WhatWG decided to scrap the &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; tag in favor of another up and comer, this time a JavaScript API called
&lt;code&gt;navigator.getUserMedia()&lt;/code&gt;. A week later, Opera put out new builds that included
support for the updated &lt;code&gt;getUserMedia()&lt;/code&gt; spec. Later that year,
Microsoft joined the party by releasing a &lt;a href=&quot;http://blogs.msdn.com/b/ie/archive/2011/12/09/media-capture-api-helping-web-developers-directly-import-image-video-and-sound-data-into-web-apps.aspx&quot; rel=&quot;noopener&quot;&gt;Lab for IE9&lt;/a&gt;
supporting the new spec.&lt;/p&gt;
&lt;p&gt;Here&#39;s what &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; would have looked like:&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;device&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;media&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;onchange&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value javascript language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&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;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;device&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autoplay&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;video&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;video&#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;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Support:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, no released browser ever included &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt;.
One less API to worry about I guess :) &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; did have two great things going
for it though: 1.) it was semantic, and 2.) it was easily extendible to support
more than just audio/video devices.&lt;/p&gt;
&lt;p&gt;Take a breath. This stuff moves fast!&lt;/p&gt;
&lt;h3 id=&quot;round-3-webrtc&quot;&gt;Round 3: WebRTC &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#round-3-webrtc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;device&amp;gt;&lt;/code&gt; element eventually went the way of the Dodo.&lt;/p&gt;
&lt;p&gt;The pace to find a suitable capture API accelerated thanks to the larger [WebRTC][webrtc-spec] (Web Real Time Communications) effort. That spec is overseen by the &lt;a href=&quot;http://www.w3.org/2011/04/webrtc/&quot; rel=&quot;noopener&quot;&gt;W3C WebRTC Working Group&lt;/a&gt;. Google, Opera, Mozilla, and &lt;a href=&quot;http://webrtc.org/&quot; rel=&quot;noopener&quot;&gt;a few others&lt;/a&gt; have
implementations.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getUserMedia()&lt;/code&gt; is related to WebRTC because it&#39;s the gateway into that set of APIs.
It provides the means to access the user&#39;s local camera/microphone stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Support:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getUserMedia()&lt;/code&gt; has been supported since Chrome 21, Opera 18, and Firefox 17.&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#getting-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;navigator.getUserMedia()&lt;/code&gt;, we can finally tap into webcam and microphone input without a plugin.
Camera access is now a call away, not an install away. It&#39;s baked directly into the browser. Excited yet?&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/getusermedia-intro/#feature-detection&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Feature detecting is a simple check for the existence of &lt;code&gt;navigator.getUserMedia&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;hasGetUserMedia&lt;/span&gt;&lt;span class=&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 operator&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webkitGetUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;            navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mozGetUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;msGetUserMedia&lt;span class=&quot;token punctuation&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 function&quot;&gt;hasGetUserMedia&lt;/span&gt;&lt;span class=&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;// Good to go!&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 function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;getUserMedia() is not supported in your browser&#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;You can also &lt;a href=&quot;http://modernizr.com/&quot; rel=&quot;noopener&quot;&gt;use Modernizr&lt;/a&gt; to detect &lt;code&gt;getUserMedia&lt;/code&gt; to avoid the vendor prefix dance yourself:&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;Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getusermedia&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; gUM &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prefixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;getUserMedia&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&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;gUM&lt;/span&gt;&lt;span class=&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;video&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;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 comment&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;h3 id=&quot;gaining-access-to-an-input-device&quot;&gt;Gaining access to an input device &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#gaining-access-to-an-input-device&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To use the webcam or microphone, we need to request permission.
The first parameter to &lt;code&gt;getUserMedia()&lt;/code&gt; is an object specifying the details and
requirements for each type of media you want to access. For example, if you want to access the webcam, the first parameter should be &lt;code&gt;{video: true}&lt;/code&gt;. To use both the microphone and camera,
pass &lt;code&gt;{video: true, audio: true}&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autoplay&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;video&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;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;errorCallback&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;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 string&quot;&gt;&#39;Reeeejected!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&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;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Not showing vendor prefixes.&lt;/span&gt;&lt;br /&gt;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&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;video&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;span class=&quot;token literal-property property&quot;&gt;audio&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;localMediaStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; video &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;video&#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;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&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;localMediaStream&lt;span 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;// Note: onloadedmetadata doesn&#39;t fire in Chrome when using it with getUserMedia.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// See crbug.com/110938.&lt;/span&gt;&lt;br /&gt;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onloadedmetadata&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// Ready to go. Do some stuff.&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; errorCallback&lt;span class=&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;OK. So what&#39;s going on here? Media capture is a perfect example of new HTML5 APIs
working together. It works in conjunction with our other HTML5 buddies, &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;.
Notice that we&#39;re not setting a &lt;code&gt;src&lt;/code&gt; attribute or including &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements
on the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element. Instead of feeding the video a URL to a media file, we&#39;re feeding
it a &lt;a href=&quot;https://web.dev/tutorials/workers/basics/#toc-inlineworkers-bloburis&quot;&gt;Blob URL&lt;/a&gt; obtained
from a &lt;code&gt;LocalMediaStream&lt;/code&gt; object representing the webcam.&lt;/p&gt;
&lt;p&gt;I&#39;m also telling the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; to &lt;code&gt;autoplay&lt;/code&gt;, otherwise it would be frozen on
the first frame. Adding &lt;code&gt;controls&lt;/code&gt; also works as you&#39;d expected.&lt;/p&gt;
&lt;p&gt;If you want something that works cross-browser, try this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;                            navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webkitGetUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;                            navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mozGetUserMedia &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;                            navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;msGetUserMedia&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; video &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;video&#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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&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;audio&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;span class=&quot;token literal-property property&quot;&gt;video&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&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;stream&lt;span class=&quot;token punctuation&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; errorCallback&lt;span class=&quot;token punctuation&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;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;somevideo.webm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// fallback.&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;h3 id=&quot;setting-media-constraints-resolution,-height,-width&quot;&gt;Setting media constraints (resolution, height, width) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#setting-media-constraints-resolution,-height,-width&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first parameter to &lt;code&gt;getUserMedia()&lt;/code&gt; can also be used to specify more requirements
(or constraints) on the returned media stream. For example, instead of just indicating you want basic access to video (e.g. &lt;code&gt;{vide: true}&lt;/code&gt;), you can additionally require the stream
to be HD:&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; hdConstraints &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;video&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;mandatory&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;minWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1280&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;minHeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;720&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span 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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hdConstraints&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successCallback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorCallback&lt;span 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 operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; vgaConstraints &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;video&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;mandatory&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;maxWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;maxHeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span 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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vgaConstraints&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successCallback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorCallback&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 more configurations, see the &lt;a href=&quot;http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-MediaTrackConstraints&quot; rel=&quot;noopener&quot;&gt;constraints API&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;selecting-a-media-source&quot;&gt;Selecting a media source &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#selecting-a-media-source&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Chrome 30 or later, &lt;code&gt;getUserMedia()&lt;/code&gt; also supports selecting the video/audio source
using the &lt;code&gt;MediaStreamTrack.getSources()&lt;/code&gt; API.&lt;/p&gt;
&lt;p&gt;In this example, the last microphone and camera that&#39;s found is selected as the
media stream source:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;MediaStreamTrack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;sourceInfos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; audioSource &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;var&lt;/span&gt; videoSource &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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; sourceInfos&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;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sourceInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sourceInfos&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kind &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;audio&#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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;microphone&#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;        audioSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sourceInfo&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 punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kind &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video&#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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sourceInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;camera&#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;        videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sourceInfo&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 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;        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;Some other kind of source: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sourceInfo&lt;span class=&quot;token punctuation&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;    &lt;span class=&quot;token function&quot;&gt;sourceSelected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;audioSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&lt;span class=&quot;token punctuation&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sourceSelected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;audioSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; constraints &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;audio&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;optional&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 literal-property property&quot;&gt;sourceId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; audioSource&lt;span class=&quot;token punctuation&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 literal-property property&quot;&gt;video&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;optional&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 literal-property property&quot;&gt;sourceId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; videoSource&lt;span class=&quot;token punctuation&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;br /&gt;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successCallback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorCallback&lt;span class=&quot;token punctuation&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 out Sam Dutton&#39;s &lt;a href=&quot;https://simpl.info/getusermedia/sources/&quot; rel=&quot;noopener&quot;&gt;great demo&lt;/a&gt; of how
to let users select the media source.&lt;/p&gt;
&lt;h3 id=&quot;security&quot;&gt;Security &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#security&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some browsers throw up an infobar upon calling &lt;code&gt;getUserMedia()&lt;/code&gt;,
which gives users the option to grant or deny access to their camera/mic.
The spec unfortunately is very quiet when it comes to security. For example, here
is Chrome&#39;s permission dialog:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Permission dialog in Chrome&quot; decoding=&quot;async&quot; height=&quot;231&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z5cVe361mrgghnmWMVw1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Permission dialog in Chrome&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If your app is running from SSL (&lt;code&gt;https://&lt;/code&gt;), this permission will be persistent.
That is, users won&#39;t have to grant/deny access every time.&lt;/p&gt;
&lt;h3 id=&quot;providing-fallback&quot;&gt;Providing fallback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/getusermedia-intro/#providing-fallback&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For users that don&#39;t have support for &lt;code&gt;getUserMedia()&lt;/code&gt;, one option is to fallback
to an existing video file if the API isn&#39;t supported and/or the call fails for some reason:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Not showing vendor prefixes or code that works cross-browser:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fallbackvideo.webm&#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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&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;stream&lt;span class=&quot;token punctuation&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia&lt;span class=&quot;token punctuation&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;fallback&lt;/span&gt;&lt;span class=&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;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&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;video&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; success&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fallback&lt;span class=&quot;token punctuation&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;</content>
    <author>
      <name>Eric Bidelman</name>
    </author><author>
      <name>Sam Dutton</name>
    </author>
  </entry>
</feed>
