Codementor Events

HTML5 QR Code scanning with javascript

Published Apr 12, 2020Last updated Jun 16, 2021

In 2015 I had written an HTML5 based QR code scanning library as a jQuery extension. Recently I realised there was some consistent traffic on my Github Project and the demo page. As I dug more into what was going on and I was embarrassed to see the poor design and obsolete support to the latest HTML APIs around Camera. I recently fixed some of the issues and refactored the javascript library that is now independent of jQuery library and supports Promise based APIs. In this article I’ll explain how to use the new version of the library, some changes and reasons for them and existing issues and plan to fix them. To callout loud, the major changes are:

Important Edit 1 (14th June 2021)


Figure: Update: Version 2.x.x can scan different types of codes including bar codes, aztec codes and different 1D codes along with QR Codes.

Lot of things have changed since v1.0.1 and latest release for the library is v2.0.9 - Please visit QR and barcode scanner using HTML and Javascript for latest content.

Continue reading article on v1.0.1

  • Removed jQuery dependency
  • Refactored the APIs to return Promise rather than being purely based on callbacks.

Introduction

QR Code is a very common technique of encoding information as images. Its a very common used in physical stores for identifying products like bar code is used.

sample qr code
Figure: A sample QR code

The javascript library available at mebjas/html5-qrcode on Github allows users to add a QR code scanner in their web applications. It works cross-platform and across different devices like PC, Mac or smartphones. It uses the stream from the camera and try to decode frames at a certain frequency (configurable) and notify the caller about results via callbacks. Requesting camera permissions in browsers usually results in a permission dialog popup to users for requesting permissions and can only be used if the user grants them.

sample qr code
Figure: Sample permission flow triggered by Chrome browser on Mac

Update: Html5QrcodeScanner, an end to end QR Code Scanner — version 1.1.5

Recently introduced an end to end Qr code scanner on top of the library presented in this article. You can use Html5QrcodeScanner class to integrate scanning with 5 lines of code — read more. If you are interested in integrating the library with your own user interface, continue with this article.

Demo

A demo for this project is hosted at https://blog.minhazav.dev/research/html5-qrcode.html

How to use

Download the Javascript code or use the Github version

The code is hosted at mebjas/html5-qrcode on Github. You can download the library and add it to your codebase from release page or use it directly like:

<script src="https://raw.githubusercontent.com/mebjas/html5-qrcode/master/minified/html5-qrcode.min.js"></script>

I highly recommend using the minified version for the following reasons:

  • It’s smaller in size and would load faster.
  • The actual code html5-qrcode.js is written using ECMAScript which is supported only in the latest browsers. It may not be supported in older browsers. The minified script has been transpiled using Babel.

Not recommended If you still wish to use the non-minified library, try including these:

<script src="./jsqrcode-combined.js"></script>
<script src="./html5-qrcode.js"></script>

Add a palceholder element in HTML

Add an empty HTML element at the appropriate position in your HTML code, give it an id. The library uses this element to insert some hidden HTML elements which shows up as a viewfinder (camera input is shown in this HTML element) to the user performing QR code scan.

Enumerate all available cameras

The Html5Qrcode exposes a static method to enumerate all supported cameras in the device. Calling this method would trigger user permissions as these permissions are required to get the name of the cameras in certain browsers. This method returns a Promise with a list of supported cameras.

// This method will trigger user permissions
Html5Qrcode.getCameras().then(cameras => { 
    /** 
      * devices would be an array of objects of type: 
      * { id: "id", label: "label" }
      */
      if (devices && devices.length) {
          var cameraId = devices[0].id; 
          // .. use this to start scanning.
       }
  }).catch(err => { 
      // handle err
  });

The returned object is an Array of Object with the following fields:

  • id: Id of the camera. This is needed to start scanning the QR Code.
  • label: Label for the camera. This is human-readable name for the camera device like Front Camera or Back Camera.

Once you have cameraId you can perform start/stop operations

Starting the camera implicitly starts QR Code scanning. It runs at a certain fps (frame per seconds) provided in configuration argument. This is an optional field and by default the scanner runs at 2 fps.

To start scanning you need to create an instance of Html5Qrcode and call start() API. This method returns a Promise which succeeds when QR Code scanning starts: Real-time feed starts to show up and QR code scanning starts. When the code is detected the callback qrCodeSuccessCallback is called and when its not detected, the qrCodeErrorCallback callback is called.

// Create instance of the object. The only argument is the "id" of HTML element created above.
const html5QrCode = new Html5Qrcode("reader");
html5QrCode.start(
    cameraId, // retreived in the previous step. 
    {
        fps: 10, // sets the framerate to 10 frame per second
        qrbox: 250 // sets only 250 X 250 region of viewfinder to 
                   // scannable, rest shaded. 
    },
    qrCodeMessage => { 
        // do something when code is read. For example: 
        console.log(`QR Code detected: ${qrCodeMessage}`); 
    },
    errorMessage => {
        // parse error, ideally ignore it. For example: 
        console.log(`QR Code no longer in front of camera.`);
    })
    .catch(err => {
        // Start failed, handle it. For example, 
        console.log(`Unable to start scanning, error: ${err}`);
    });

The configuration argument in start() method can effect both scanning behavior and UI. There are two optional properties right now, if you don’t want them you can just pass an empty Object like {}.

fps - Integer, Example = 10

Also known as frame per seconds, the default value for this is 2 but it can be increased to get faster scanning. Increasing to a very high value could affect performance. Value >1000 will simply fail. Ideal value for this could be 10.

qrbox - Integer, Example = 250

Use this property to limit the region of the viewfinder you want to use for scanning. The rest of the viewfinder would be shaded and ignored by the QR code scanner. For example by passing config { qrbox : 250 }, the screen will look like:

screenshot

This is an optional property, if nothing is passed, the scanner will scan the entire region of the viewfinder.

To stop scanning, use stop() method

The stop() member method in Html5Qrcode returns a Promise which finishes when the video feed is closed and held resources are released.

html5QrCode.stop().then(ignore => { 
  // QR Code scanning is stopped. 
    console.log("QR Code scanning stopped.");
}).catch(err => {
    // Stop failed, handle it. 
    console.log("Unable to stop scanning.");
});

This should only be called after QR Code has been started successfully.

For more clarity I have dumped the javascript code used in demo in this public gist.

This code uses jQuery but it’s not really needed.

Changelog in v1.0.0

  • Removed jQuery dependency
  • Refactored the library to Promise based APIs.
  • Refactored the code to ECMAScript
  • Merged Babel transpiled minifed library with jsqrcode to a single 52Kb library.

Cross-platform support

Checkout my original article for more information - blog.minhazav.dev

Existing issues and plan to fix them

In milestore/v1.0.1 I plan to fix compatibility issues with Opera browser and IOS based browsers.

Some open issues are:

  • issue/14 - Fix camera query issue in IOS browsers - Chrome, Mozilla, Edge, Opera.
  • [FIXED] issue/14 - Fix stuck camera issues in IOS Safari
  • issue/13 - Fix camera query issue in Android Opera Mini
  • issue/16 - Fix Camera Query issue in Mac OS Opera browser

How to contribute

If you are excited or interested you can contribute to this project by:

  • If you find compatibility issues with certain browser, create an issue here.
  • Raising issues for bugs faced, at Github issue page for the project. Feel free to add some related interesting discussions which could be taken up as work-item.
  • Sending a Pull Request for bugs fixed by you.
  • Rating the project with stars and shares.
Discover and read more posts from minhaz
get started
post commentsBe the first to share your opinion
fanz fanz
4 years ago

Hello, thank you for sharing the QR code scanning. Were you able to resolve the compatibility issue with the IOS Chrome browser? I cannot query the camera. Is it solvable or IOS security blocks these browsers from scanning the camera. Is there a workaround?

minhaz
4 years ago

As replied offline, this is an IOS issue - only Safari is able to access camera directly. For other browsers users can either use the default camera or select file in gallery to scan.

Show more replies