npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

web-wasm-barcode-reader

v1.5.0

Published

Barcode scanner powered by ZBar compiled to WebAssembly

Readme

Web WASM Barcode Reader

A browser-based barcode scanner powered by ZBar compiled to WebAssembly. Works on any browser, including Safari/iOS where the native Barcode Detection API isn't available.

Built with TypeScript, Vite, and a wrapper-ready BarcodeScanner class designed for easy integration into React, Vue, or any frontend framework.

Based on the blog post: Using the ZBar barcode scanning suite in the browser with WebAssembly.

My take: Barcode Scanning on iOS: The Missing Web API and a WebAssembly Solution.

NPM: https://www.npmjs.com/package/web-wasm-barcode-reader

npm i web-wasm-barcode-reader

Features

  • Real-time camera scanning with configurable scan region and interval
  • Rotation-based skew correction — scans at 0°, +30°, and -30° with early exit, so tilted barcodes are detected without perfect alignment
  • Animated CSS overlay — dark viewfinder mask, corner brackets, and a sweeping laser line (replaces the old static PNG)
  • Audible beep on detection (base64-encoded, no external file)
  • Torch toggle for devices that support flashlight control
  • Debug preview panel — shows all three rotation passes side-by-side with detection highlighting
  • Framework-agnosticBarcodeScanner class with start()/stop() lifecycle, constructor has no side effects (React strict-mode safe)

Supported Barcode Formats

ZBar supports the following symbologies:

| 1D | 2D | |---|---| | EAN-13, EAN-8 | QR Code | | UPC-A, UPC-E | | | ISBN-10, ISBN-13 | | | Code 128, Code 39, Code 93 | | | Interleaved 2 of 5 (I25) | | | DataBar | |

Note: Data Matrix, PDF417, and Aztec codes are not supported by ZBar.

Quick Start

npm install
npm run dev

Open the URL shown by Vite (typically http://localhost:5173). Grant camera access and point at a barcode within the scan region.

Production Build

npm run build
npm run preview

The build output goes to dist/. Type-checking runs automatically before the Vite build.

Project Structure

web-wasm-barcode-reader/
├── public/
│   ├── a.out.js              # Emscripten JS glue (pre-compiled)
│   └── a.out.wasm            # ZBar WASM binary (pre-compiled)
├── src/
│   ├── scanner.ts            # BarcodeScanner class — core lifecycle + scan loop
│   ├── overlay.ts            # Animated CSS scan overlay (mask, brackets, laser)
│   ├── main.ts               # Demo entry point
│   └── types/
│       └── emscripten.d.ts   # TypeScript declarations for the Emscripten Module
├── index.html                # Demo page
├── scan.c                    # C source — ZBar image scanning + WASM buffer management
├── library.js                # Emscripten JS library — bridges WASM results to JS
├── package.json
├── tsconfig.json             # strict: true, noImplicitAny: true
└── vite.config.ts

Architecture

How a Scan Works

Camera frame
    │
    ├─ drawImage() ──► Offscreen canvas (crop scan region from video)
    │                      │
    │                      ├─ Rotate 0°  ──► grayscale ──► WASM scan_image()
    │                      ├─ Rotate +30° ──► grayscale ──► WASM scan_image()  (only if 0° missed)
    │                      └─ Rotate -30° ──► grayscale ──► WASM scan_image()  (only if ±30° missed)
    │
    └─ On detection: polygon overlay + beep + onDetect callback

WASM Bridge

The scanner communicates with ZBar through three C functions exposed via Emscripten:

| C function | Purpose | |---|---| | create_buffer(w, h) | malloc a buffer on the WASM heap for image data | | scan_image(ptr, w, h) | Run ZBar on grayscale pixels at ptr. Calls js_output_result on hit. | | destroy_buffer(ptr) | free a buffer (unused — ZBar frees it internally via zbar_image_free_data) |

library.js defines js_output_result, which reads symbol name, data, and polygon coordinates from the WASM heap and forwards them to Module.processResult — a callback set by the BarcodeScanner class.

Important: scan_image registers the buffer with zbar_image_free_data as the cleanup handler, so ZBar frees the buffer when the image is destroyed. A fresh buffer must be allocated for every scan_image call — reusing a pointer is use-after-free.

Usage as npm Package

The library requires the Emscripten WASM glue script (a.out.js) to be loaded before the scanner is started. The WASM binary (a.out.wasm) is fetched automatically by the glue script at runtime.

1. Copy the WASM files into your public/static directory

After installing, copy the WASM assets to a location your web server can serve:

cp node_modules/web-wasm-barcode-reader/public/a.out.js  public/
cp node_modules/web-wasm-barcode-reader/public/a.out.wasm public/

Or with a bundler, add a copy step to your build pipeline.

2. Load the glue script before using the scanner

Add a <script> tag in your HTML before your app bundle:

<script src="/a.out.js"></script>
<script type="module" src="/your-app.js"></script>

Important: a.out.js must be loaded as a classic (non-module) script because it sets up the global Module object that the scanner depends on.

3. Import and use

import { BarcodeScanner } from 'web-wasm-barcode-reader';

const scanner = new BarcodeScanner({
  container: document.getElementById('scanner-mount')!,
  onDetect: (result) => {
    console.log(result.symbol, result.data);
  },
});

await scanner.start();
// later...
scanner.stop();

If a.out.js is not loaded, start() will reject with a clear error message.

API

BarcodeScanner

import { BarcodeScanner } from 'web-wasm-barcode-reader';

const scanner = new BarcodeScanner({
  container: document.getElementById('scanner-mount')!,
  onDetect: (result) => {
    console.log(result.symbol, result.data);
  },
});

await scanner.start();
// later...
scanner.stop();

ScannerOptions

| Option | Type | Default | Description | |---|---|---|---| | container | HTMLElement | required | Element to mount the scanner into | | onDetect | (result: ScanResult) => void | required | Called on each barcode detection | | onError | (error: Error) => void | console.error | Called on unrecoverable errors | | scanInterval | number | 150 | Milliseconds between scan attempts | | beepOnDetect | boolean | true | Play an audible beep on detection | | facingMode | 'environment' \| 'user' | 'environment' | Camera facing mode | | scanRegion | { width: number; height: number } | { width: 0.702, height: 0.242 } | Scan region as fraction of container | | previewCanvas | HTMLCanvasElement | undefined | Optional canvas for rotation debug preview |

ScanResult

| Field | Type | Description | |---|---|---| | symbol | string | Barcode symbology (e.g. "EAN-13", "QR-Code") | | data | string | Decoded barcode content | | polygon | number[] | Flat [x1, y1, x2, y2, ...] bounding polygon in container coordinates |

Methods

| Method | Description | |---|---| | start(): Promise<void> | Initialize camera, WASM, and start scanning | | stop(): void | Stop scanning, release camera, remove DOM elements | | toggleTorch(): Promise<boolean> | Toggle flashlight, returns new state | | isRunning: boolean | Whether the scanner is currently active |

Framework Integration

The BarcodeScanner constructor has no side effects — it only stores configuration. This makes it safe to use with React strict mode, Vue lifecycle hooks, etc.

React Example

import { useEffect, useRef } from 'react';
import { BarcodeScanner } from 'web-wasm-barcode-reader';

function Scanner({ onScan }: { onScan: (data: string) => void }) {
  const mountRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const scanner = new BarcodeScanner({
      container: mountRef.current!,
      onDetect: (result) => onScan(result.data),
    });
    scanner.start();
    return () => scanner.stop();
  }, [onScan]);

  return <div ref={mountRef} style={{ width: 400, height: 400 }} />;
}

Recompiling the WASM Module

The public/a.out.js and public/a.out.wasm files are pre-compiled. To rebuild them, you need Emscripten and ZBar installed:

emcc scan.c \
  -s WASM=1 \
  -s EXPORTED_FUNCTIONS='["_scan_image", "_create_buffer", "_destroy_buffer"]' \
  -s EXPORTED_RUNTIME_METHODS='["cwrap", "UTF8ToString"]' \
  --js-library library.js \
  -lzbar \
  -o public/a.out.js

The exact flags may vary depending on your Emscripten version and ZBar installation path.

Scripts

| Script | Command | Description | |---|---|---| | dev | npm run dev | Start Vite dev server with HMR | | build | npm run build | Type-check + production build to dist/ | | preview | npm run preview | Serve the production build locally |

Browser Requirements

  • getUserMedia (camera access) — all modern browsers
  • WebAssembly — all modern browsers
  • OffscreenCanvas — Chrome 69+, Firefox 105+, Safari 16.4+
  • HTTPS or localhost (required for camera access)

License

See the ZBar license for the scanning library. The JavaScript/TypeScript wrapper code in this repository is unlicensed.