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.
Maintainers
Readme
🔍 bs-barcode-scanner
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-scannerOr 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 scanneroptions?: 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 activatecameraSettings?: 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 constantenabled: 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 // PDF417GUI 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:fixBuild Output
The build process creates:
dist/standalone.js- Main IIFE bundle for browsersdist/standalone.d.ts- TypeScript type definitionsdist/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:coverageTest files are located in __tests__/:
legacy-barcode-scanner.test.ts- Main scanner testsbarcode-image-processor.test.ts- Image processing testsworker-pool.test.ts- Worker pool testsstandalone-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.htmlReact 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
- Camera Access: Uses
getUserMediaAPI to access device cameras - Frame Capture: Continuously captures frames from the video stream
- Worker Pool: Distributes frame processing across multiple web workers
- Image Processing: Applies intelligent preprocessing strategies:
- Resize for optimal performance
- Grayscale conversion
- Contrast enhancement
- Edge detection
- Gaussian blur
- Thresholding
- Barcode Detection: Uses ZXing-WASM for high-performance barcode recognition
- Caching: Smart frame deduplication to avoid redundant processing
- Result Delivery: Filters duplicates and delivers results via callbacks
Processing Strategies
The library uses multiple processing strategies in sequence:
- Original Frame: Direct processing without modifications
- Grayscale: Convert to grayscale for better contrast
- Contrast Enhancement: Improve barcode edge visibility
- Edge Detection: Emphasize barcode patterns
- Gaussian Blur + Threshold: Reduce noise and binarize
- 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:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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:
- ZXing-WASM - WebAssembly port of ZXing barcode library
- Photon - High-performance image processing library
- barcode-detector - Polyfill for Barcode Detection API
📞 Support
- Issues: GitHub Issues
- NPM Package: bs-barcode-scanner
- Repository: GitHub
Made with ❤️ by Zoobean
If you find this library helpful, please consider giving it a ⭐ on GitHub!
