Companies like Walmart and Amazon have embraced this technique to draw customers to their online and offline stores using their phone app. Other companies like Fedex and UPS allow customers to scan the codes on packages using a phone app, instead of needing to manually type in long lists of characters.
If the users of your mobile website have a need to type in long codes like activation codes, or they like to look up specific products on your website based on a model number printed in a magazine or advertisement, then you too can take advantage of QR codes to eliminate the frustration of tiny keyboards and spare them the need to double check for errors.
QR Code Scanning with Your Mobile Website
You don’t need a native phone app to scan QR codes. It’s quite simple to create your own QR code reader. Your website running on a smartphone equipped with a camera and running a little JavaScript can do the same trick.
Here’s a demo of a QR code scanner that works not only on Mobile but also in most modern devices. All you need is a camera and a QR code to scan.
If you don’t have a QR code handy, here’s one that shows the first eight digits of Pi.
Creating the QR Code Reader
Our QR code reader will need some HTML and JavaScript but most importantly, a JavaScript library capable of interpreting the QR code.
We’re not going to build that ourselves, because there are some great libraries out there doing this for us, so we don’t need to reinvent the wheel for our current purposes.
Let’s begin by creating an index.html
file.
Adding the HTML
We’ll need some very simple HTML for this project. Add the following to your body tag:
<div id="container">
<h1>QR Code Scanner</h1>
<a id="btn-scan-qr">
<img src="https://uploads.sitepoint.com/wp-content/uploads/2017/07/1499401426qr_icon.svg">
<a/>
<canvas hidden="" id="qr-canvas"></canvas>
<div id="qr-result" hidden="">
<b>Data:</b> <span id="outputData"></span>
</div>
</div>
<script src="./src/qrCodeScanner.js"></script>
As you can see, we have a wrapper container with a title, the QR icon image wrapped in an a
tag, a canvas
and a div
where we’ll show the result of the scan.
Outside the container div
we’re including the qrCodeScanner.js
file. We’ll create it later, but first we’ll improve the look of our app.
Adding Styles
Add the stylesheet to the head of our HTML:
<link rel="stylesheet" href="src/styles.css" />
Now we want to create the style.css
file within the src
folder. We just want some basic styles for this sample app. Add the following to your css file:
html {
height: 100%;
}
body {
font-family: sans-serif;
padding: 0 10px;
height: 100%;
background: black;
margin: 0;
}
h1 {
color: white;
margin: 0;
padding: 15px;
}
#container {
text-align: center;
margin: 0;
}
#qr-canvas {
margin: auto;
width: calc(100% - 20px);
max-width: 400px;
}
#btn-scan-qr {
cursor: pointer;
}
#btn-scan-qr img {
height: 10em;
padding: 15px;
margin: 15px;
background: white;
}
#qr-result {
font-size: 1.2em;
margin: 20px auto;
padding: 20px;
max-width: 700px;
background-color: white;
}
Nothing fancy at all. We’ll leave everything centered with a big QR button in the middle and the result underneath. We’re using black and white like the QR codes.
Including the Dependent JavaScript Libraries
The secret to reading QR codes is math, and the substitute for math is open-source libraries. To read QR codes, we’ll be using the JavaScript port of the Java-based image processing library written by ZXing. The JavaScript version was ported by Lazar Laszlo.
Because the JavaScript library consists of 17 files, we’ve taken the liberty of merging them into one file, wrapping the code in an anonymous function to prevent pollution of the global namespace and putting the file through Google Closure’s minifier to make the file size smaller.
Some Minor Tweaks to the Library
In order to make the library more adaptable, we’ve added a few minor changes to the library’s output function to differentiate between a success response and an error response.
Two important changes made were in qrcode.js, to these two lines:
qrcode.result = "error decoding QR Code";
//...
qrcode.callback("Failed to load the image");
These strings have been replaced by Error
objects:
qrcode.result = Error("error decoding QR Code");
//...
qrcode.callback(Error("Failed to load the image"));
Now I can detect in my callback function whether an error occurred, just by checking if the callback payload is an instance of Error
or not.
Those changes can be found in this fork of the library.
Adding the Script Tag
To use the library in our QR code reader, we first need to include it in our HTML using a regular script tag:
<script src="https://rawgit.com/sitepoint-editors/jsqrcode/master/src/qr_packed.js">
</script>
Treating It as an App
Something we’ll need to do is tell mobile browsers that we don’t want to scale this site in portrait mode. This can be achieved by adding the following meta tag within the head
element:
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
Adding the JavScript
Now we need to create the qrCodeReader.js
file in the src
folder, which should be at the same level as our HTML file.
Let’s add some code to our new file:
const qrcode = window.qrcode;
const video = document.createElement("video");
const canvasElement = document.getElementById("qr-canvas");
const canvas = canvasElement.getContext("2d");
const qrResult = document.getElementById("qr-result");
const outputData = document.getElementById("outputData");
const btnScanQR = document.getElementById("btn-scan-qr");
let scanning = false;
At the top of this bit of code we get the qrcode
object from the window
and assign it to a constant for convenience. We’re also creating a video
element that we’ll use to handle the images coming from the camera.
Then we get the canvas
element and we use it to assign the 2d context
to a constant. We’ll need this to draw the images coming from our camera.
Then we get the relevant elements to show the results and interact with the app and, at the bottom, we declare the scanning
variable, to keep the status of our scanner.
Next we’ll set the callback for our QR code reader. Add the following at the bottom of the file:
qrcode.callback = (res) => {
if (res) {
outputData.innerText = res;
scanning = false;
video.srcObject.getTracks().forEach(track => {
track.stop();
});
qrResult.hidden = false;
btnScanQR.hidden = false;
canvasElement.hidden = true;
}
};
Here we’re assigning the callback
function of the qrcode
object. This will be called by the library when it detects a QR code. It provides the res
parameter containing the result of the scan, so we’re assigning that to the innerText
property of the outputData
element.
There are four other things going on here. First, we’re setting the scanning
variable to false, since we don’t want to scan anymore after we’ve already decoded our QR code.
Then, we’re getting all tracks from the stream inside the srcObjec
property of the video
element and stopping them one by one. This is how we stop streaming the user’s camera.
Right after that, we’re making sure we display the qrResult
element and the btnScanQR
element so that the user can see the result and trigger another scan. Finally, we hide the canvasElement
, since we don’t need it anymore.
This is all we need to handle the scanner response.
Now we need to access the camera feed and set up a loop to draw the images in our canvas every frame. We also need another loop to scan for QR codes every x milliseconds.
Scanning every frame would be a waste of resources, so we’re better off handling that in a separate loop where we can control the frequency in which we run the algorithm.
We’ll do this in the onclick
handler of the btnScanQR
element:
btnScanQR.onclick = () =>
navigator.mediaDevices
.getUserMedia({ video: { facingMode: "environment" } })
.then(function(stream) {
scanning = true;
qrResult.hidden = true;
btnScanQR.hidden = true;
canvasElement.hidden = false;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.srcObject = stream;
video.play();
tick();
scan();
});
};
OK, let’s go through this. We’re calling the getUserMedia function from the mediaDevices
object, which is part of the navigator object. This will make the browser ask the user for permission to use their camera.
The getUserMedia
function takes an object as a parameter, to which we’re passing the video object with the facingMode
set to "environment"
. If the user is using a mobile device, this will attempt to get the camera on the back. It returns a promise that, when resolved, provides a stream we can assign to the srcObject
of the video
element we created. Then we’re setting the "playsinline"
attribute to true
, which will prevent iOS safari from going into fullscreen.
At this point, we can play()
the video but, of course, this is not enough. We need to draw the stream every frame, so we’re calling the tick
function for that purpose and then the scan
function to trigger the algorithm.
Let’s define the tick
function:
function tick() {
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
scanning && requestAnimationFrame(tick);
}
This is a classic frame-by-frame loop. We’re setting the hight and width of the canvasElement
to the dimensions of the video
. Then we draw the video
to the canvas
and at the bottom we use requestAnimationFrame
and pass in the tick
function so that it will be called again when the browser draws the next frame. We’re doing this conditionally to the scanning
variable being true
.
Now let’s define the scan function:
function scan() {
try {
qrcode.decode();
} catch (e) {
setTimeout(scan, 300);
}
}
Now, this is quite simple. We run the decode
function from the qrcode
library, which will look for a canvas
with an ID of "qr-canvas"
and scan its contents. If we can’t find anything, the error we defined will be caught and we’ll call a setTimeout
to scan in 300 milliseconds. You can set this to something else to see what happens. The more you wait for the next scan, the slower it’ll be. The less you wait, the more you’ll be demanding from the user’s device, so be mindful. Try to look for a sweet spot.
That’s all we need! Now let’s try the app.
See the QR Code Reader in Action
Here’s the working project in codesandbox. Click on the QR button and show the camera some QR code to scan. Hold it in place for an instant and you’ll get your result. You’ll be surprised at how fast and smooth it is.
Conclusion
So there we have it, your very own QR code reader for your mobile website. You can also use this from any platform, which makes it super dynamic and brings a lot of value to your customers.
QR codes have been around for many years, and the image processing code written by ZXing was first ported to JavaScript almost nine years ago. It has stood the test of time so well that it still remains one of the fastest — if not the fastest — option out there for the Web. It’s also free an open source, which makes it even better.
We hope you have fun coming up with something amazing!