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 🙏

© 2025 – Pkg Stats / Ryan Hefner

bs-barcode-scanner

v0.1.16

Published

A React barcode scanner component with WASM support for ZXing and Photon image processing. Includes IIFE standalone build for direct browser use.

Readme

🔍 bs-barcode-scanner

npm version License: MIT

A powerful, high-performance barcode and QR code scanner library with WebAssembly support. Features ZXing-WASM for maximum compatibility and Photon-WASM for advanced image processing. Provides a familiar API with standalone browser support.

✨ Features

  • 🚀 Zero Dependencies Runtime - Standalone IIFE build works directly in browsers
  • 🎯 Multiple Barcode Formats - Supports 13+ barcode types including QR, EAN, UPC, Code128, and more
  • WebAssembly Powered - Uses ZXing-WASM and Photon-WASM for blazing-fast processing
  • 🔧 Worker Pool Support - Multi-threaded processing for optimal performance
  • 📸 Advanced Camera Controls - Torch toggle, camera switching, tap-to-focus
  • 🎨 Customizable UI - Multiple GUI styles (laser, viewfinder, none)
  • 🖼️ Intelligent Image Processing - Adaptive strategies including grayscale, contrast enhancement, edge detection
  • 💾 Frame Caching - Smart frame deduplication to avoid redundant processing
  • 🎛️ Detailed Logging - Uses console.log, console.debug, and console.trace for different verbosity levels

📦 Installation

npm install bs-barcode-scanner

Or with yarn:

yarn add bs-barcode-scanner

🚀 Quick Start

Standalone Browser Usage (IIFE)

The simplest way to get started - no build tools required!

Using CDN

<!DOCTYPE html>
<html>
<head>
  <title>Barcode Scanner</title>
</head>
<body>
  <div id="scanner-container" style="width: 640px; height: 480px;"></div>

  <!-- Load from CDN -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/standalone.js"></script>
  
  <script>
    // Access from global variable
    const { BarcodePicker, ScanSettings, Symbology, GuiStyle } = window.FastBarcodeScanner;

    // Configure scan settings
    const settings = new ScanSettings();
    settings.enableSymbologies([
      Symbology.QR,
      Symbology.EAN13,
      Symbology.CODE128
    ]);
    settings.setCodeDuplicateFilter(1000); // Filter duplicates within 1 second

    // Create the scanner
    BarcodePicker.create(document.getElementById('scanner-container'), {
      scanSettings: settings,
      guiStyle: GuiStyle.LASER,
      playSoundOnScan: true,
      vibrateOnScan: true
    }).then(picker => {
      // Handle scan results
      picker.onDidScan((result) => {
        result.barcodes.forEach(barcode => {
          console.log(`Scanned: ${barcode.data} (${barcode.symbology})`);
        });
      });

      // Start scanning
      picker.resumeScanning();
    });
  </script>
</body>
</html>

Using Local Installation

<!DOCTYPE html>
<html>
<head>
  <title>Barcode Scanner</title>
</head>
<body>
  <div id="scanner-container" style="width: 640px; height: 480px;"></div>

  <!-- Load the standalone bundle -->
  <script src="./node_modules/bs-barcode-scanner/dist/standalone.js"></script>
  
  <script>
    // Access from global variable
    const { BarcodePicker, ScanSettings, Symbology, GuiStyle } = window.FastBarcodeScanner;

    // Configure scan settings
    const settings = new ScanSettings();
    settings.enableSymbologies([
      Symbology.QR,
      Symbology.EAN13,
      Symbology.CODE128
    ]);
    settings.setCodeDuplicateFilter(1000); // Filter duplicates within 1 second

    // Create the scanner
    BarcodePicker.create(document.getElementById('scanner-container'), {
      scanSettings: settings,
      guiStyle: GuiStyle.LASER,
      playSoundOnScan: true,
      vibrateOnScan: true
    }).then(picker => {
      // Handle scan results
      picker.onDidScan((result) => {
        result.barcodes.forEach(barcode => {
          console.log(`Scanned: ${barcode.data} (${barcode.symbology})`);
        });
      });

      // Start scanning
      picker.resumeScanning();
    });
  </script>
</body>
</html>

Module Usage (ES6/TypeScript)

import { 
  BarcodePicker, 
  ScanSettings, 
  Symbology, 
  GuiStyle
} from 'bs-barcode-scanner';

// Configure scan settings
const settings = new ScanSettings();
settings.enableSymbologies([
  Symbology.QR,
  Symbology.EAN13,
  Symbology.EAN8,
  Symbology.CODE128,
  Symbology.DATA_MATRIX
]);
settings.setMaxNumberOfCodesPerFrame(3);
settings.setCodeDuplicateFilter(1500);

// Create scanner instance
const container = document.getElementById('scanner-container');
const picker = await BarcodePicker.create(container, {
  scanSettings: settings,
  guiStyle: GuiStyle.VIEWFINDER,
  playSoundOnScan: true,
  vibrateOnScan: true,
  enableTorchToggle: true,
  enableCameraSwitcher: true,
  enableTapToFocus: true,
  useWorkers: true, // Enable multi-threaded processing
  workerPoolConfig: {
    workerCount: 4,
    workerScriptPath: './node_modules/bs-barcode-scanner/dist/barcode-worker.js'
  }
});

// Handle scan events
picker.onDidScan((result) => {
  result.barcodes.forEach(barcode => {
    console.log(`Code: ${barcode.data}`);
    console.log(`Format: ${barcode.symbology}`);
    console.log(`Raw data:`, barcode.rawData);
  });
});

// Control scanning
picker.resumeScanning();
// picker.pauseScanning();
// picker.stopScanning();

📚 API Reference

BarcodePicker

The main scanner component.

Static Methods

BarcodePicker.create(element, options?)

Creates a new barcode picker instance.

Parameters:

  • element: HTMLElement - Container element for the scanner
  • options?: BarcodePickerOptions - Configuration options

Returns: Promise<BarcodePicker>

Example:

const picker = await BarcodePicker.create(container, {
  scanSettings: new ScanSettings(),
  guiStyle: GuiStyle.LASER,
  playSoundOnScan: true
});

Instance Methods

onDidScan(callback)

Register a callback for successful scans.

Parameters:

  • callback: (result: ScanResult) => void - Function called when barcodes are detected
picker.onDidScan((result) => {
  result.barcodes.forEach(barcode => {
    console.log(barcode.data);
  });
});
onScan(callback)

Register a callback with session information.

Parameters:

  • callback: (session: ScanSession) => void - Function called with scan session
picker.onScan((session) => {
  console.log('Newly recognized codes:', session.newlyRecognizedCodes);
});
onError(callback)

Register an error callback.

Parameters:

  • callback: (error: Error) => void - Function called on errors
resumeScanning()

Start or resume barcode scanning.

pauseScanning()

Pause barcode scanning without releasing the camera.

stopScanning()

Stop scanning and release camera resources.

destroy()

Clean up all resources and remove the scanner.

setTorchEnabled(enabled)

Toggle the camera flash/torch.

Parameters:

  • enabled: boolean - True to turn on, false to turn off

Returns: Promise<BarcodePicker>

setActiveCamera(camera, cameraSettings?)

Switch to a specific camera.

Parameters:

  • camera: { deviceId?: string } - Camera to activate
  • cameraSettings?: CameraSettings - Optional camera settings

Returns: Promise<BarcodePicker>

getActiveCamera()

Get information about the currently active camera.

Returns: { deviceId?: string, label: string, cameraType: string } | undefined

ScanSettings

Configuration for barcode detection.

Methods

enableSymbologies(symbologies)

Enable specific barcode types.

Parameters:

  • symbologies: string[] - Array of symbology constants
const settings = new ScanSettings();
settings.enableSymbologies([
  Symbology.QR,
  Symbology.EAN13,
  Symbology.CODE128
]);
setSymbologyEnabled(symbology, enabled)

Enable or disable a specific barcode type.

Parameters:

  • symbology: string - Symbology constant
  • enabled: boolean - Enable or disable
setCodeDuplicateFilter(milliseconds)

Set the duplicate detection window.

Parameters:

  • milliseconds: number - Time window for filtering duplicates (default: 500)
setMaxNumberOfCodesPerFrame(max)

Set maximum barcodes to detect per frame.

Parameters:

  • max: number - Maximum number of codes (default: 1)
setActiveScanningArea(rect)

Restrict scanning to a specific area.

Parameters:

  • rect: { x: number, y: number, width: number, height: number } - Scanning area (normalized 0-1)
settings.setActiveScanningArea({
  x: 0.1,
  y: 0.3,
  width: 0.8,
  height: 0.4
});

Supported Barcode Types

The Symbology object provides constants for all supported barcode formats:

Symbology.EAN13        // EAN-13
Symbology.EAN8         // EAN-8
Symbology.UPCA         // UPC-A
Symbology.UPCE         // UPC-E
Symbology.CODE128      // Code 128
Symbology.CODE39       // Code 39
Symbology.CODE93       // Code 93
Symbology.ITF          // Interleaved 2 of 5
Symbology.CODABAR      // Codabar
Symbology.QR           // QR Code
Symbology.DATA_MATRIX  // Data Matrix
Symbology.AZTEC        // Aztec
Symbology.PDF417       // PDF417

GUI Styles

Control the scanner overlay appearance:

GuiStyle.NONE        // No overlay
GuiStyle.LASER       // Laser line overlay
GuiStyle.VIEWFINDER  // Rectangular viewfinder with rounded corners

🔧 Configuration Options

BarcodePickerOptions

interface BarcodePickerOptions {
  accessCamera?: boolean;              // Request camera access (default: true)
  camera?: {
    deviceId?: string;                 // Specific camera device ID
  };
  cameraSettings?: {
    resolutionPreference?: string;     // Resolution preference
  };
  enableCameraSwitcher?: boolean;      // Show camera switch button (default: true)
  enableTapToFocus?: boolean;          // Enable tap-to-focus (default: true)
  enableTorchToggle?: boolean;         // Show torch toggle button (default: true)
  guiStyle?: number;                   // GUI style (default: GuiStyle.LASER)
  playSoundOnScan?: boolean;           // Play beep on scan (default: false)
  scanSettings?: ScanSettings;         // Scan configuration
  scanningPaused?: boolean;            // Start in paused state (default: false)
  vibrateOnScan?: boolean;             // Vibrate on scan (default: false)
  visible?: boolean;                   // Initial visibility (default: true)
  useWorkers?: boolean;                // Enable worker pool (default: true)
  workerPoolConfig?: {
    workerCount?: number;              // Number of workers (default: 4)
    workerScriptPath?: string;         // Path to worker script
  };
}

🎯 Advanced Usage

Worker Pool for Multi-threaded Processing

Enable worker pool for optimal performance:

const picker = await BarcodePicker.create(container, {
  useWorkers: true,
  workerPoolConfig: {
    workerCount: 4,
    workerScriptPath: './dist/barcode-worker.js'
  }
});

Custom Scanning Area

Focus scanning on a specific region:

const settings = new ScanSettings();
settings.setActiveScanningArea({
  x: 0.1,      // 10% from left
  y: 0.3,      // 30% from top
  width: 0.8,  // 80% of width
  height: 0.4  // 40% of height
});

Handling Multiple Codes

Detect multiple barcodes in a single frame:

const settings = new ScanSettings();
settings.setMaxNumberOfCodesPerFrame(5);

picker.onDidScan((result) => {
  console.log(`Found ${result.barcodes.length} codes:`);
  result.barcodes.forEach((barcode, index) => {
    console.log(`${index + 1}. ${barcode.data} (${barcode.symbology})`);
  });
});

Error Handling

picker.onError((error) => {
  console.error('Scanner error:', error);
  if (error.name === 'NotAllowedError') {
    alert('Camera access denied. Please grant permission.');
  } else if (error.name === 'NotFoundError') {
    alert('No camera found on this device.');
  }
});

Camera Control

// Toggle torch/flash
await picker.setTorchEnabled(true);

// Get currently active camera information
const activeCameraInfo = picker.getActiveCamera();
console.log('Active camera info:', activeCameraInfo);

// Switch to specific camera
await picker.setActiveCamera({ deviceId: 'your-camera-device-id' });

// Apply camera settings
await picker.applyCameraSettings({ resolutionPreference: 'hd' });

Session Management

picker.onScan((session) => {
  const codes = session.newlyRecognizedCodes;
  
  // Process new codes
  codes.forEach(code => {
    console.log('New code:', code.data);
  });
  
  // Control scanning flow
  if (codes.length > 0) {
    session.pauseScanning();
    // Do something with the code...
    setTimeout(() => session.resumeScanning(), 2000);
  }
});

🏗️ Build Configuration

Building from Source

# Install dependencies
npm install

# Build the standalone library
npm run build

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Lint code
npm run lint
npm run lint:fix

Build Output

The build process creates:

  • dist/standalone.js - Main IIFE bundle for browsers
  • dist/standalone.d.ts - TypeScript type definitions
  • dist/barcode-worker.js - Web Worker script for multi-threaded processing
  • WASM files are bundled from dependencies

🧪 Testing

The library includes comprehensive test coverage:

# Run all tests
npm test

# Watch mode for development
npm run test:watch

# Generate coverage report
npm run test:coverage

Test files are located in __tests__/:

  • legacy-barcode-scanner.test.ts - Main scanner tests
  • barcode-image-processor.test.ts - Image processing tests
  • worker-pool.test.ts - Worker pool tests
  • standalone-iife.test.ts - Standalone build tests

🌐 Browser Compatibility

  • Chrome/Edge: Full support with Web Workers
  • Firefox: Full support with Web Workers
  • Safari: Full support (iOS 14.3+, macOS 11+)
  • Mobile: Full support on iOS and Android

Requirements:

  • WebAssembly support
  • getUserMedia API for camera access
  • ES6+ JavaScript support

📝 Example Projects

Basic HTML Example

See simple-example.html for a complete standalone example.

To run the example:

# Start a local server
python3 -m http.server 8000
# or
npx serve

# Open browser
# Navigate to http://localhost:8000/simple-example.html

React Integration

import { useEffect, useRef } from 'react';
import { BarcodePicker, ScanSettings, Symbology } from 'bs-barcode-scanner';

function BarcodeScanner() {
  const containerRef = useRef<HTMLDivElement>(null);
  const pickerRef = useRef<BarcodePicker | null>(null);

  useEffect(() => {
    const initScanner = async () => {
      if (!containerRef.current) return;

      const settings = new ScanSettings();
      settings.enableSymbologies([Symbology.QR, Symbology.EAN13]);

      const picker = await BarcodePicker.create(containerRef.current, {
        scanSettings: settings,
        guiStyle: 1
      });

      picker.onDidScan((result) => {
        console.log('Scanned:', result.barcodes);
      });

      pickerRef.current = picker;
    };

    initScanner();

    return () => {
      pickerRef.current?.destroy();
    };
  }, []);

  return <div ref={containerRef} style={{ width: '100%', height: '400px' }} />;
}

🔍 How It Works

Architecture

  1. Camera Access: Uses getUserMedia API to access device cameras
  2. Frame Capture: Continuously captures frames from the video stream
  3. Worker Pool: Distributes frame processing across multiple web workers
  4. Image Processing: Applies intelligent preprocessing strategies:
    • Resize for optimal performance
    • Grayscale conversion
    • Contrast enhancement
    • Edge detection
    • Gaussian blur
    • Thresholding
  5. Barcode Detection: Uses ZXing-WASM for high-performance barcode recognition
  6. Caching: Smart frame deduplication to avoid redundant processing
  7. Result Delivery: Filters duplicates and delivers results via callbacks

Processing Strategies

The library uses multiple processing strategies in sequence:

  1. Original Frame: Direct processing without modifications
  2. Grayscale: Convert to grayscale for better contrast
  3. Contrast Enhancement: Improve barcode edge visibility
  4. Edge Detection: Emphasize barcode patterns
  5. Gaussian Blur + Threshold: Reduce noise and binarize
  6. Sharpen: Enhance barcode edges

Strategies are applied until a barcode is detected or all strategies are exhausted.

🤝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Guidelines

  • Write tests for new features
  • Follow the existing code style (use npm run lint)
  • Update documentation as needed
  • Ensure all tests pass (npm test)

📄 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

This library builds upon excellent open-source projects:

📞 Support


Made with ❤️ by Zoobean

If you find this library helpful, please consider giving it a ⭐ on GitHub!