Skip to content

Instantly share code, notes, and snippets.

@dvergeylen
Last active April 3, 2024 16:27
Show Gist options
  • Save dvergeylen/a256deb3182b8a863238fcc0704aecb9 to your computer and use it in GitHub Desktop.
Save dvergeylen/a256deb3182b8a863238fcc0704aecb9 to your computer and use it in GitHub Desktop.
JSQrcode library tutorial

Introduction

This tutorial is a step by step guide on how to use the javascript QR Code scanner on a webcam video stream in browser. Some people reported experiencing problems including it in their own projets so I guess a tutorial might help others.

Step 1: Include library in <head> section

<html lang="en">
  <head>
    <script type="text/javascript" src="grid.js"></script>
    <script type="text/javascript" src="version.js"></script>
    <script type="text/javascript" src="detector.js"></script>
    <script type="text/javascript" src="formatinf.js"></script>
    <script type="text/javascript" src="errorlevel.js"></script>
    <script type="text/javascript" src="bitmat.js"></script>
    <script type="text/javascript" src="datablock.js"></script>
    <script type="text/javascript" src="bmparser.js"></script>
    <script type="text/javascript" src="datamask.js"></script>
    <script type="text/javascript" src="rsdecoder.js"></script>
    <script type="text/javascript" src="gf256poly.js"></script>
    <script type="text/javascript" src="gf256.js"></script>
    <script type="text/javascript" src="decoder.js"></script>
    <script type="text/javascript" src="qrcode.js"></script>
    <script type="text/javascript" src="findpat.js"></script>
    <script type="text/javascript" src="alignpat.js"></script>
    <script type="text/javascript" src="databr.js"></script>
  </head>

  <body>
    [...]
  </body>
</html>

Step 2: Create video and canvas markups

JSQrcode doesn't capture QRCodes from video stream directly, but from a canvas. Once there is sufficient data available from the video stream, one copies the data into the canvas and applies JSQRcode decoding method on it.

<div class="video-container">
  <video id="video-preview"></video>
  <canvas id="qr-canvas" class="hidden" ></canvas>
</div>

Notice the canvas must have id=qr-canvas (harcoded in library).

Step 3: Custom javascript to copy video into canvas

Create a new javascript file that you insert after the ones inserted at Step 1. On page load, start the video stream.

window.onload = async function() {
  try {
    /* Ask for "environnement" (rear) camera if available (mobile),
     * will fallback to only available otherwise (desktop).
     * User will be prompted if (s)he allows camera to be started */
    const stream = await navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: "environment",
      },
      audio: false,
    });
    const video = document.getElementById("video-preview");
    video.srcObject = stream;
    video.setAttribute("playsinline", true); /* otherwise iOS safari starts fullscreen */
    video.play();
    setTimeout(tick, 100); /* We launch the tick function 100ms later (see next step) */
  } catch(err) {
    console.log(err); /* User probably refused to grant access*/
  });
};

Step 4: Copy Video image to canvas and start decoding

function tick() {
  const video = document.getElementById('video-preview');
  const qrCanvasElement = document.getElementById('qr-canvas');
  const qrCanvas = qrCanvasElement.getContext('2d');
  let width;
  let height;

  if (video.readyState === video.HAVE_ENOUGH_DATA) {
    qrCanvasElement.height = video.videoHeight;
    qrCanvasElement.width = video.videoWidth;
    qrCanvas.drawImage(video, 0, 0, qrCanvasElement.width, qrCanvasElement.height);

    try {
      const result = qrcode.decode();
      console.log(result)
      /* Video can now be stopped */
      video.pause();
      video.src = '';
      video.srcObject.getVideoTracks().forEach(track => track.stop());

      /* Display Canvas and hide video stream */
      qrCanvasElement.classList.remove('hidden');
      video.classList.add('hidden');
    } catch(e) {
      /* No Op */
    }
  }

  /* If no QR could be decoded from image copied in canvas */
  if (!video.classList.contains('hidden'))
    setTimeout(tick, 100);
}
@dvergeylen
Copy link
Author

dvergeylen commented Dec 3, 2018

Hi @rayj10,

According to Chrome Doc :

Starting with Chrome 47, getUserMedia() requests are only allowed from secure origins: HTTPS or localhost.

This means accessing your webpage from a non secured origin (≃ http://) will make Chrome to refuse executing any javascript calling getUserMedia() (→ access to webcam). Either you put an SSL certificate in place (see Let's Encrypt) either you access your webpage via http://localhost.

Hope this helps!

Edit 2019/07/09:
Firefox 68 now also expects camera and microphone to be accessed from https pages only, see: https://blog.mozilla.org/webrtc/camera-microphone-require-https-in-firefox-68/

@gilly3
Copy link

gilly3 commented Jan 9, 2019

It seems like most of what you've got here is already built-in and exposed via qrcode.setWebcam(videoId). It's difficult to find that due to the lack of documentation.

It would also be a good idea to assign a callback function to qrcode.callback in order to handle the result, once it's received.

EDIT: Except, it doesn't quite work right out of the box, does it? I'm finding your information here to be very valuable, thanks!

@faiztriguna
Copy link

faiztriguna commented Dec 5, 2019

Hi @dvergeylen, can we use this library on a modal ? Can we put Step.3 on "show.bs.modal" ? 'Cause I plan to use it when user click a button, then a modal showed with this library in it.

Thanks in advanced !

@dvergeylen
Copy link
Author

@faiztriguna I would say yes, why not? 🤔

@faiztriguna
Copy link

Hi again @dvergeylen, I've successfully implement your technique on modal. Thank you so much !

@selrahcDC
Copy link

Thank you so much for this tutorials

@ukhaiyr
Copy link

ukhaiyr commented Feb 11, 2020

Thank you for this tutorial.

Take note, the result of the qrcode decoded will be shown in the console.

@Wirah
Copy link

Wirah commented May 27, 2020

I slip the alert message after the line navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" }, audio: false }).then(function(stream) {, because nothing happen in my all android browser.
And alert not showing.
I see message in desktop browser's console log:
MediaStreamError
constraint: ""
message: "The object can not be found here."
name: "NotFoundError"
stack: ""
: MediaStreamErrorPrototype { name: Getter, message: Getter, constraint: Getter, … }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment