Build a Web QR Code Scanner using the Barcode Detection API
The Web Platform Incubator Community Group has published a Shape Detection API specification. The API aims at detecting features like faces and QR codes from images. It is still in draft status and has limited browser support. In this article, we are going to use the Barcode Detection API included in the Shape Detection API to create a web QR code scanner.
This article is Part 1 in a 2-Part Series.
Supported Browsers
You can check out the browser support here: https://caniuse.com/?search=BarcodeDetector%20API
The API mainly works on Chrome 83+ for macOS and Android. In addition, it requires localhost or HTTPS to provide a secure context.
Build a Web QR Code Scanner using the Barcode Detection API
Create a BarcodeDetector Object
We can create a barcode detector with the following code:
-
Check whether it is supported:
if (!('BarcodeDetector' in window)) { console.log('Barcode Detector is not supported by this browser.'); } else { console.log('Barcode Detector supported!'); }
-
Create a barcode detector:
var barcodeDetector = new BarcodeDetector();
-
Create a barcode detector which only detects specified barcode formats:
var barcodeDetector = new BarcodeDetector({formats: ['code_39', 'codabar', 'ean_13']})
You can get the supported formats with the
getSupportedFormats
method:// check supported types await BarcodeDetector.getSupportedFormats(); // ['aztec', 'code_128', 'code_39', 'code_93', 'data_matrix', 'ean_13', 'ean_8', 'itf', 'pdf417', 'qr_code', 'upc_e']
Open Camera using getUserMedia
We can access the camera using the getUserMedia
API and then detect QR codes from the camera video stream. Here, we load camera devices list to a select
and play the first device in a video element:
var localStream;
function loadDevicesAndPlay(){
var constraints = {video: true, audio: false};
navigator.mediaDevices.getUserMedia(constraints).then(stream => {
localStream = stream;
var cameraselect = document.getElementById("cameraSelect");
cameraselect.innerHTML="";
navigator.mediaDevices.enumerateDevices().then(function(devices) {
var count = 0;
var cameraDevices = [];
var defaultIndex = 0;
for (var i=0;i<devices.length;i++){
var device = devices[i];
if (device.kind == 'videoinput'){
cameraDevices.push(device);
var label = device.label || `Camera ${count++}`;
cameraselect.add(new Option(label,device.deviceId));
if (label.toLowerCase().indexOf("back") != -1) { //select the back camera as the default
defaultIndex = cameraDevices.length - 1;
}
}
}
if (cameraDevices.length>0) {
cameraselect.selectedIndex = defaultIndex;
play(cameraDevices[defaultIndex].deviceId);
}else{
alert("No camera detected.");
}
});
});
}
function play(deviceId) {
stop(); // close before play
var constraints = {};
if (!!deviceId){
constraints = {
video: {deviceId: deviceId},
audio: false
}
}else{
constraints = {
video: true,
audio: false
}
}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
localStream = stream;
var cameraVideo = document.getElementsByClassName("camera")[0];
// Attach local stream to video element
cameraVideo.srcObject = stream;
}).catch(function(err) {
console.error('getUserMediaError', err, err.stack);
alert(err.message);
});
}
function stop(){
try{
if (localStream){
localStream.getTracks().forEach(track => track.stop());
}
} catch (e){
alert(e.message);
}
}
Detect QR Codes from the Camera Video Stream
After the camera is opened, we can use the Barcode Detection API to detect QR codes and barcodes.
-
Add
loadeddata
event to thevideo
element. This event will be fired when the camera is opened.document.getElementsByClassName("camera")[0].addEventListener('loadeddata',onOpened, false);
-
In the
onOpened
event, set aninterval
to detect barcodes from the video.var interval; var decoding = false; function onOpened() { startDecoding(); } function startDecoding(){ clearInterval(interval); interval = setInterval(decode, 500); } async function decode(){ if (decoding === false) { console.log("decoding"); var cameraVideo = document.getElementsByClassName("camera")[0]; decoding = true; var barcodes = await barcodeDetector.detect(cameraVideo); decoding = false; console.log(barcodes); } }
Here is an example of the detection results:
[
{
"boundingBox": {
"x": 36,
"y": 36,
"width": 325,
"height": 327,
"top": 36,
"right": 362,
"bottom": 364,
"left": 36
},
"cornerPoints": [
{
"x": 36,
"y": 36
},
{
"x": 362,
"y": 36
},
{
"x": 360,
"y": 360
},
{
"x": 36,
"y": 364
}
],
"format": "qr_code",
"rawValue": "dynamsoft"
}
]
Conclusion
The Barcode Detector API is easy to use and has a fairly good performance. But it has a severe compatibility issue. In the next article, we are going to create a polyfill for the Barcode Detection API with Dynamsoft Barcode Reader JavaScript as its backend so that it can work on all major platforms.
Source Code
A demo has been created, which can draw QR code overlays and read QR codes from image elements and video elements. You can find the online demo and its source code here: https://github.com/xulihang/barcode-detection-api-demo