<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Nikil Mathew on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Nikil Mathew</name>
  </author>
  <link href="https://web.dev/authors/nikilmathew/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/mc2N7pSjT8K7WBi8PR6L.jpeg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Lead Software engineer at Mishipay</subtitle>
  
  
  <entry>
    <title>MishiPay&#39;s PWA increases transactions 10 times and saves 2.5 years of queuing</title>
    <link href="https://web.dev/mishipay/"/>
    <updated>2022-03-28T00:00:00Z</updated>
    <id>https://web.dev/mishipay/</id>
    <content type="html" mode="escaped">&lt;p&gt;MishiPay empowers shoppers to scan and pay for their shopping with their smartphones, rather than
wasting time queuing at the checkout. With MishiPay&#39;s &lt;a href=&quot;https://mishipay.com/&quot; rel=&quot;noopener&quot;&gt;Scan &amp;amp; Go&lt;/a&gt; technology,
shoppers can use their own phone to scan the barcode on items and pay for them, then simply leave
the store. &lt;a href=&quot;https://www.adyen.com/en_GB/landing/online/uk/2019/bnb/report&quot; rel=&quot;noopener&quot;&gt;Studies&lt;/a&gt; reveal that
in-store queuing costs the global retail sector about $200 billion annually.&lt;/p&gt;
&lt;p&gt;Our technology relies on device hardware capabilities such as GPS sensors and cameras that allow
users to locate MishiPay-enabled stores, scan item barcodes within the physical store, and then pay
using the digital payment method of their choice. The initial versions of our Scan &amp;amp; Go technology
were platform-specific iOS and Android applications, and early adopters loved the technology. Read
on to learn how switching to a PWA increased transactions by 10 times and saved 2.5 years of
queuing!&lt;/p&gt;
&lt;ul class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;
      10&lt;sub&gt;×&lt;/sub&gt;
    &lt;/p&gt;
    &lt;p&gt;Increased transactions&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;
      2.5 years
   &lt;/p&gt;
    &lt;p&gt;Queuing saved&lt;/p&gt;
  &lt;/div&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;challenge&quot;&gt;Challenge &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#challenge&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Users find our technology extremely helpful when waiting in a queue or check-out line, as it allows
them to skip the queue and have a smooth in-store experience. But the hassle of downloading an
Android or iOS application made users not choose our technology despite the value. It was a growing
challenge for MishiPay, and we needed to increase user adoption with a lower barrier of entry.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot;&gt;Solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#solution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our efforts at building and launching the PWA helped us remove the installation hassle and
encouraged new users to try our technology inside a physical store, skip the queue, and have a
seamless shopping experience. Since the launch, we have seen a massive spike in user adoption with
our PWA compared to our platform-specific applications.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/8WbTDNrhLsU0El80frMBGE4eMCD3/jtJZ418n11SOtHLTsOht.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    Side-by-side comparison of directly launching the PWA (left, faster) vs. installing and launching the Android app (right, slower).
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Transactions by platform. ¡OS: 16397 (3.98%). Android: 13769 (3.34%). Web: 382184 (92.68%).&quot; decoding=&quot;async&quot; height=&quot;415&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/I5FMltbcuDHqgqEQMyqA.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;
    The majority of all transactions happen on the web.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;technical-deep-dive&quot;&gt;Technical deep-dive &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#technical-deep-dive&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;locating-mishipay-enabled-stores&quot;&gt;Locating MishiPay enabled stores &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#locating-mishipay-enabled-stores&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To enable this feature, we rely on the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Geolocation/getCurrentPosition&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getCurrentPosition()&lt;/code&gt;&lt;/a&gt;
API along with an IP-based fallback solution.&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; geoOptions &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;timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;enableHighAccuracy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;maximumAge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geolocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span 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 parameter&quot;&gt;position&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; cords &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coords&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;Latitude :  &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cords&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latitude&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;    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;Longitude :  &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;cords&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;longitude&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;span 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 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;debug&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;Error: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&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 comment&quot;&gt;/**&lt;br /&gt;     * Invoke the IP based location services&lt;br /&gt;     * to fetch the latitude and longitude of the user.&lt;br /&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;  geoOptions&lt;span 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;This approach worked well in the earlier versions of the app, but was later proven to be a huge pain
point for MishiPay&#39;s users for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Location inaccuracies in the IP-based fallback solutions.&lt;/li&gt;
&lt;li&gt;A growing listing of MishiPay-enabled stores per region requires users to scroll a list and
identify the correct store.&lt;/li&gt;
&lt;li&gt;Users accidentally occasionally choose the wrong store, causing the purchases to be recorded
incorrectly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To address these issues, we embedded unique geolocated QR codes on the in-store displays for each
store. It paved the way for a faster onboarding experience. Users simply scan the geolocated QR
codes printed on marketing material present in the stores to access the Scan &amp;amp; Go web application.
This way, they can avoid typing in the web address &lt;code&gt;mishipay.shop&lt;/code&gt; to access the service.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; height=&quot;600&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; width=&quot;1296&quot; style=&quot;--vid-width: 1296; --vid-height: 600&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/8WbTDNrhLsU0El80frMBGE4eMCD3/NqyMBZGYzGSNqLE7soXR.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    In-store scanning experience using the PWA.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;scanning-products&quot;&gt;Scanning products &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#scanning-products&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A core feature in the MishiPay app is the barcode scanning as this empowers our users to scan their
own purchases and see the running total even before they would otherwise have reached a cash
register.&lt;/p&gt;
&lt;p&gt;To build a scanning experience on the web, we have identified three core layers.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing the three main thread layers: video stream, processing layer, and decoder layer.&quot; decoding=&quot;async&quot; height=&quot;358&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/jRJeqbGW7yqU8VpdbjBO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;video-stream&quot;&gt;Video stream &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#video-stream&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With the help of the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/MediaDevices/getUserMedia&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getUserMedia()&lt;/code&gt;&lt;/a&gt; method, we
can access the user&#39;s rear view camera with the constraints listed below. Invoking the method
automatically triggers a prompt for users to accept or deny access to their camera. Once we have
access to the video stream, we can relay it to a video element as shown below:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Video Stream Layer&lt;br /&gt; * https://developer.mozilla.org/docs/Web/API/MediaDevices/getUserMedia&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; canvasEle &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;canvas&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; videoEle &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;videoElement&#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; canvasCtx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvasEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;2d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;fetchVideoStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;fetchVideoStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; 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;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;span class=&quot;token literal-property property&quot;&gt;facingMode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;environment&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;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;mediaDevices &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;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;mediaDevices&lt;br /&gt;      &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;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token 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;        videoEle&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;        videoStream &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;        videoEle&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 comment&quot;&gt;// Initiate frame capture - Processing Layer.&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 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;debug&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;        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&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;Failed to access the stream:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&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;span class=&quot;token punctuation&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;warn&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;getUserMedia API not supported!!&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;processing-layer&quot;&gt;Processing layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#processing-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For detecting a barcode in a given video stream, we need to periodically capture frames and transfer
them to the decoder layer. To capture a frame, we simply draw the streams from &lt;code&gt;VideoElement&lt;/code&gt; onto
an &lt;code&gt;HTMLCanvasElement&lt;/code&gt; using the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/drawImage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;drawImage()&lt;/code&gt;&lt;/a&gt;
method of the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Canvas_API&quot; rel=&quot;noopener&quot;&gt;Canvas 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;/**&lt;br /&gt; * Processing Layer - Frame Capture&lt;br /&gt; * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas&lt;br /&gt; */&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;captureFrames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;videoEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;readyState &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; videoEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HAVE_ENOUGH_DATA&lt;/span&gt;&lt;span class=&quot;token punctuation&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; canvasHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvasEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; videoEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;videoHeight&lt;span class=&quot;token punctuation&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; canvasWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvasEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; videoEle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;videoWidth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    canvasCtx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;videoEle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasHeight&lt;span class=&quot;token punctuation&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;// Transfer the `canvasEle` to the decoder for barcode detection.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decodeBarcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvasEle&lt;span class=&quot;token punctuation&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;Video feed not available yet&#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;For advanced use cases, this layer also performs some pre-processing tasks such as cropping,
rotating, or converting to grayscale. These tasks can be CPU-intensive and result in the application
being unresponsive given that barcode scanning is a long-running operation. With the help of the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/OffscreenCanvas&quot; rel=&quot;noopener&quot;&gt;OffscreenCanvas&lt;/a&gt; API, we can offload
the CPU-intensive task to a web worker. On devices that support hardware graphics acceleration,
WebGL API and its
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebGL2RenderingContext&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;WebGL2RenderingContext&lt;/code&gt;&lt;/a&gt; can
optimize gains on the CPU-intensive pre-processing tasks.&lt;/p&gt;
&lt;h3 id=&quot;decoder-layer&quot;&gt;Decoder layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#decoder-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The final layer is the decoder layer which is responsible for decoding barcodes from the frames
captured by the processing layer. Thanks to the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Barcode_Detection_API&quot; rel=&quot;noopener&quot;&gt;Shape Detection API&lt;/a&gt; (which is
not yet available on all browsers) the browser itself decodes the barcode from an
&lt;code&gt;ImageBitmapSource&lt;/code&gt;, which can be an &lt;code&gt;img&lt;/code&gt; element, an SVG &lt;code&gt;image&lt;/code&gt; element, a &lt;code&gt;video&lt;/code&gt; element, a
&lt;code&gt;canvas&lt;/code&gt; element, a &lt;code&gt;Blob&lt;/code&gt; object, an &lt;code&gt;ImageData&lt;/code&gt; object, or an &lt;code&gt;ImageBitmap&lt;/code&gt; object.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing the three main thread layers: video stream, processing layer, and Shape Detection API.&quot; decoding=&quot;async&quot; height=&quot;358&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GR4od5fHOxys6lrxttPn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Barcode Decoder with Shape Detection API&lt;br /&gt; * https://web.dev/shape-detection/&lt;br /&gt; */&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;decodeBarcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formats &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 string&quot;&gt;&#39;aztec&#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;code_128&#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;code_39&#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;code_93&#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;codabar&#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;data_matrix&#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;ean_13&#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;ean_8&#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;itf&#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;pdf417&#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;qr_code&#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;upc_a&#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;upc_e&#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 keyword&quot;&gt;const&lt;/span&gt; barcodeDetector &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;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BarcodeDetector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    formats&lt;span 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 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;const&lt;/span&gt; barcodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; barcodeDetector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;detect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvas&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;barcodes&lt;span class=&quot;token punctuation&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; barcodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; barcodes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;rawValue&#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 keyword&quot;&gt;undefined&lt;/span&gt;&lt;span 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;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 keyword&quot;&gt;throw&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For devices that don&#39;t support the Shape Detection API yet, we need a fallback solution to decode
the barcodes. The Shape Detection API exposes a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/BarcodeDetector/getSupportedFormats&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getSupportedFormats()&lt;/code&gt;&lt;/a&gt;
method which helps switch between the Shape Detection API and the fallback solution.&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;// Feature detection.&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;BarceodeDetector&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Check supported barcode formats.&lt;/span&gt;&lt;br /&gt;BarcodeDetector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSupportedFormats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;supportedFormats&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;  supportedFormats&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;format&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; 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;format&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;img alt=&quot;Flow diagram showing how, dependent on Barcode Detector support and the supported barcode formats, either the Shape Detection API or the fallback solution  is being used.&quot; decoding=&quot;async&quot; height=&quot;404&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/GmrPC7mFTdtsEdS1BDWp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;fallback-solution&quot;&gt;Fallback solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#fallback-solution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Several open-source and enterprise scanning libraries are available that can be easily integrated
with any web application to implement scanning. Here are some of the libraries that MishiPay
recommend.&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Library Name&lt;/th&gt;
        &lt;th&gt;Type&lt;/th&gt;
        &lt;th&gt;Wasm Solution&lt;/th&gt;
        &lt;th&gt;Barcode Formats&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/serratus/quaggaJS&quot;&gt;QuaggaJs&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Open Source&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;1D&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/zxing-js/library&quot;&gt;ZxingJs&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Open Source&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;1D &amp; 2D (Limited)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://codecorp.com/products&quot;&gt;CodeCorp&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Enterprise&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;1D &amp; 2D&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://docs.scandit.com/stable/web/&quot;&gt;Scandit&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Enterprise&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;1D &amp; 2D&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption style=&quot;max-width:initial;&quot;&gt;
      Comparison of open-source and commercial barcode scanning libraries
    &lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;All the above libraries are full-fledged SDKs that compose all the layers discussed above. They also
expose interfaces to support various scanning operations. Depending on the barcode formats and
detection speed needed for the business case, a decision can be between Wasm and non-Wasm solutions.
Despite the overhead of requiring an additional resource (Wasm) to decode the barcode, Wasm
solutions outperform the non-Wasm solution in terms of accuracy.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.scandit.com/stable/web/&quot; rel=&quot;noopener&quot;&gt;Scandit&lt;/a&gt; was our primary choice. It supports all barcode
formats required for our business use cases; it beats all the available open-source libraries in
scanning speed.&lt;/p&gt;
&lt;h2 id=&quot;future-of-scanning&quot;&gt;Future of scanning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#future-of-scanning&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once the Shape Detection API is fully supported by all major browsers, we could potentially have a
new HTML element &lt;code&gt;&amp;lt;scanner&amp;gt;&lt;/code&gt; that has the capabilities required for a barcode scanner. Engineering
at MishiPay believes there is a solid use case for the barcode scanning functionality to be a new
HTML element due to the growing number of open source and licensed libraries that are enabling
experiences such as Scan &amp;amp; Go and many others.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;App fatigue is an issue that developers face when their products enter the market. Users often want
to understand the value that an application gives them before they download it. In a store, where
MishiPay saves shoppers&#39; time and improves their experience, it is counterintuitive to wait for a
download before they can use an application. This is where our PWA helps. By eliminating the barrier
to entry, we have increased our transactions by 10 times and enabled our users to save 2.5 years of
waiting in the queue.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/mishipay/#acknowledgements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article was reviewed by &lt;a href=&quot;https://github.com/jpmedley&quot; rel=&quot;noopener&quot;&gt;Joe Medley&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Nikil Mathew</name>
    </author><author>
      <name>Thomas Steiner</name>
    </author>
  </entry>
</feed>
