How to Build a Web Barcode Scanner Using OpenCV.js
OpenCV is an open-source computer vision and machine learning software library and has a basic barcode reading ability. It has a QR Code detector added in 2018 and a barcode detector added in 2021 which supports detecting and decoding of EAN-13, EAN-8, and UPC-A barcodes.
It has a JavaScript version based on WebAssembly which can run in the browser. In this article, we are going to build a web barcode scanner with OpenCV.js that reads EAN-13, EAN-8, UPC-A, and QR codes from images and a live camera stream.
What you’ll build: A browser-based barcode scanner that uses OpenCV.js (WebAssembly) to detect and decode 1D barcodes and QR codes from static images and a live camera feed.
Key Takeaways
- OpenCV.js brings OpenCV’s barcode and QR code detectors to the browser via WebAssembly, supporting EAN-13, EAN-8, UPC-A, and QR Code.
- The
cv.barcode_BarcodeDetectorclass handles 1D barcode detection whilecv.QRCodeDetectorhandles QR codes — both use a detect-then-decode two-step workflow. - Live camera scanning is achieved by combining
getUserMediawith asetIntervaldecoding loop that captures video frames to a hidden canvas. - OpenCV.js supports only four barcode symbologies; for production use cases requiring broader format support or higher accuracy, a commercial SDK like Dynamsoft Barcode Reader is recommended.
Common Developer Questions
- How do I scan barcodes from a webcam in JavaScript using OpenCV.js?
- What barcode types does OpenCV.js support in the browser?
- How does OpenCV.js barcode detection compare to commercial barcode SDKs?
Prerequisites
- A modern browser with WebAssembly support (Chrome, Firefox, Edge, Safari).
- Basic knowledge of HTML and JavaScript.
- A camera-equipped device for live scanning.
- (Optional) Get a 30-day free trial license for Dynamsoft Barcode Reader if you want to compare results.
Step 1: Include the OpenCV.js Library in Your Web Page
Include OpenCV with the following tag:
<script async src="https://docs.opencv.org/4.8.0/opencv.js" type="text/javascript"></script>
We can know its loading status in the onRuntimeInitialized event.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<title>Web Barcode Detector with OpenCV.js</title>
</head>
<body>
<p id="status">OpenCV.js is loading...</p>
<script type="text/javascript">
var Module = {
// https://emscripten.org/docs/api_reference/module.html#Module.onRuntimeInitialized
onRuntimeInitialized() {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}
};
</script>
<script async src="https://docs.opencv.org/4.8.0/opencv.js" type="text/javascript"></script>
</body>
</html>
Then we can use the cv namespace to perform related functions.
Step 2: Read Barcodes from a Static Image
-
Initialize a barcode detector for reading 1D barcodes or a QR code detector for reading 2D barcodes.
let 1DDetector = new cv.barcode_BarcodeDetector(); let QRDetector = new cv.QRCodeDetector(); -
Use the detector to read barcodes from an image element or a canvas element. We use the
detectfunction to get the location of the barcode and then use thedecodefunction to read the barcode content.let mat = cv.imread(imageSource); let points = new cv.Mat(); detector.detect(mat,points); let result = detector.decode(mat,points); mat.delete(); points.delete();
Step 3: Read Barcodes from a Live Camera Stream
We can use getUserMedia to open a camera and perform live barcode scanning.
-
Add a video element and a canvas element in the HTML.
<canvas id="hiddenCanvas" style="display: none;"></canvas> <video class="camera fullscreen" muted autoplay="autoplay" playsinline="playsinline" webkit-playsinline></video> -
Open the camera with
getUserMedia.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); } } -
Add a function to capture a frame from the camera using
canvas.function capture(){ let video = document.getElementsByClassName("camera")[0]; let canvas = document.getElementById("hiddenCanvas"); let w = video.videoWidth; let h = video.videoHeight; canvas.width = w; canvas.height = h; let ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, w, h); } -
Start an interval to capture frames and read barcodes from the frames.
function startDecodingLoop(){ stopDecodingLoop(); interval = setInterval(captureAndDecode,200); } async function captureAndDecode(){ if (decoding) { return; } if (!cv) { return; } decoding = true; try { capture(); let cvs = document.getElementById("hiddenCanvas"); let results = decodeWithOpenCV(cvs); console.log(results); } catch (error) { console.log(error); } decoding = false; } function stopDecodingLoop(){ clearInterval(interval); decoding = false; }
Extend with Custom Image Processing
As OpenCV provides a rich collection of image processing methods, we can build our own barcode reader like this one or control the reading behavior using things like motion detection.
OpenCV.js vs. Commercial SDKs: Feature Comparison
Compared to commercial barcode reading SDKs like Dynamsoft Barcode Reader, OpenCV’s barcode detection has some limitations.
-
Limited barcode types.
OpenCV Dynamsoft Barcode Reader 1D Types EAN-13
EAN-8
UPC-ACode 128
EAN-8
EAN-13
UPC-A
UPC-E
Code 39 (including Code 39 Extended)
Code 93
Interleaved 2 of 5
Industrial 2 of 5
Codabar
GS1 DataBar
Postal Codes
Patch Code
GS1 Composite Code
MSI (Modified Plessey)
Code 112D Types QR Code QR Code (including Micro QR Code)
Data Matrix
PDF417 (including Micro PDF417)
Aztec Code
MaxiCode (mode 2-5)
DotCode -
Less robust.
Dynamsoft Barcode Reader can read barcodes in various conditions, like damaged barcodes, shadowed barcodes, and wrinkled barcodes. Go to this dataset website to see what tough barcodes Dynamsoft Barcode Reader can read.
-
Less customizability.
Dynamsoft Barcode Reader provides rich parameters to tune the decoding process (arthitecture).
-
Lack of commercial support.
Common Issues and Edge Cases
- OpenCV.js fails to load or
cvis undefined. The WebAssembly binary is large (~8 MB). Ensure the script tag usesasyncand only call OpenCV APIs afteronRuntimeInitializedfires. On slow connections, add a visible loading indicator. cv.barcode_BarcodeDetectoris not a constructor. This API was added in OpenCV 4.5.3. If you load an older build ofopencv.js, the barcode module is missing. Use version 4.5.3 or later.- Camera frame decoding is slow or skips frames. The 200 ms interval in the decoding loop can overlap with slow decode calls. Guard with a
decodingflag (shown above) and consider increasing the interval on lower-end devices.
Source Code
Get the source code of the demo to have a try. You can compare the performance of Dynamsoft Barcode Reader and OpenCV using this demo: