Learn how to create a live barcode scanner for your web application using the webcam in JavaScript

How to create a live Barcode scanner using the webcam in JavaScript

A barcode is a machine-readable code in the form of numbers or patterns of parallel lines of varying widths. They are used for identifying a product. The Barcode systems help businesses and organizations to track products and prices for centralized management in a computer software system allowing for incredible increases in productivity and efficiency. From a long time ago supermarkets, hypermarkets and other department stores use barcode scanners that are incorporated on complex devices or systems and they allow the seller to scan the product easily. However, such systems are usually, as said complex and nothing cheap.

If you are a web developer working with (or maybe with a big) company that for some reason, either to sell something or just because some of the information of the documents generated by your system can be retrieven easily with this kind of tool, then you can implement this feature on your app and offer it for your clients. They'll be surely happy if they know that they don't need to buy an extra device to scan the code but with only their web camera and achieve at the same time, the same quality.

In this article we'll show you how to create a live barcode scanner with JavaScript using the awesome QuaggaJS library in your web application.

What is QuaggaJS

QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real- time localization and decoding of various types of barcodes such as EAN, CODE 128, CODE 39, EAN 8, UPC-A, UPC-C, I2of5 and CODABAR. The library is also capable of using getUserMedia to get direct access to the user's camera stream. Although the code relies on heavy image- processing even recent smartphones are capable of locating and decoding barcodes in real-time. QuaggaJS is a modified version of the zxing library that makes the scanning process of barcodes for your user easy. Simply speaking, this reader is invariant to scale and rotation, whereas other libraries require the barcode to be aligned with the viewport. This is not yet another port of the great zxing library, but more of an extension to it. This implementation features a barcode locator which is capable of finding a barcode-like pattern in an image resulting in an estimated bounding box including the rotation.

Quagga makes use of many modern Web-APIs which are not implemented by all browsers yet. There are two modes in which Quagga operates:

  1. analyzing static images (through Blobs).
  2. using a camera to decode the images from a live-stream (requires the presence of the MediaDevices API).

That's right, you can use quaggaJS directly with images too.

Installation

If you use package managers, you can install Quagga easily in your project using NPM:

npm install quagga

Or with bower:

bower install quagga

Alternatively, you can simply download and include quagga.min.js in your project and you are ready to go. The script exposes the library on the global namespace under Quagga:

<!-- Include quagga minified version from a local file -->
<script src="quagga.min.js"></script>

<!-- Or use a CDN -->
<script src="https://cdn.rawgit.com/serratus/quaggaJS/0420d5e0/dist/quagga.min.js"></script>

Or if you are using it from ES6 with React, Angular etc, then you may like to import it:

import Quagga from 'quagga'; // ES6
const Quagga = require('quagga').default; // Common JS (important: default)

For more information about this library, we recommend you to visit the official repository at Github here.

Usage

As previously mentioned, Quagga works in 2 ways, using the live scanner with the webcam or processing a static image:

Live scanner

To be able to scan dinamically with the webcam, you need obviously a browser that supports navigator.getUserMedia. Start by initializing Quagg and configure the LiveStream mode. Once the default configuration has been set, use the Quagga.start method to start the video stream and begin locating and decoding the images. You need to set the type of barcodes that you want to process through the properties readers in the decoder option:

Quagga.init({
    inputStream : {
        name : "Live",
        type : "LiveStream",
         // Or '#yourElement' (optional)
        target: document.querySelector('#yourElement') 
    },
    decoder : {
        readers : ["code_128_reader"]
    }
}, function(err) {
    if (err) {
        console.log(err);
        return
    }
    console.log("Initialization finished. Ready to start");
    Quagga.start();
});

At the end of the article, you can see a full example of the live scanner including the tracking feature.

Static image

If you want to process images from files (either an image uploaded to the server by your user or in the browser directly), you can do it with Quagga. The only thing you need to do is to set the locate option to true and provide the path to the image (or the base64 data uri representation of the image):

Quagga.decodeSingle({
    decoder: {
        readers: ["code_128_reader"] // List of active readers
    },
    locate: true, // try to locate the barcode in the image
    // You can set the path to the image in your server
    // or using it's base64 data URI representation data:image/jpg;base64, + data
    src: '/barcode_image.jpg'
}, function(result){
    if(result.codeResult) {
        console.log("result", result.codeResult.code);
    } else {
        console.log("not detected");
    }
});

Although we have summarized both functions at its most easy expression, it's worth to read the documentation to check for more interesting features that you can implement.

Example

The following document shows a very simple implementation that will display the scanner on the scanner-container div and will track the barcode scanner on it. Once a valid barcode is processed, it will show its result in the console (on detected canvas):

<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        /* In order to place the tracking correctly */
        canvas.drawing, canvas.drawingBuffer {
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
</head>

<body>
    <!-- Div to show the scanner -->
    <div id="scanner-container"></div>
    <input type="button" id="btn" value="Start/Stop the scanner" />

    <!-- Include the image-diff library -->
    <script src="quagga.min.js"></script>

    <script>
        var _scannerIsRunning = false;

        function startScanner() {
            Quagga.init({
                inputStream: {
                    name: "Live",
                    type: "LiveStream",
                    target: document.querySelector('#scanner-container'),
                    constraints: {
                        width: 480,
                        height: 320,
                        facingMode: "environment"
                    },
                },
                decoder: {
                    readers: [
                        "code_128_reader",
                        "ean_reader",
                        "ean_8_reader",
                        "code_39_reader",
                        "code_39_vin_reader",
                        "codabar_reader",
                        "upc_reader",
                        "upc_e_reader",
                        "i2of5_reader"
                    ],
                    debug: {
                        showCanvas: true,
                        showPatches: true,
                        showFoundPatches: true,
                        showSkeleton: true,
                        showLabels: true,
                        showPatchLabels: true,
                        showRemainingPatchLabels: true,
                        boxFromPatches: {
                            showTransformed: true,
                            showTransformedBox: true,
                            showBB: true
                        }
                    }
                },

            }, function (err) {
                if (err) {
                    console.log(err);
                    return
                }

                console.log("Initialization finished. Ready to start");
                Quagga.start();

                // Set flag to is running
                _scannerIsRunning = true;
            });

            Quagga.onProcessed(function (result) {
                var drawingCtx = Quagga.canvas.ctx.overlay,
                drawingCanvas = Quagga.canvas.dom.overlay;

                if (result) {
                    if (result.boxes) {
                        drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
                        result.boxes.filter(function (box) {
                            return box !== result.box;
                        }).forEach(function (box) {
                            Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 });
                        });
                    }

                    if (result.box) {
                        Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 });
                    }

                    if (result.codeResult && result.codeResult.code) {
                        Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 });
                    }
                }
            });


            Quagga.onDetected(function (result) {
                console.log("Barcode detected and processed : [" + result.codeResult.code + "]", result);
            });
        }


        // Start/stop scanner
        document.getElementById("btn").addEventListener("click", function () {
            if (_scannerIsRunning) {
                Quagga.stop();
            } else {
                startScanner();
            }
        }, false);
    </script>
</body>

</html>

Happy coding !


Senior Software Engineer at Software Medico. Interested in programming since he was 14 years old, Carlos is a self-taught programmer and founder and author of most of the articles at Our Code World.

Sponsors