A fully production-grade, single-file educational website that explains the complete engineering lifecycle of a barcode scanner — from laser photons to decoded product numbers — with a live real-time camera scanner built in.
Open barcode-scanner.html in any modern browser (Chrome or Edge recommended).
| Section | Description |
|---|---|
| Hero | Animated scanner illustration with live laser beam + decoded output cycling |
| How It Works | 6-step process breakdown with hover cards |
| Reflection Demo | Live bar strip + analog waveform canvas animation |
| Live Decoder | Type any UPC-A code → full digit-by-digit binary decode |
| EAN-13 Anatomy | Visual digit map with colour-coded zones |
| Signal Flow | Animated 6-node pipeline diagram |
| Data Structures | 4 code cards with real implementation examples |
| Components | 6 hardware component cards with specs |
| 📷 Live Camera Scanner | Real-time barcode detection via webcam/phone camera |
- Full 6-step scan process: Laser → Reflection → Photodiode → ADC → Decoder → POS
- Physics layer: visual bar strip with animated laser cursor and live photodiode waveform
- EAN-13 digit structure breakdown (Country / Manufacturer / Product / Check Digit)
- Signal flow pipeline with animated data chips at each hardware stage
- Enter any 12-digit UPC-A code
- Renders the barcode visually using real L-code / R-code encoding
- Decodes each digit's 7-bit binary pattern in a table
- Validates the Mod-10 checksum in real time
- Updates on every keystroke
- Real-time camera feed with animated laser sweep and corner bracket targeting UI
- Dual detection engine:
- ZXing (
@zxing/library) — primary, wide format support - Native
BarcodeDetectorAPI — hardware-accelerated fallback (Chrome/Edge)
- ZXing (
- Supported formats: EAN-13, EAN-8, UPC-A, UPC-E, Code 39, Code 128, ITF, QR Code, DataMatrix, Codabar
- Green bounding box drawn on detected barcode location
- Audio beep on successful scan
- Full digit-by-digit breakdown for numeric barcodes (EAN/UPC)
- Scan history (last 8 scans) with click-to-replay
- Front/back camera switch
- Pre-flight
navigator.permissions.query({ name: 'camera' })check on page load - Contextual step-by-step fix guide shown inside the viewfinder for each error type:
| Error | Shown Guide |
|---|---|
NotAllowedError |
Chrome address bar → 🔒 icon → Camera → Allow |
file:// protocol |
Instructs to serve via localhost using a local server |
NotFoundError |
"No camera hardware found" message |
NotReadableError |
Lists apps to close (Zoom, Teams, Meet) |
| Unsupported browser | Chrome/Edge recommendation |
- ↻ Retry Camera button resets and retries
getUserMediawithout page reload - Info note below controls updates dynamically with permission state
| Structure | Used For |
|---|---|
| HashMap / Lookup Table | UPC digit encoding (7-bit pattern → decimal) |
| Ring Buffer (Array) | ADC bitstream circular buffer |
| Stack | Guard pattern detector (start / middle / end guards) |
| Array + Modular Arithmetic | Mod-10 checksum validator |
barcode-scanner.html ← Entire project — single self-contained file
README.md ← This file
The entire application is a single HTML file with no build step, no bundler, no framework. CSS, JS, and HTML are all inline.
# Just double-click barcode-scanner.html
# OR open it via your browser's File menu
⚠️ Camera will be blocked by Chrome onfile://— use Option 2 for the live scanner.
Python (built-in):
python3 -m http.server 8080
# Then open: http://localhost:8080/barcode-scanner.htmlNode.js (npx):
npx serve .
# Then open the URL shown in terminalVS Code:
Install the Live Server extension → right-click barcode-scanner.html → Open with Live Server
| Browser | Camera Scanner | Decoder | Animations |
|---|---|---|---|
| Chrome 88+ | ✅ Full support | ✅ | ✅ |
| Edge 88+ | ✅ Full support | ✅ | ✅ |
| Firefox | ✅ | ✅ | |
| Safari 17+ | ✅ | ✅ | |
| Mobile Chrome | ✅ (back camera) | ✅ | ✅ |
The live scanner requires HTTPS or localhost. It will not work over plain
http://on remote servers due to browser security policy.
All loaded via CDN — no local installation needed.
| Library | Version | Purpose |
|---|---|---|
@zxing/library |
0.21.3 |
Primary barcode detection engine |
JetBrains Mono |
Google Fonts | Monospace code/data typography |
Syne |
Google Fonts | Display/heading typography |
Camera Feed (getUserMedia)
│
▼
Frame Capture (canvas drawImage every ~300ms)
│
├──► ZXing BrowserMultiFormatReader.decodeFromCanvas()
│ └─ Tries all formats in hints map
│
└──► BarcodeDetector.detect(canvas) [fallback]
└─ Native browser API, hardware-accelerated
│
▼
onBarcodeDetected(code, format, cornerPoints)
│
├─ Debounce: skip if same code seen < 2 seconds ago
├─ drawDetectionBox(cornerPoints) → canvas overlay
├─ playBeep() → AudioContext oscillator
├─ setScannerState('success') → UI feedback
├─ showDecodedResult(code, format)
├─ drawBreakdown(code) → digit grid + checksum
└─ addToHistory(code, format)
// Left-side L-code (digits 1–6 of UPC-A)
const UPC_L = {
0: '0001101', 1: '0011001', 2: '0010011',
3: '0111101', 4: '0100011', 5: '0110001',
6: '0101111', 7: '0111011', 8: '0110111',
9: '0001011'
};
// Right-side R-code (digits 7–12 of UPC-A) — complement of L-code
const UPC_R = {
0: '1110010', 1: '1100110', 2: '1101100',
3: '1000010', 4: '1011100', 5: '1001110',
6: '1010000', 7: '1000100', 8: '1001000',
9: '1110100'
};function validateChecksum(digits) {
const weights = [3,1,3,1,3,1,3,1,3,1,3]; // alternating for UPC-A
let sum = 0;
for (let i = 0; i < 11; i++) sum += digits[i] * weights[i];
return (10 - (sum % 10)) % 10 === digits[11];
}| Token | Value | Usage |
|---|---|---|
--bg |
#050a0e |
Page background |
--laser |
#00ff88 |
Primary accent, laser green |
--laser2 |
#00e5ff |
Secondary accent, cyan |
--red-laser |
#ff3c3c |
Laser beam colour, errors |
--gold |
#ffd166 |
Warnings, highlights |
--muted |
#6b8fa0 |
Secondary text |
Fonts: Syne (headings/UI) + JetBrains Mono (code/data)
| Concept | Where Covered |
|---|---|
| Laser physics (650nm, Class 2) | Hero section + Components |
| Photodiode + TIA amplifier | Signal Flow node 3 |
| ADC + Schmitt trigger | Signal Flow node 4 + Step 4 |
| Guard patterns (101, 01010, 101) | Data Structures — Stack card |
| Module width timing | Step 5 — Pattern Decoding |
| USB HID keyboard emulation | Step 6 + Signal Flow node 6 |
| EAN-13 vs UPC-A structure | EAN-13 Anatomy section |
| Mod-10 check digit | Data Structures — Checksum card |
Change the hero barcode pattern:
// In the script section, edit heroPattern array
const heroPattern = [2,1,2,1,1,3,1,2,1,1,1,2,3,...];Add more barcode formats to ZXing:
const formats = [
ZXing.BarcodeFormat.EAN_13,
ZXing.BarcodeFormat.PDF_417, // add PDF-417
ZXing.BarcodeFormat.AZTEC, // add Aztec
// ...
];Adjust scan throttle rate (default 300ms):
if (now - lastScan < 300) return; // change 300 to desired msBuilt for educational purposes. Free to use, share, and modify.
BARCODELAB — From photons to product codes.