Zero-dependency, single-file QR Code, Data Matrix & Barcode generator & reader for JavaScript.
Supports PNG, SVG, WEBP, HTML, ASCII output — with rounded modules, finder pattern styling, logo, label, transparent background, built-in camera/image reader, Data Matrix ECC200 (all 6 encoding modes), and 1D barcode scanner (EAN-13, EAN-8, UPC-A, Code-128, Code-39, ITF).
czQR/
├── src/ # Source modules (concatenated by build.js)
│ ├── core.js # Class shell & constants
│ ├── utils.js # Shared helpers
│ ├── qr-encode.js # QR Code encoder
│ ├── qr-decode.js # QR Code decoder
│ ├── barcode-encode.js # 1D Barcode generator
│ ├── datamatrix-encode.js # Data Matrix ECC200 encoder
│ ├── datamatrix-decode.js # Data Matrix decoder & image reader
│ ├── barcode-decode.js # 1D Barcode decoder
│ ├── readall.js # Multi-code reader orchestrator
│ ├── markers.js # Detection marker overlay
│ └── closing.js # Class closing
├── czQR.js # Built — full with comments (development)
├── dist/
│ └── czQR.min.js # Minified — production ready (~105 KB)
├── docs/
│ ├── index.html # Demo — Generator (GitHub Pages)
│ ├── reader.html # Demo — Reader (GitHub Pages)
│ └── czQR.min.js # Minified copy for demo
├── build.js # Build script (concat + terser)
├── LICENSE
└── README.md
<script src="https://cdn.jsdelivr.net/gh/cyberzilla/czQR/dist/czQR.min.js"></script><!-- Development (full source, ~107 KB) -->
<script src="czQR.js"></script>
<!-- Production (minified, ~56 KB) -->
<script src="dist/czQR.min.js"></script>Just copy dist/czQR.min.js into your project — no build tools required.
<script src="dist/czQR.min.js"></script>
<script>
const qr = new czQR('https://github.com/cyberzilla/czQR', 'H', 2);
qr.size(400)
.colors('#000000', '#ffffff')
.moduleRadius(0.4);
// Append to DOM
document.body.appendChild(qr.render('canvas'));
// Async rendering (required for logo)
const canvas = await qr.renderAsync('canvas');
// Download
await qr.download('czqr.png', 'png');
</script><script src="dist/czQR.min.js"></script>
<script>
// From an image element
const result = czQR.readFromImage(imgElement);
console.log(result.data); // decoded text
// From ImageData (canvas)
const result2 = czQR.read(imageData);
// From camera (live scanning)
czQR.readFromCamera(videoElement, (result) => {
console.log('Scanned:', result.data);
});
</script>const qr = new czQR(data, ec = 'M', quietZone = 2, minVer = 1, maxVer = 40);| Parameter | Default | Description |
|---|---|---|
data |
(required) | Text, URL, or data to encode |
ec |
'M' |
Error correction level |
quietZone |
2 |
Number of modules for quiet zone (border padding) |
minVer |
1 |
Minimum QR version (1 = 21×21 modules) |
maxVer |
40 |
Maximum QR version (40 = 177×177 modules) |
| Level | Recovery | Recommendation |
|---|---|---|
'L' |
~7% | Maximum data capacity, no logo |
'M' |
~15% | General balance |
'Q' |
~25% | With text labels |
'H' |
~30% | Required for logo — highest damage tolerance |
Encoding mode is automatically selected based on data content:
| Mode | Efficiency | Pattern |
|---|---|---|
| Numeric | 3.3 bit/char | Digits 0-9 only |
| Alphanumeric | 5.5 bit/char | Digits, A-Z uppercase, $%*+-./: and space |
| Byte | 8 bit/char | All characters including UTF-8 |
You can use string literals or static constants:
// Both are equivalent:
new czQR('data', 'H');
new czQR('data', czQR.EC_H);| Constant | Value |
|---|---|
czQR.EC_L |
'L' |
czQR.EC_M |
'M' |
czQR.EC_Q |
'Q' |
czQR.EC_H |
'H' |
| Constant | Value |
|---|---|
czQR.SHAPE_SQUARE |
'square' |
czQR.SHAPE_DOT |
'dot' |
czQR.SHAPE_DIAMOND |
'diamond' |
| Constant | Value |
|---|---|
czQR.FMT_CANVAS |
'canvas' |
czQR.FMT_PNG |
'png' |
czQR.FMT_SVG |
'svg' |
czQR.FMT_WEBP |
'webp' |
czQR.FMT_HTML |
'html' |
czQR.FMT_ASCII |
'ascii' |
czQR.FMT_DATAURI |
'datauri' |
czQR.FMT_BASE64 |
'base64' |
czQR.FMT_IMGTAG |
'imgtag' |
| Constant | Value | Description |
|---|---|---|
czQR.MODE_NUMBER |
1 |
Numeric only |
czQR.MODE_ALPHA_NUM |
2 |
Alphanumeric |
czQR.MODE_8BIT_BYTE |
4 |
Byte / UTF-8 |
czQR.MODE_KANJI |
8 |
Kanji |
All setters support method chaining and return this.
qr.size(600); // Output 600×600 pxqr.colors('#1a1a2e', '#ffffff'); // Custom colors
qr.colors('#1a1a2e', 'transparent'); // Transparent background (PNG/Canvas)qr.moduleRadius(0.0); // Sharp corners (default)
qr.moduleRadius(0.25); // Slightly rounded
qr.moduleRadius(0.5); // Full circleqr.moduleShape('square'); // Default — square
qr.moduleShape('dot'); // Separate circles with gap
qr.moduleShape('diamond'); // Diamond shape (45° rotation)Note: Finder patterns ("eyes") are not affected by shape — they use rounded rects via
finderStyle().
qr.finderStyle('#e74c3c', '#3498db', 0.5, 0.5);| Parameter | Description |
|---|---|
outerColor |
Outer frame color (null = follow foreground) |
innerColor |
Center dot color (null = follow foreground) |
outerRadius |
Frame radius (0.0–0.5) |
innerRadius |
Dot radius (0.0–0.5) |
Embed a logo in the center of the QR code. Use EC Level 'H'.
qr.logo('logo.png', 0.25, 10, 20);
// ratio: 25% of QR size
// padding: 10px around logo
// radius: 20% rounded cornersLogo rendering is async. Use
renderAsync()ordownload().
Logo alpha channel is preserved automatically.
qr.label('SCAN ME', 0.08, '#333', 'Arial, sans-serif', false);| Parameter | Default | Description |
|---|---|---|
text |
— | Label text |
size |
0.1 |
Font size ratio relative to QR size |
color |
'#000' |
Text color |
fontFamily |
'Inter, Arial, sans-serif' |
CSS font-family |
strip |
false |
Full-width background strip |
Note: Logo and label are mutually exclusive. Calling
logo()automatically clears the label, and vice versa.
qr.quality(80); // WEBP quality 0-100 (default 85)qr.scalable(true); // SVG without width/height (responsive)qr.accessibility('QR Code', 'Link to GitHub'); // SVG <title> and <desc>qr.margin(3); // ASCII art margin (default 2)qr.render(format); // Synchronous — returns string/Canvas
await qr.renderAsync(format); // Async — required for logo on canvas
await qr.download(filename, format); // Download file via Blob| Format | Return | Description |
|---|---|---|
'canvas' |
HTMLCanvasElement | DOM element |
'png' |
Data URI | Default bitmap |
'svg' |
SVG string | Vector, scalable |
'webp' |
Data URI | Smaller than PNG |
'html' |
HTML string | For email templates |
'ascii' |
ASCII string | For terminal/CLI |
'datauri' |
data:image/... |
Inline base64 |
'base64' |
Base64 string | Without data: prefix |
'imgtag' |
<img> tag |
Inline GIF/PNG |
Decode QR code from an ImageData object (e.g., from canvas).
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const result = czQR.read(imageData);
if (result) console.log(result.data);Decode QR code from an <img> element or Image object.
const img = document.getElementById('myImage');
const result = czQR.readFromImage(img);
if (result) console.log(result.data);Live camera scanning with continuous decode.
czQR.readFromCamera(videoEl, (result) => {
console.log('Decoded:', result.data);
});Decode QR code directly from a <canvas> element.
const result = czQR.readFromCanvas(myCanvas);
if (result) console.log(result.data);- Multi-pass pipeline — upscale, dilation, blur, multi-binarizer for styled QR codes
- Styled QR support — reads dot, diamond, rounded, and logo QR codes
- Adaptive binarizer — neighbor threshold propagation (HybridBinarizer)
- Reed-Solomon error correction — Sugiyama algorithm, corrects up to 30% damage (EC=H)
- Perspective transform — handles tilted/skewed QR codes from camera
- Transparent background — alpha compositing support
Read 1D barcode from an ImageData, <canvas>, or <img> element.
const result = czQR.readBarcode(imgElement);
if (result) {
console.log(result.data); // "4006381333931"
console.log(result.format); // "ean13"
console.log(result.type); // "1d"
console.log(result.checksumValid); // true
}
// Filter specific formats:
const result2 = czQR.readBarcode(imgElement, {
formats: ['code128', 'ean13']
});czQR.read() now automatically tries QR decoding first, then falls back to 1D barcode scanning:
const result = czQR.read(imgElement);
if (result) {
if (result.format === 'qr') {
console.log('QR Code:', result.data);
} else {
console.log(`Barcode (${result.format}):`, result.data);
}
}| Format | Constant | Description |
|---|---|---|
| EAN-13 | czQR.BC_EAN13 |
13-digit product barcode (global) |
| EAN-8 | czQR.BC_EAN8 |
8-digit compact product barcode |
| UPC-A | czQR.BC_UPCA |
12-digit product barcode (US/CA) |
| UPC-E | czQR.BC_UPCE |
8-digit compressed UPC |
| Code-128 | czQR.BC_CODE128 |
Full ASCII, logistics & shipping |
| Code-39 | czQR.BC_CODE39 |
Alphanumeric, industrial |
| ITF | czQR.BC_ITF |
Interleaved 2-of-5, carton labels |
| Codabar | czQR.BC_CODABAR |
Blood banks, libraries |
| GS1-128 | czQR.BC_GS1_128 |
Supply chain with AI identifiers |
| EAN-14 | czQR.BC_EAN14 |
14-digit trade item |
{
data: "4006381333931", // Decoded content
format: "ean13", // Format identifier
type: "1d", // "1d" for barcodes, "2d" for QR
checksumValid: true // Checksum verification result
}Read ALL QR codes and barcodes from a single image. Returns an array of results.
const results = czQR.readAll(imgElement);
// results = [
// { data: "https://example.com", format: "qr", type: "2d", version: 2 },
// { data: "4006381333931", format: "ean13", type: "1d", checksumValid: true },
// { data: "ABC-1234", format: "code39", type: "1d", checksumValid: true }
// ]
for (const r of results) {
console.log(`[${r.format}] ${r.data}`);
}Supports: multiple QR codes side by side, QR + barcode mixed, multiple barcodes, Data Matrix.
Generate a Data Matrix ECC200 barcode.
const svg = czQR.dataMatrix('Hello World', {
size: 300,
fg: '#000000',
bg: '#ffffff',
margin: 1,
output: 'svg' // 'svg' | 'canvas' | 'png' | 'datauri'
});
document.getElementById('dm').innerHTML = svg;The encoder automatically selects the optimal encoding mode (or mix of modes) via dynamic programming:
| Mode | Optimal For | Efficiency |
|---|---|---|
| ASCII | Digits, mixed content | 1 CW/char, digit pairs = 1 CW/2 chars |
| C40 | Uppercase + digits + space | 3 chars → 2 CW |
| TEXT | Lowercase + digits + space | 3 chars → 2 CW |
| X12 | ANSI X12 (*, >, CR, uppercase) |
3 chars → 2 CW |
| EDIFACT | Characters 32–94 | 4 chars → 3 CW |
| Base256 | Binary data (bytes > 127) | 1 CW/byte + length prefix |
24 standard square sizes from 10×10 (3 data CW) to 144×144 (1558 data CW).
Detect and decode a Data Matrix from an ImageData object.
const result = czQR.readDataMatrix(imageData);
if (result) {
console.log(result.data); // "Hello World"
console.log(result.format); // "datamatrix"
console.log(result.modes); // ["ASCII", "C40"]
console.log(result.symbol); // "18x18"
}Decode from a known 2D module grid (array of arrays, 1=dark, 0=light).
const result = czQR.decodeDataMatrix(moduleGrid);
// { data: "...", modes: ["ASCII", "TEXT"], symbol: "22x22" }{
data: "Hello World", // Decoded content
format: "datamatrix", // Format identifier
type: "2d", // "2d" for Data Matrix
modes: ["ASCII", "C40"], // Encoding modes detected
symbol: "18x18", // Symbol dimensions
bounds: { x, y, w, h } // Detection bounds (image reader)
}Data Matrix is also detected by czQR.readAll() alongside QR codes and barcodes.
const qr = new czQR('https://github.com/cyberzilla/czQR', 'M', 2);
document.body.appendChild(qr.size(400).render('canvas'));const qr = new czQR('https://github.com/cyberzilla/czQR', 'H', 2);
qr.size(600)
.colors('#000', '#fff')
.moduleRadius(0.4)
.logo('logo.png', 0.25, 10, 30);
const canvas = await qr.renderAsync('canvas');
document.body.appendChild(canvas);const qr = new czQR('https://github.com/cyberzilla/czQR', 'M', 2);
qr.size(400).colors('#000', 'transparent');
const canvas = await qr.renderAsync('canvas');const qr = new czQR('https://github.com/cyberzilla/czQR', 'H', 2);
qr.size(600)
.moduleRadius(0.4)
.finderStyle('#e74c3c', '#3498db', 0.5, 0.5)
.render('canvas');const qr = new czQR('https://github.com/cyberzilla/czQR', 'H', 2);
qr.size(500)
.moduleShape('dot')
.finderStyle('#e74c3c', '#3498db', 0.5, 0.5);
document.body.appendChild(qr.render('canvas'));const qr = new czQR('https://github.com/cyberzilla/czQR', 'Q', 2);
qr.size(500)
.moduleRadius(0.3)
.label('SCAN ME', 0.08, '#333', 'Arial, sans-serif', true);
document.body.appendChild(qr.render('canvas'));const qr = new czQR('https://github.com/cyberzilla/czQR', 'H', 2);
qr.size(600).colors('#1a1a2e', '#fff');
await qr.download('czqr.png', 'png');
await qr.download('czqr.svg', 'svg');
await qr.download('czqr.webp', 'webp');const img = new Image();
img.onload = () => {
const result = czQR.readFromImage(img);
if (result) {
console.log('Decoded:', result.data);
console.log('Version:', result.version);
console.log('EC Level:', result.ecLevel);
}
};
img.src = 'qrcode.png';qr.isDark(row, col) // Boolean — dark/light module
qr.getModuleCount() // Number — total dimension (with quiet zone)
qr.getRawModuleCount() // Number — dimension without quiet zone
qr.matrix() // 2D boolean array
qr.info() // Object — metadata (version, mode, utilization, etc.)| Feature | Support |
|---|---|
| PNG (Canvas) | ✅ |
| SVG | ✅ |
| WEBP (Canvas) | ✅ |
| HTML | ✅ |
| ASCII | ✅ |
| Transparent BG | ✅ |
| Module Shapes (square/dot/diamond) | ✅ |
| Module Radius | ✅ |
| Finder Styling | ✅ |
| Logo (raster + SVG) | ✅ (async) |
| Label + Strip | ✅ |
| SVG Scalable + Accessibility | ✅ |
| Data URI / Base64 | ✅ |
| Download (Blob) | ✅ |
| QR Reader (image) | ✅ |
| QR Reader (camera) | ✅ |
| Styled QR Reader | ✅ |
| RS Error Correction | ✅ (Sugiyama) |
| 1D Barcode Reader | ✅ (EAN-13, EAN-8, UPC-A, Code-128, Code-39, ITF) |
| Data Matrix Generator | ✅ (ECC200, all 6 encoding modes, DP optimizer) |
| Data Matrix Reader | ✅ (image + camera, mode detection, RS correction) |
| Auto-detect QR + Barcode + DM | ✅ |
| Multi-Code Reader | ✅ (readAll() — QR + barcodes + Data Matrix) |
| Demo | Live | File |
|---|---|---|
| Generator | cyberzilla.github.io/czQR | docs/index.html |
| Reader | cyberzilla.github.io/czQR/reader | docs/reader.html |
No server required — also works offline by opening the files directly in browser.
Modern browser with Canvas API (Chrome, Firefox, Safari, Edge).
MIT