Skip to content

Instantly share code, notes, and snippets.

@dvergeylen
Last active April 3, 2024 16:27
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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);
}
@itsaditi
Copy link

Hi,
The camera is not opening in ios as it gives a black box (instead of a canvas/ video tag) in safari browser.

Got any idea what's going wrong?

@dvergeylen
Copy link
Author

Hi,

This might help you. I don't have access to Safari browser for the moment, I can't help for test unfortunately.

@engwoeilim
Copy link

Hi,

Sorry I am a really new to this, may i know where should i put step 3 and step 4 code to ?

@dvergeylen
Copy link
Author

Hi @engwoeilim,

You put the code in a file, let's call it qrdecoder.js (NOT qrcode.js as that name is already taken by the library) and append

 <script type="text/javascript" src="qrdecoder.js"></script>

after databr.js inclusion (see step 1).

@engwoeilim
Copy link

Hi @engwoeilim,

You put the code in a file, let's call it qrdecoder.js (NOT qrcode.js as that name is already taken by the library) and append

 <script type="text/javascript" src="qrdecoder.js"></script>

after databr.js inclusion (see step 1).

Thanks for your reply. What about the code for step 4?

@dvergeylen
Copy link
Author

Thanks for your reply. What about the code for step 4?

You put it in the same file (after last } of Step 3).

@rayj10
Copy link

rayj10 commented Nov 30, 2018

Hi I'm trying to implement this and my html doesn't show anything on chrome, just white screen. Do I need to add anything else other than the above?
I currently have html file with the header and all the associated header files you wrote there, plus the div part in the body. as you mentioned the ids are hardcoded in the lib, so I just copy and pasted everything from step 1 and 2 as my html file.
I have downloaded and put in the same dir all the js files listed on the html head.

then I copy pasted everything from step 3 and 4 into 1 js file and named it qrdecoder.js

is there anything I'm missing?
I noticed on http://webqr.com window.onload will also trigger a permission, is there anything like that I have to add?

EDIT:
atm the console is saying DOMException: Only secure origins are allowed and I noticed on the comment linking this post someone said we need certificate. can you please help point me in the right direction as to how to get that certificate? I'm just starting with web dev and currently using company's server. A non intrusive way to do this would be preferred.

Thanks in advance!:)

@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