How to Build a Browser-Based QR Code Scanner with JavaScript and HTML5

QR codes1 are everywhere - URLs, payment flows, ticketing, inventory labels, patient wristbands. Building a browser-based QR code scanner means users get instant scanning capability on any device without downloading an app. This guide shows how to build one using the Dynamsoft Barcode Reader JavaScript SDK v11, which runs entirely in-browser via WebAssembly.

Dynamsoft QR code scanner in HTML5

The SDK supports 30+ barcode formats including QR Code, Micro QR Code, and QR Code Model 1, with complementation and deformation-resistant algorithms built in for damaged or wrinkled codes.

What you’ll build: A fully functional, camera-powered QR code scanner that runs in any modern browser — no app download required — using Dynamsoft Barcode Reader v11 and fewer than 20 lines of JavaScript.

Key Takeaways

  • Dynamsoft Barcode Reader v11 delivers a complete browser-based QR scanning experience via a single launch() call, with no native app required.
  • The barcodeFormats filter restricts decoding to QR codes only, measurably improving speed and accuracy in real-time video scenarios.
  • SM_MULTI_UNIQUE scan mode enables continuous multi-code collection with automatic de-duplication, suitable for inventory and batch validation workflows.
  • The SDK runs entirely via WebAssembly and integrates into WebView, React, Vue, and Angular without modification.

Common Developer Questions

  • How do I scan a QR code from a webcam using JavaScript in a browser?
  • How do I prevent duplicate QR codes from being scanned multiple times?
  • How do I scan only QR codes and ignore other barcode types with Dynamsoft?

Prerequisites

Add the SDK to Your Project

CDN (quickest)

<script src="https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.2.4000/dist/dbr.bundle.js"></script>

npm / yarn (for React, Vue, Angular, etc.)

npm install dynamsoft-barcode-reader-bundle@11.2.4000
# or
yarn add dynamsoft-barcode-reader-bundle@11.2.4000

When using a package manager you must also configure engineResourcePaths so the SDK can locate its .wasm engine files. See the framework samples for ready-made examples.

Scan Your First QR Code in Under 20 Lines

The entire scanner - camera UI, viewfinder, decoding engine - launches with one call. The launch() promise resolves when the user scans a barcode or closes the scanner.

<!DOCTYPE html>
<html>
<head>
  <title>QR Code Scanner</title>
  <script src="https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.2.4000/dist/dbr.bundle.js"></script>
</head>
<body>
  <button onclick="scan()">Scan QR Code</button>
  <script>
    async function scan() {
      const result = await new Dynamsoft.BarcodeScanner({
        license: "YOUR_LICENSE_KEY_HERE",
        barcodeFormats: [Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE]
      }).launch();
      if (result?.barcodeResults?.length) {
        alert(result.barcodeResults[0].text);
      }
    }
  </script>
</body>
</html>

The barcodeFormats filter restricts recognition to QR codes only, which improves both speed and accuracy for QR-specific workflows.

Collect Multiple QR Codes Continuously Without Duplicates

For workflows that need to collect several barcodes in sequence - inventory counts, batch ticket validation - use SM_MULTI_UNIQUE mode. The scanner stays open and fires onUniqueBarcodeScanned each time a new unique code is detected. A built-in BarcodeResultView lists results in real time.

<!DOCTYPE html>
<html>
<head>
  <title>Multi QR Code Scanner</title>
  <script src="https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.2.4000/dist/dbr.bundle.js"></script>
</head>
<body>
  <button onclick="scan()">Start Scanning</button>
  <script>
    async function scan() {
      const result = await new Dynamsoft.BarcodeScanner({
        license: "YOUR_LICENSE_KEY_HERE",
        barcodeFormats: [Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE],
        scanMode: Dynamsoft.EnumScanMode.SM_MULTI_UNIQUE,
        onUniqueBarcodeScanned: (item) => {
          console.log(`[${item.formatString}] ${item.text}`);
        }
      }).launch();
      // result.barcodeResults contains all unique barcodes collected
      console.log("Final results:", result.barcodeResults);
    }
  </script>
</body>
</html>

By default, a barcode is considered “new” again after 3 seconds (duplicateForgetTime). Adjust this to suit your use case:

const scanner = new Dynamsoft.BarcodeScanner({
  license: "YOUR_LICENSE_KEY_HERE",
  scanMode: Dynamsoft.EnumScanMode.SM_MULTI_UNIQUE,
  duplicateForgetTime: 5000 // ms
});

Multiple QR Codes scanned in one session

Supported QR Code Variants

Beyond standard QR codes, the SDK also recognises Micro QR Code and QR Code Model 1 without any extra configuration.

Micro QR code

Customize the Scanner Behavior and UI

Restrict Decoding to a Scan Region

To speed up decoding and guide the user, you can restrict recognition to a rectangular region of the camera frame. Set the scan region via the BarcodeScannerConfig using a custom template file (templateFilePath), or rely on the built-in viewfinder to give users a visual alignment guide.

Scan region viewfinder

Filter by Barcode Format to Improve Speed

Pass any combination of EnumBarcodeFormat values to barcodeFormats:

const scanner = new Dynamsoft.BarcodeScanner({
  license: "YOUR_LICENSE_KEY_HERE",
  barcodeFormats: [
    Dynamsoft.DBR.EnumBarcodeFormat.BF_QR_CODE,
    Dynamsoft.DBR.EnumBarcodeFormat.BF_CODE_128
  ]
});

Limiting formats is one of the most effective ways to improve scan speed in real-time video scenarios.

Decode Damaged or Deformed QR Codes with a Custom Template

For damaged, incomplete, or heavily deformed QR codes (plastic bags, wrinkled paper), provide a custom template JSON via templateFilePath:

const scanner = new Dynamsoft.BarcodeScanner({
  license: "YOUR_LICENSE_KEY_HERE",
  templateFilePath: "path/to/DBR-PresetTemplates.json"
});

The template file lets you enable algorithms such as BarcodeComplementModes (for incomplete codes) and DeformationResistingModes (for wrinkled/curved codes). Start from the dist/DBR-PresetTemplates.json file included in the SDK package and add the parameters you need.

Incomplete QR Code

Replace the Default UI with Your Own

The scanner’s built-in UI is defined in dist/ui/barcode-scanner.ui.xml. Copy and edit this file, then point the scanner to your custom version:

const scanner = new Dynamsoft.BarcodeScanner({
  license: "YOUR_LICENSE_KEY_HERE",
  uiPath: "path/to/my-barcode-scanner.ui.xml?v=1"
});

You can show or hide individual controls (flash button, camera-switch button, upload-image button, close button) via BarcodeScannerConfig properties without modifying the XML.

Use the Scanner Inside a WebView App

Because the scanner is pure HTML5/JavaScript, it runs in WebView components on Android and iOS without modification.

Integrate with React, Vue, or Angular

For React, Vue, and Angular projects, install via npm and configure engineResourcePaths to point the SDK to its WASM assets:

import { BarcodeScanner, EnumScanMode } from "dynamsoft-barcode-reader-bundle";

const config = {
  license: "YOUR_LICENSE_KEY_HERE",
  engineResourcePaths: {
    rootDirectory: "https://cdn.jsdelivr.net/npm/"
  },
  uiPath: "https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.2.4000/dist/ui/barcode-scanner.ui.xml",
  container: ".barcode-scanner-view"
};

const scanner = new BarcodeScanner(config);
scanner.launch().then(result => console.log(result));

See the official framework samples for complete Angular, React, and Vue starters.

Common Issues & Edge Cases

  • Camera permission denied: On iOS Safari and some Android browsers, camera access requires the page to be served over HTTPS. Localhost is an exception; all production deployments must use TLS or scanning will silently fail.
  • WASM fails to load from CDN in restricted networks: Corporate firewalls or offline environments may block jsdelivr.net. Host the dist/ folder yourself and set engineResourcePaths.rootDirectory to your own origin to avoid this.
  • Duplicate scans in multi-mode: If the same physical label appears in two consecutive frames, SM_MULTI_UNIQUE will suppress the duplicate until duplicateForgetTime elapses. For kiosk use cases where the same code is intentionally re-scanned, set duplicateForgetTime: 0.

Further Reading

Source Code

References

  1. https://en.wikipedia.org/wiki/QR_code