Introducing STRICH: Barcode Scanning for Web Apps

    Alex Suzuki
    Alex Suzuki
    Share

    We created this article in partnership with Pixelverse. Thank you for supporting the partners who make SitePoint possible.

    Barcodes are ubiquitous in almost any business that deals with physical items. Whether it’s a parcel going out for delivery, serial numbers on devices, concert tickets or receipts, chances are there’s at least one barcode involved.

    Table of Contents

    Barcodes on everyday items

    Traditionally, these barcodes were read using dedicated scanning equipment that transmits the barcode value to an application that consumes it, either via a cable connection or a wireless link like Bluetooth. The scanner emulates a keyboard and sends the characters in the barcode as virtual key presses.

    But these days, everyone is carrying a smartphone in their pocket — a high-powered, connected computing device with a camera. Developers can build apps with a UI tailored to their specific use case, scan barcodes using the camera and directly link it to data in the cloud. Some examples include:

    • recording that maintenance has happened on a device by scanning its serial number
    • scanning outbound parcels and marking them as delivered
    • processing returns by scanning a barcode on a return sheet and then scanning the UPC codes on the returned items
    • validating concert tickets and marking them as consumed
    • … and much more

    In the beginning, these apps were implemented as native apps for Android and iOS. With the modern Web’s capabilities, these apps can now instead be run on a platform everybody knows: the web browser.

    Introducing STRICH: Barcode Scanning for Web Apps

    STRICH is a JavaScript library that brings real-time 1D/2D barcode scanning to the browser. Behind the strange-sounding name (Strich is German for “line” or “stroke”, the building blocks of barcodes) is a lean library distributed via npm — with zero dependencies, allowing for easy integration into any web app. The built-in scanning UI provides user-friendly features like camera selection, tap-to-focus and flashlight (for low-light conditions), further accelerating development. Typescript bindings are available, along with well-maintained reference documentation.

    All commonly used barcode types are supported:

    • 1D linear barcodes like EAN/UPC, Code 128, Code 39 and others
    • 2D matrix codes such as QR Code, Data Matrix, Aztec Code and PDF417

    STRICH leverages technologies like WebGL and WebAssembly to perform the heavy lifting of processing the camera stream and achieving native-like performance in locating and decoding barcodes. This allows for highly efficient and reliable scanning of barcodes.

    Launched in early 2023, STRICH is a relatively young product and under constant development. The current focus is on improving recognition rates for existing barcode types under difficult conditions (uneven lighting, out-of-focus codes, damaged codes etc.), but support for less common barcode types such as Micro QR is also in the works.

    Why Barcode Scanning in Web Apps?

    Opting for web apps instead of native apps offers some unique advantages, which we’ll go into now.

    Simplify development by targeting a single platform

    Instead of having to build for multiple platforms (iOS and Android, possibly Windows) natively or using a hybrid framework like Ionic/Capacitor or Xamarin, you can just build a web app from a single codebase. With recently added and now widely supported PWA capabilities like Push Notifications and Install to Home Screen, there’s often no good reason for developing a native app, especially for in-house apps.

    Easy distribution: you own the channel

    If you’re building an app to scan barcodes, chances are you’re building an in-house app.
    These apps are typically only used by employees, and publishing to Apple’s App Store or Google Play can be a real hassle:

    • separate distribution channels (Apple App Store/Google Play) with their individual idiosyncrasies, and associated costs
    • screenshots and other assets needed, in multiple resolutions and languages
    • filling out lengthy privacy questionnaires
    • instructions need to be provided for reviewers to test the app

    The last point in particular is a common hurdle, as these types of apps often require connectivity to internal backends or authentication credentials that can’t be mocked for testing. Rejected app updates, frequent manual intervention and inexplicable delays in publishing are common — not to mention the risk of a business-critical app being removed from the App Store because someone forgot to act on an email.

    Deployment of a native app vs a web app

    Web apps, in comparison, are easy to distribute and don’t require specialized Android or iOS resources or personnel. Deploying a web app is typically automated using a CI/CD pipeline, with no user interaction or delays involved. A web app is always up to date, there’s no review process, and you’re in complete control of the distribution channel.

    App fatigue

    Going “no app” is also increasingly seen as a positive differentiator, as users are becoming tired of installing apps on their phone’s crowded home screen — especially ones they don’t use on a daily basis.

    Offering a delightful browser-based experience with no extra hoops to jump through is appreciated, especially by tech-savvy folks. And with Progressive Web Apps (PWAs), you can still add Install to Home Screen or offline capabilities.

    For in-house apps, having a QR code displayed in a prominent location is an easy way to launch your app, as almost everyone is familiar with the concept of scanning a QR code to open a website after the pandemic.

    Launch web app by scanning QR code

    Building a Scanning App with STRICH

    Building an app that scans barcodes is very easy with STRICH. To illustrate, we’ll build a ticket scanner app.

    Obtaining a license key

    STRICH requires a valid license key to use. To obtain a key, you need to create an account in the Customer Portal and start the free trial. You then specify the URLs under which your app is reachable and create the key.

    In the example below, we’re adding three URLs: one for development, one for a staging environment, and one for production.

    Creating a license key in the Customer Portal

    Note: apps that require camera access need to be served from a secure context, which means the connection needs to be secure (HTTPS) or via localhost. Tools like ngrok make this easy by mapping a local port to a publicly accessible URL with an automatically generated TLS certificate. But you don’t need them if you’re comfortable setting up certificates yourself.

    Adding the SDK to your app

    Once you’ve obtained the license key for your app, you can go ahead and install the SDK. If you’re using npm to manage your dependencies, you can use npm install just like with any other dependency:

    npm install @pixelverse/strichjs-sdk
    

    Then, in your application’s startup code, provide the license key to the StrichSDK.initialize() method:

    StrichSDK.initialize('<your license key>')
        .then(() => console.log('STRICH initialized'));
    

    When the promise resolves, the SDK is ready to use.

    Implementing a scanning use case

    Now we can start implementing the actual barcode scanning flow.

    For this article, we’ll build a simple ticket scanner. The app will scan the barcode on a ticket, display the ticket number, validity and information on the holder. Looking up the latter would probably involve an HTTP request, which we’ll omit for the sake of simplicity.

    Choosing a layout

    Most scanning apps adopt a split-screen layout, where the upper part contains the camera preview and the lower part provides process context, result display and actions. Our ticket scanning app will adopt a layout composed of the following elements:

    • Header: Displays a title for guidance.

    • Scanning Area: Where the camera feed will be shown.

    • Data: The scanned ticket number along with the name and age of the holder, which might have been fetched from a service.

    • Actions: A set of actions to take after scanning a ticket. Actions should be located at the bottom of the screen to provide easy thumb access.

    Basic layout of app

    With the layout in place, we can start putting together some HTML, CSS and JavaScript.

    The HTML for the app is shown below. I’ve omitted the styling as it‘s not very interesting. The full source code of the example is available on GitHub:

    <html lang="en">
    <head>
        <title>Ticket Scanner Example</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <style>
            /* omitted */
        </style>
    </head>
    <body>
    <main>
        <header>Ticket Scanner</header>
        <section id="scanner">
            <!-- BarcodeReader will live here -->
        </section>
        <section id="results">
            <h3>Ticket #</h3>
            <p id="ticket_number">
                -
            </p>
    
            <h3>Ticket holder</h3>
            <p id="ticket_holder">
                -
            </p>
        </section>
        <section class="actions">
            <button id="deny_button">DENY</button>
            <button id="admit_button">ADMIT</button>
        </section>
    </main>
    
    <script type="module">
       <!-- see next chapter -->
    </script>
    </body>
    </html>
    

    Configuring a barcode reader

    Let’s write some JavaScript to connect a barcode reader to the HTML. The element that will host the barcode reader has an ID value of scanner, so it can be referenced through a CSS selector #scanner. Assuming our tickets have Code 128 barcodes (the most common type, except for the EAN/UPC codes used in retail), we’ll configure the BarcodeReader as follows:

    // variable holding a reference to the BarcodeReader
    let theBarcodeReader = null;
    
    function initializeBarcodeReader() {
        let configuration = {
            selector: '#scanner',
            engine: {
                symbologies: ['code128']
            }
        };
        new BarcodeReader(configuration).initialize()
            .then(reader => {
                reader.detected = (detections) => {
                    displayTicket(detections[0].data);
                };
                // store a reference for interacting with it later
                theBarcodeReader = reader;
                return reader.start();
            });
    }
    

    We store a reference the newly created BarcodeReader in a variable so we can access it later.

    Handling barcode detections

    When a barcode is detected, we’ll invoke displayTicket() to display its the ticket number, along with some mock data on the ticket holder. In a real-life app, this would be the place where we would issue an HTTP request using the fetch API and look up data associated with the ticket from a database.

    Here, we just display the values, pause the scanning and enable the action buttons. The action buttons will clear the displayed values and resume the barcode scanning:

    // hook up UI elements
    const admitButton = document.getElementById('admit_button');
    const denyButton = document.getElementById('deny_button');
    const ticketNumberLabel = document.getElementById('ticket_number');
    const ticketHolderLabel = document.getElementById('ticket_holder');
    
    // show ticket data, enable action buttons
    function displayTicket(ticketNumber) {
    
        // stop barcode detection
        theBarcodeReader.stop();
    
        // display ticket data
        ticketNumberLabel.innerText = ticketNumber;
        ticketHolderLabel.innerText = 'John Doe'; // in a real app, you would look this up
        admitButton.disabled = false;
        denyButton.disabled = false;
    }
    
    // clear ticket data, resume scanning
    function resumeScanning() {
        ticketNumberLabel.innerText = '-';
        ticketHolderLabel.innerText = '-';
        admitButton.disabled = true;
        denyButton.disabled = true;
    
        // start barcode detection
        theBarcodeReader.start();
    }
    
    // hook up action buttons
    admitButton.onclick = () => {
        resumeScanning();
    };
    admitButton.disabled = true;
    denyButton.onclick = () => {
        resumeScanning();
    };
    denyButton.disabled = true;
    

    Putting it all together

    To keep things as simple as possible, I’ve chosen to put the entire app in a single HTML file, with the CSS styles and JavaScript code inlined in the HTML. This is certainly not a recommended practice, but it keeps the example lean and serves as a useful reminder that web app development can be this simple!

    The single HTML file in its entirety is available on GitHub.

    Here’s a demo of the app doing its work:

    Conclusion

    In this article, I’ve shown how to create a simple ticket scanning app that uses the STRICH Barcode Scanning SDK. Web apps offer compelling advantages over native apps, especially for in-house apps that don’t need to be in the App Store. Modern web browsers, combined with a capable SDK like STRICH, make creating user-friendly barcode scanning apps fast, cost-efficient and fun.