quadqr-js 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1042 @@
1
+ # QuadQR (QQR)
2
+
3
+ <p align="center">
4
+ <img src="assets/banner.png?raw=true" alt="QuadQR banner">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>Four visible states. Two bits per data cell. A modern experiment in high-density matrix codes.</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://akanshsirohi.github.io/QuadQR/demo/"><strong>Try the Live Demo</strong></a>
13
+ ·
14
+ <a href="#current-benchmark">Benchmark</a>
15
+ ·
16
+ <a href="#getting-started">Use the Library</a>
17
+ ·
18
+ <a href="docs/README.md">Documentation</a>
19
+ ·
20
+ <a href="FORMAT.md">Format Specification</a>
21
+ </p>
22
+
23
+ **QuadQR** is an experimental open-source 2D matrix code that uses four visible data states instead of the two states used by a traditional black-and-white QR module.
24
+
25
+ Each QuadQR data cell represents exactly **2 bits**:
26
+
27
+ | Color | Bits |
28
+ |---|---|
29
+ | Red | `00` |
30
+ | Green | `01` |
31
+ | Blue | `10` |
32
+ | White | `11` |
33
+
34
+ That gives QuadQR a four-symbol alphabet and a raw density of **2 bits per data cell**.
35
+
36
+ QuadQR keeps the parts that make QR-like codes practical, such as a square matrix, finder patterns, timing structures, error correction, masking, perspective recovery, and camera scanning, while experimenting with a higher-density color-based data layer.
37
+
38
+ > **Important:** QuadQR is an experimental custom format. It is **not ISO QR Code**, and normal QR scanner apps cannot decode it.
39
+
40
+ ## Examples
41
+
42
+ Below are QuadQR symbols generated with different error-correction profiles. They use the same four-state RGBW data alphabet while varying the amount of space dedicated to Reed-Solomon protection.
43
+
44
+ <table>
45
+ <tr>
46
+ <td align="center">
47
+ <img src="assets/quadqr-v2-L.png?raw=true" alt="QuadQR example using ECC profile L" width="320"><br>
48
+ <sub><strong>ECC L</strong> · Higher payload capacity</sub>
49
+ </td>
50
+ <td align="center">
51
+ <img src="assets/quadqr-v2-M.png?raw=true" alt="QuadQR example using ECC profile M" width="320"><br>
52
+ <sub><strong>ECC M</strong> · More error-correction redundancy</sub>
53
+ </td>
54
+ </tr>
55
+ </table>
56
+
57
+ <p align="center">
58
+ <a href="https://akanshsirohi.github.io/QuadQR/demo/"><strong>Generate, scan, and benchmark QuadQR in your browser →</strong></a>
59
+ </p>
60
+
61
+ ---
62
+
63
+ ## Why I built QuadQR
64
+
65
+ I have always found QR codes fascinating. They are compact, practical, extremely well optimized, and they have continued to work reliably for decades.
66
+
67
+ But one thing caught my attention: the basic visual data representation is still binary. A module is essentially one of two states, black or white, representing `0` or `1`.
68
+
69
+ That made me wonder:
70
+
71
+ > If modern cameras, displays, image processing, and computing are much more capable today, can a QR-like matrix code reliably use more than two visible states and store more information in the same area?
72
+
73
+ I started researching the idea and working through the math. Four reliably distinguishable states are especially interesting because:
74
+
75
+ ```text
76
+ log2(4) = 2 bits
77
+ ```
78
+
79
+ So a four-state module can represent exactly two bits without requiring fractional-bit packing.
80
+
81
+ That eventually became QuadQR.
82
+
83
+ The current format uses **Red, Green, Blue, and White**, giving every data cell four possible states while keeping a square module grid that is easy to generate, sample, correct for perspective, and scan.
84
+
85
+ This project is an experiment in exploring how far a QR-inspired design can be pushed with modern hardware and software.
86
+
87
+ ### Development note
88
+
89
+ I designed the format, researched the approach, iterated on the encoding structure, and built the project as an independent experiment. **OpenAI Codex** helped significantly with some of the more complex implementation work, including parts of the codec, error correction, image-processing logic, testing, and optimization.
90
+
91
+ ---
92
+
93
+ ## What makes QuadQR different?
94
+
95
+ A traditional QR data module has two possible states:
96
+
97
+ ```text
98
+ Black
99
+ White
100
+ ```
101
+
102
+ That gives:
103
+
104
+ ```text
105
+ log2(2) = 1 bit per module
106
+ ```
107
+
108
+ QuadQR uses four data states:
109
+
110
+ ```text
111
+ Red
112
+ Green
113
+ Blue
114
+ White
115
+ ```
116
+
117
+ That gives:
118
+
119
+ ```text
120
+ log2(4) = 2 bits per data cell
121
+ ```
122
+
123
+ So at the raw data-cell level:
124
+
125
+ | Format | States per data cell | Raw information |
126
+ |---|---:|---:|
127
+ | Binary QR | 2 | 1 bit |
128
+ | QuadQR | 4 | 2 bits |
129
+
130
+ This is a **2× raw symbol-density advantage**.
131
+
132
+ ### Spectrum ECC: QuadQR-specific reliability without capacity loss
133
+
134
+ QuadQR now uses a second idea that is possible because the scanner already measures calibrated color rather than only black/white state: **confidence-aware Reed-Solomon recovery**.
135
+
136
+ For every sampled RGBW data module, the scanner keeps both the selected state and a confidence score based on the distance to the nearest and second-nearest calibrated color states. Four module confidences are combined into the confidence of their GF(256) byte symbol.
137
+
138
+ If normal hard-decision Reed-Solomon decoding fails, the least-confident byte symbols can be promoted to **known erasures**. Reed-Solomon can spend parity more efficiently on known erasure locations than on completely unknown errors. CRC-32 remains the final acceptance check.
139
+
140
+ QuadQR also applies a deterministic **spectral-spatial interleaver** after ECC. Neighboring logical codeword cells are scattered across distant physical data positions, so a scratch, glare patch, shadow, or localized print defect tends to affect many different RS symbols instead of destroying a contiguous run. The permutation is reversible and consumes **zero extra data cells**.
141
+
142
+ These features do **not** change the RGBW alphabet, module count, ECC parity count, or payload-capacity calculation. They improve how the existing redundancy is used.
143
+
144
+ However, raw cell density is not the same thing as final user-payload capacity. Finder patterns, timing structures, calibration cells, headers, CRC, masking, error correction, and other reserved cells all consume space.
145
+
146
+ That is why QuadQR includes its own benchmark instead of assuming that every completed symbol will contain exactly twice the payload of a standard QR code.
147
+
148
+ ---
149
+
150
+ ## Secure Payload v1
151
+
152
+ QuadQR now includes an **optional authenticated-encryption layer**. It sits above the matrix codec, so normal QuadQR symbols keep the same RGBW mapping, Spectrum ECC, scanner, and capacity behavior.
153
+
154
+ Two security modes are supported:
155
+
156
+ | Mode | Key source | Best suited for |
157
+ |---|---|---|
158
+ | Password | PBKDF2-HMAC-SHA-256 derives a 256-bit key from a password | Human-to-human protected payloads |
159
+ | Raw 256-bit key | Exact 32-byte random key supplied by the application | Apps, tickets, provisioning, enterprise scanners |
160
+
161
+ Both modes use **AES-256-GCM**, which provides encryption and authentication. A wrong password/key or modified encrypted payload fails decryption instead of returning silent garbage.
162
+
163
+ Password mode currently defaults to **600,000 PBKDF2-HMAC-SHA-256 iterations** with a random 16-byte salt. Raw-key mode avoids password-KDF work and automatically stores an 8-byte SHA-256 key fingerprint as a non-secret key ID unless disabled or overridden. The actual raw key is never embedded in the symbol.
164
+
165
+ The security envelope is versioned independently from the QuadQR matrix format so a future release can add another KDF or encryption mode without redesigning the RGBW/ECC layer.
166
+
167
+ Security is intentionally opt-in. Unencrypted codes remain the default.
168
+
169
+ The browser demo supports secure scanning in **both scanner paths**:
170
+
171
+ - **Image scanner:** upload/exported images and photographs are decoded normally; secure symbols are detected automatically and show the matching password or raw-key decrypt control inline.
172
+ - **Camera scanner:** live camera frames use the same secure-aware decode path. When an encrypted symbol is verified, scanning stops, the result is marked **Secure QR**, and plaintext is revealed only after successful authenticated decryption.
173
+
174
+ The scanner never expects the secret to be embedded in the QuadQR itself. Raw-key symbols expose only their non-secret key ID/fingerprint.
175
+
176
+ ---
177
+
178
+ ## Current benchmark
179
+
180
+ Run:
181
+
182
+ ```bash
183
+ npm run benchmark
184
+ ```
185
+
186
+ The benchmark compares QuadQR with standard QR **at the same matrix dimensions** using byte-mode QR reference capacities.
187
+
188
+ Current representative results at the project's `M` ECC profile:
189
+
190
+ | Version | Matrix | QuadQR payload | Standard QR payload | Gain | Ratio |
191
+ |---:|---:|---:|---:|---:|---:|
192
+ | 1 | 21×21 | **24 B** | 14 B | +10 B | **1.71×** |
193
+ | 2 | 25×25 | **48 B** | 26 B | +22 B | **1.85×** |
194
+ | 5 | 37×37 | **227 B** | 84 B | +143 B | **2.70×** |
195
+ | 10 | 57×57 | **630 B** | 213 B | +417 B | **2.96×** |
196
+ | 20 | 97×97 | **1992 B** | 666 B | +1326 B | **2.99×** |
197
+ | 30 | 137×137 | **4054 B** | 1370 B | +2684 B | **2.96×** |
198
+ | 40 | 177×177 | **6858 B** | 2331 B | +4527 B | **2.94×** |
199
+
200
+ ### How to read this table
201
+
202
+ For example:
203
+
204
+ ```text
205
+ Version 10
206
+ Matrix: 57×57
207
+
208
+ QuadQR: 630 bytes
209
+ Standard QR: 213 bytes
210
+
211
+ Gain: 417 bytes
212
+ Ratio: 2.96×
213
+ ```
214
+
215
+ This means that under the benchmark's current assumptions, a 57×57 QuadQR symbol can carry 630 user-payload bytes, while the standard QR byte-mode reference at the same dimensions and `M` label carries 213 bytes.
216
+
217
+ ### Important benchmark warning
218
+
219
+ The letters `L`, `M`, `Q`, and `H` in QuadQR are **project-defined ECC profiles**.
220
+
221
+ They do **not** currently claim the same standardized recovery percentages as ISO QR Code ECC levels with the same letters.
222
+
223
+ Therefore:
224
+
225
+ > The capacity benchmark is a same-dimension and same-label comparison, not yet an equal-damage-tolerance comparison.
226
+
227
+ The raw QuadQR data alphabet is exactly **2 bits per data cell**. Ratios approaching ~3× in the current usable-payload benchmark are caused by differences in total structural and ECC overhead between the two formats, not because a QuadQR cell contains 3 bits.
228
+
229
+ A future goal is to add **equal-reliability benchmarking**, where QuadQR and standard QR are compared after calibrating both to similar real-world damage recovery.
230
+
231
+ ---
232
+
233
+ ## Performance benchmark
234
+
235
+ The benchmark also measures direct codec performance.
236
+
237
+ It reports:
238
+
239
+ - payload size;
240
+ - automatically selected version;
241
+ - matrix dimensions;
242
+ - mean encode time;
243
+ - 95th percentile encode time;
244
+ - mean decode time;
245
+ - 95th percentile decode time.
246
+
247
+ Example output:
248
+
249
+ ```text
250
+ payload_B matrix version encode_mean encode_p95 decode_mean decode_p95
251
+ 24 21x21 v1 ...
252
+ 128 33x33 v4 ...
253
+ 512 53x53 v9 ...
254
+ 1024 73x73 v14 ...
255
+ 2048 101x101 v21 ...
256
+ ```
257
+
258
+ These timings measure the **matrix codec**, not the complete camera-scanning pipeline.
259
+
260
+ Camera scanning additionally includes finder detection, perspective correction, calibration, module sampling, and color classification.
261
+
262
+ For more stable local timing results, use more iterations:
263
+
264
+ ```bash
265
+ npm run benchmark -- --iterations=500
266
+ ```
267
+
268
+ For machine-readable results:
269
+
270
+ ```bash
271
+ npm run --silent benchmark -- --json
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Version 1 optimization
277
+
278
+ Small symbols have a difficult tradeoff because fixed metadata and ECC consume a much larger fraction of the matrix.
279
+
280
+ The original 21×21 QuadQR design could not fit a valid `M` payload because the protected framing itself was larger than the available data area.
281
+
282
+ Version 1 now uses a dedicated compact small-symbol profile.
283
+
284
+ At `M`:
285
+
286
+ ```text
287
+ 21×21 QuadQR
288
+ 24 bytes user payload
289
+ ```
290
+
291
+ compared with:
292
+
293
+ ```text
294
+ 21×21 standard QR reference
295
+ 14 bytes byte-mode payload
296
+ ```
297
+
298
+ Version 2 and above continue to use the normal framing and ECC structure.
299
+
300
+ ---
301
+
302
+ ## Encoding pipeline
303
+
304
+ The current QuadQR pipeline is:
305
+
306
+ ```text
307
+ payload bytes
308
+
309
+ optional Secure Payload v1 (AES-256-GCM)
310
+
311
+ header + CRC-32
312
+
313
+ GF(256) Reed-Solomon error correction
314
+
315
+ interleaved byte codewords
316
+
317
+ split every encoded byte into four 2-bit values
318
+
319
+ zero-overhead spectral-spatial permutation
320
+
321
+ 00 / 01 / 10 / 11
322
+
323
+ Red / Green / Blue / White
324
+
325
+ quaternary masking at physical positions
326
+
327
+ square QuadQR matrix
328
+ ```
329
+
330
+ Because four states map naturally to two bits, QuadQR does not require base-3 conversion or fractional-bit packing.
331
+
332
+ One encoded byte maps naturally to four data cells:
333
+
334
+ ```text
335
+ 8 bits ÷ 2 bits/cell = 4 cells
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Symbol structure
341
+
342
+ QuadQR currently uses:
343
+
344
+ - a square overall symbol;
345
+ - square modules;
346
+ - three 7×7 black-and-white finder patterns;
347
+ - black-and-white timing structures;
348
+ - one 5×5 primary alignment reference plus compact 3×3 secondary alignment markers on larger symbols;
349
+ - RGB calibration swatches;
350
+ - structural black/white references;
351
+ - a two-column zig-zag physical data-position path;
352
+ - deterministic spectral-spatial interleaving of logical codeword cells;
353
+ - four-state masking;
354
+ - GF(256) Reed-Solomon ECC with error + erasure decoding;
355
+ - per-module RGBW confidence for scanner-assisted erasures;
356
+ - CRC-32 integrity verification;
357
+ - optional versioned AES-256-GCM Secure Payload envelope.
358
+
359
+ White is a valid data state.
360
+
361
+ QuadQR still uses exactly three large finder patterns, just like standard QR. Starting at version 2, alignment markers follow the standard QR version-dependent center schedule. The bottom-right alignment reference remains a full 5×5 marker and is used as the fourth homography reference; additional distributed markers are compact 3×3 black rings with white centers. Version 1 keeps one QuadQR-specific 5×5 bottom-right bootstrap marker because it otherwise would have no fourth projective reference.
362
+
363
+ The decoder does not treat a white-looking area as automatically empty. It reconstructs the matrix geometry first and then determines whether a sampled position is structural or data.
364
+
365
+ ---
366
+
367
+ ## Color mapping
368
+
369
+ The current mapping is intentionally simple:
370
+
371
+ ```text
372
+ 00 → Red
373
+ 01 → Green
374
+ 10 → Blue
375
+ 11 → White
376
+ ```
377
+
378
+ Ideal display-space reference colors are conceptually:
379
+
380
+ ```text
381
+ Red → (255, 0, 0)
382
+ Green → ( 0, 255, 0)
383
+ Blue → ( 0, 0, 255)
384
+ White → (255, 255, 255)
385
+ ```
386
+
387
+ Real camera input is not expected to match those exact values.
388
+
389
+ QuadQR includes calibration and nearest-color classification so the scanner can work with observed colors after lighting, camera processing, perspective changes, and other image transformations.
390
+
391
+ ---
392
+
393
+ ## Reed-Solomon error correction
394
+
395
+ QuadQR uses Reed-Solomon over:
396
+
397
+ ```text
398
+ GF(2^8) = GF(256)
399
+ ```
400
+
401
+ Primitive polynomial:
402
+
403
+ ```text
404
+ x^8 + x^4 + x^3 + x^2 + 1
405
+ 0x11d
406
+ ```
407
+
408
+ One Reed-Solomon symbol is one byte.
409
+
410
+ Since one byte becomes four QuadQR data cells, ECC symbols stay naturally aligned with the RGBW representation.
411
+
412
+ ### Versions 2 through 40
413
+
414
+ | Profile | Parity bytes per body block | Correctable byte symbols per block |
415
+ |---|---:|---:|
416
+ | L | 12 | 6 |
417
+ | M | 24 | 12 |
418
+ | Q | 36 | 18 |
419
+ | H | 48 | 24 |
420
+
421
+ ### Version 1 compact profile
422
+
423
+ | Profile | Body parity bytes | Correctable body byte symbols |
424
+ |---|---:|---:|
425
+ | L | 4 | 2 |
426
+ | M | 8 | 4 |
427
+ | Q | 12 | 6 |
428
+ | H | 16 | 8 |
429
+
430
+ Version 1 also uses a compact 4-byte logical header protected by 4 Reed-Solomon parity bytes, correcting up to 2 damaged header byte symbols.
431
+
432
+ CRC-32 remains the final integrity check.
433
+
434
+ ---
435
+
436
+ ## Scanner pipeline
437
+
438
+ The image/camera scanner currently follows this general pipeline:
439
+
440
+ ```text
441
+ camera or image RGB frame
442
+
443
+ grayscale structural analysis
444
+
445
+ finder candidate detection
446
+
447
+ version hypothesis
448
+
449
+ primary alignment search
450
+
451
+ homography / perspective correction
452
+
453
+ distributed alignment-grid validation
454
+
455
+ module-grid reconstruction
456
+
457
+ RGB + structural black/white calibration
458
+
459
+ nearest-color classification + confidence scoring
460
+
461
+ four-state unmasking
462
+
463
+ reverse spectral-spatial permutation
464
+
465
+ protected header Reed-Solomon hard decode
466
+
467
+ confidence-guided erasure retry when needed
468
+
469
+ body deinterleaving + error/erasure Reed-Solomon decode
470
+
471
+ CRC-32 verification
472
+
473
+ payload
474
+ ```
475
+
476
+ An axis-aligned fallback is also available for clean generated images and simple inputs.
477
+
478
+ ---
479
+
480
+ ## Rendering styles
481
+
482
+ QuadQR keeps visual styling separate from the wire format. The encoded matrix is unchanged, so applications can choose a renderer without creating a new barcode format.
483
+
484
+ The current render profiles are:
485
+
486
+ | Style | Behavior |
487
+ |---|---|
488
+ | `classic` | Original fully solid square modules. |
489
+ | `depth` | Deterministically mixes normal and lightly faded data tiles with subtle edge shading for a layered/3D appearance. |
490
+ | `soft` | Uses rounded data tiles for a softer visual treatment. |
491
+ | `inset` | Uses narrow recessed edge lighting while preserving the exact encoded center color. |
492
+
493
+ Finder patterns, timing structures, alignment markers, and calibration cells stay **square and fully solid** in styled output so the visual treatment does not weaken the scanner's geometric/color references.
494
+
495
+ The `inset` style preserves the exact encoded R/G/B/W color around the center of every data module. Its recessed shading is confined to a narrow outer band, and white data modules remain pure white. This matches the scanner's center-sampling strategy and avoids visual effects bleeding into neighboring cells.
496
+
497
+ ```js
498
+ renderToCanvas(code, canvas, {
499
+ moduleSize: 12,
500
+ quietZone: 4,
501
+ style: "inset" // classic | depth | soft | inset
502
+ });
503
+ ```
504
+
505
+ The styling is deterministic. Generating the same matrix with the same style produces the same visual tile treatment rather than changing randomly on every render.
506
+
507
+ ---
508
+
509
+ ## Demo
510
+
511
+ **Live demo:** https://akanshsirohi.github.io/QuadQR/demo/
512
+
513
+ The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded.
514
+
515
+ ### Generator & Image Scanner
516
+
517
+ Use this tab to:
518
+
519
+ - enter text/data;
520
+ - generate a QuadQR code;
521
+ - select ECC;
522
+ - optionally encrypt using a password or raw 256-bit key;
523
+ - inspect version and capacity;
524
+ - download the generated image;
525
+ - scan an uploaded image.
526
+
527
+ ### Camera Scanner
528
+
529
+ The live camera scanner has its own dedicated tab.
530
+
531
+ This keeps the generator interface lightweight and prevents an always-visible video element from making the main page bulky.
532
+
533
+ The camera stream automatically stops when you leave the camera tab.
534
+
535
+ ### Benchmark
536
+
537
+ The benchmark tab provides an easier visual view of:
538
+
539
+ - capacity by matrix size;
540
+ - comparison with standard QR;
541
+ - byte gain;
542
+ - capacity ratio;
543
+ - codec timing.
544
+
545
+ ---
546
+
547
+ ## Getting started
548
+
549
+ QuadQR can be used as an npm package, directly from a CDN, or from this repository.
550
+
551
+ ### Requirements
552
+
553
+ - **Node.js 20.19+** for the published Node/CommonJS entry points
554
+ - npm for package workflows
555
+ - A modern browser for the demo and documentation site
556
+ - HTTPS or localhost for browser camera access
557
+
558
+ ### Install from npm
559
+
560
+ ```bash
561
+ npm install quadqr
562
+ ```
563
+
564
+ Core usage:
565
+
566
+ ```js
567
+ import { encodeText, decodeMatrix } from "quadqr";
568
+
569
+ const code = encodeText("Hello from QuadQR", { ecc: "M" });
570
+ const result = decodeMatrix(code.matrix);
571
+ console.log(result.text);
572
+ ```
573
+
574
+ Node PNG usage:
575
+
576
+ ```js
577
+ import { encodeText } from "quadqr";
578
+ import { savePNG, scanFile } from "quadqr/node";
579
+
580
+ const code = encodeText("Generated on Node.js");
581
+ await savePNG(code, "quadqr.png", { moduleSize: 12, quietZone: 4 });
582
+
583
+ const result = await scanFile("quadqr.png");
584
+ console.log(result.text);
585
+ ```
586
+
587
+ ### CDN / script tag
588
+
589
+ After the package is published to npm, the classic global build can be loaded from npm-backed CDNs:
590
+
591
+ ```html
592
+ <script src="https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/quadqr.min.js"></script>
593
+ <script>
594
+ const code = QuadQR.encodeText("Hello from a script tag");
595
+ </script>
596
+ ```
597
+
598
+ The same file is available through unpkg. Pin an exact version in production.
599
+
600
+ ### Clone for development
601
+
602
+ ```bash
603
+ git clone https://github.com/akanshsirohi/QuadQR.git
604
+ cd QuadQR
605
+ npm install
606
+ ```
607
+
608
+ Run the complete test suite:
609
+
610
+ ```bash
611
+ npm test
612
+ ```
613
+
614
+ Build the npm/CDN distribution and prebuilt WASM asset:
615
+
616
+ ```bash
617
+ npm run build
618
+ ```
619
+
620
+ Start the interactive demo:
621
+
622
+ ```bash
623
+ npm start
624
+ ```
625
+
626
+ Start the documentation site:
627
+
628
+ ```bash
629
+ npm run docs
630
+ ```
631
+
632
+ Run benchmarks:
633
+
634
+ ```bash
635
+ npm run benchmark
636
+ ```
637
+
638
+ Inspect exactly what npm will publish:
639
+
640
+ ```bash
641
+ npm run pack:check
642
+ ```
643
+
644
+ ### Optional WASM acceleration
645
+
646
+ The package ships a prebuilt WASM helper but never requires it.
647
+
648
+ ```js
649
+ import { initWasm } from "quadqr";
650
+
651
+ await initWasm();
652
+ ```
653
+
654
+ If WASM cannot load, the normal JavaScript codec remains available.
655
+
656
+ ---
657
+
658
+ ## Package entry points
659
+
660
+ | Import | Purpose |
661
+ |---|---|
662
+ | `quadqr` | Runtime-neutral core, secure payloads, rendering, scanning, optional WASM |
663
+ | `quadqr/browser` | Browser ESM entry |
664
+ | `quadqr/node` | Node core plus PNG/file/buffer helpers |
665
+ | `quadqr/benchmark` | Capacity and codec benchmark helpers |
666
+ | `quadqr/quadqr.min.js` | Classic browser global/CDN bundle |
667
+
668
+ The Node PNG path is dependency-free. For JPEG, WebP, or AVIF input, the Node adapter can use `sharp` when the consuming application already has it installed.
669
+
670
+ ---
671
+
672
+ ## Repository layout
673
+
674
+ ```text
675
+ library/
676
+ quadqr.js Core encoder, decoder, renderer, scanner, and public API
677
+ security.js Secure Payload v1 encryption and key handling
678
+ reed-solomon.js GF(256) Reed-Solomon implementation
679
+ geometry.js Version, size, and alignment geometry
680
+ vision.js Finder detection, perspective correction, and color sampling
681
+ node.js Node PNG/file/buffer adapters
682
+ wasm.js Optional prebuilt WASM loader
683
+ benchmark.js Reusable benchmark utilities
684
+
685
+ wasm-src/
686
+ quadqr_core.c Small portable WASM accelerator source
687
+
688
+ wasm/
689
+ quadqr-core.wasm Maintainer build output for direct source usage
690
+
691
+ dist/
692
+ index.js ESM package entry
693
+ index.cjs Modern CommonJS wrapper
694
+ browser.js Browser ESM entry
695
+ node.js Node entry
696
+ quadqr.js Classic global browser bundle
697
+ quadqr.min.js Compact CDN browser bundle
698
+ wasm/ Prebuilt WASM package asset
699
+
700
+ demo/
701
+ index.html Interactive generator, image scanner, camera scanner, benchmark
702
+ app.js
703
+ styles.css
704
+
705
+ docs-site/
706
+ index.html Standalone documentation website
707
+ app.js
708
+ styles.css
709
+
710
+ docs/
711
+ README.md Markdown documentation index
712
+ GETTING_STARTED.md
713
+ API.md
714
+ BROWSER_CDN.md
715
+ NODE.md
716
+ SECURITY.md
717
+ WASM.md
718
+ PUBLISHING.md
719
+
720
+ types/ TypeScript declarations for JavaScript consumers
721
+ bin/ `npx quadqr` CLI
722
+ scripts/ Build, benchmark, and local server scripts
723
+ tests/ Codec and package distribution tests
724
+ FORMAT.md Wire-format specification
725
+ AGENT.md Development guidance
726
+ README.md Project overview
727
+ ```
728
+
729
+ ---
730
+
731
+ ## Main API
732
+
733
+ ### `encodeText(text, options?)`
734
+
735
+ ```js
736
+ const code = encodeText("Hello from QuadQR", {
737
+ version: "auto",
738
+ minVersion: 1,
739
+ maxVersion: 40,
740
+ ecc: "M"
741
+ });
742
+ ```
743
+
744
+ ### `encodeBytes(bytes, options?)`
745
+
746
+ Encodes arbitrary binary data from a `Uint8Array`.
747
+
748
+ ```js
749
+ const code = encodeBytes(myBytes, {
750
+ version: "auto",
751
+ ecc: "M"
752
+ });
753
+ ```
754
+
755
+ ### `encodeSecureText(text, options?)`
756
+
757
+ Secure encoding is asynchronous because it uses Web Crypto.
758
+
759
+ Password mode:
760
+
761
+ ```js
762
+ const code = await encodeSecureText("Private message", {
763
+ ecc: "M",
764
+ security: {
765
+ mode: "password",
766
+ password: "correct horse battery staple"
767
+ }
768
+ });
769
+ ```
770
+
771
+ Raw 256-bit key mode:
772
+
773
+ ```js
774
+ const key = generateRaw256Key();
775
+
776
+ const code = await encodeSecureText("Device configuration", {
777
+ ecc: "M",
778
+ security: {
779
+ mode: "raw-key",
780
+ key
781
+ }
782
+ });
783
+ ```
784
+
785
+ Raw keys may be supplied as an exact 32-byte `Uint8Array` or as a 64-character hexadecimal string. By default, raw-key envelopes include a short SHA-256 key fingerprint (`keyIdHex`) that helps an application select the correct secret key without storing the key inside the QuadQR.
786
+
787
+ ### `encodeSecureBytes(bytes, options?)`
788
+
789
+ Binary equivalent of `encodeSecureText()`.
790
+
791
+ ### `decryptDecoded(result, credentials)`
792
+
793
+ A secure matrix/image scan first returns encrypted metadata without exposing plaintext:
794
+
795
+ ```js
796
+ const locked = decodeMatrix(matrix);
797
+
798
+ console.log(locked.secure); // true
799
+ console.log(locked.requiresDecryption); // true
800
+ console.log(locked.security.mode); // password | raw-key
801
+ ```
802
+
803
+ Then decrypt it:
804
+
805
+ ```js
806
+ const result = await decryptDecoded(locked, {
807
+ password: "correct horse battery staple"
808
+ });
809
+
810
+ console.log(result.text);
811
+ ```
812
+
813
+ For raw-key mode:
814
+
815
+ ```js
816
+ const result = await decryptDecoded(locked, { key });
817
+ ```
818
+
819
+ The decrypted result preserves the encrypted envelope as `encryptedPayload` for applications that need both forms.
820
+
821
+ ### `decodeMatrix(matrix, options?)`
822
+
823
+ Decodes an already reconstructed QuadQR matrix.
824
+
825
+ ```js
826
+ const result = decodeMatrix(matrix);
827
+ ```
828
+
829
+ When a scanner has per-cell confidence values, they can also be supplied directly:
830
+
831
+ ```js
832
+ const result = decodeMatrix(matrix, {
833
+ cellConfidence: confidenceMatrix
834
+ });
835
+ ```
836
+
837
+ The result reports fields such as `spectralInterleaving`, `confidenceAssisted`, `erasureSymbols`, and `correctedSymbols`. Secure symbols additionally report `secure`, `requiresDecryption`, and parsed `security` metadata while keeping `text` unset until successful decryption.
838
+
839
+ ### `renderToCanvas(codeOrMatrix, canvas, options?)`
840
+
841
+ Renders a QuadQR symbol into a browser canvas. `options.style` supports `classic`, `depth`, `soft`, and `inset`.
842
+
843
+ ### `renderToImageData(codeOrMatrix, options?)`
844
+
845
+ Returns an ImageData-like object and supports the same rendering styles as `renderToCanvas()`:
846
+
847
+ ```js
848
+ {
849
+ width,
850
+ height,
851
+ data
852
+ }
853
+ ```
854
+
855
+ This is also useful for tests and non-DOM workflows.
856
+
857
+ ### `scanImageData(imageData, options?)`
858
+
859
+ Runs the complete perspective-aware and color-aware image scanner. The scanner automatically computes RGBW confidence values and uses confidence-guided erasure decoding only when ordinary hard-decision ECC is insufficient.
860
+
861
+ ### `scanFile(file, options?)`
862
+
863
+ Scans an uploaded browser image file.
864
+
865
+ ### `scanVideoFrame(video, options?)`
866
+
867
+ Scans one frame from an HTML video element.
868
+
869
+ ### `startCameraScanner(video, options?)`
870
+
871
+ Starts a reusable live-camera scanning loop.
872
+
873
+ ### `getVersionInfo(version, options?)`
874
+
875
+ Returns information such as:
876
+
877
+ - matrix size;
878
+ - number of data cells;
879
+ - theoretical raw bits;
880
+ - usable payload capacity;
881
+ - structural metadata.
882
+
883
+ ---
884
+
885
+ ## Tests
886
+
887
+ Run:
888
+
889
+ ```bash
890
+ npm test
891
+ ```
892
+
893
+ The current test suite covers areas including:
894
+
895
+ - RGBW 2-bit mapping;
896
+ - binary round trips;
897
+ - Unicode text round trips;
898
+ - automatic version selection;
899
+ - all ECC profiles;
900
+ - version 1 compact framing;
901
+ - capacity boundaries;
902
+ - deliberate Reed-Solomon corruption recovery;
903
+ - Reed-Solomon error + erasure recovery;
904
+ - zero-overhead spectral-spatial permutation validation;
905
+ - confidence-assisted recovery beyond the ordinary hard-error limit;
906
+ - rotation handling;
907
+ - generated-image scanning;
908
+ - perspective distortion;
909
+ - color-cast scanning;
910
+ - benchmark reference data;
911
+ - timed codec round trips;
912
+ - password-mode secure round trips and wrong-password rejection;
913
+ - raw 256-bit key round trips, key fingerprinting, and wrong-key rejection;
914
+ - secure rendered-image scan and decryption.
915
+
916
+ ---
917
+
918
+ ## Current limitations
919
+
920
+ QuadQR is still experimental.
921
+
922
+ Important areas that need more research and real-device testing include:
923
+
924
+ - printed codes across different printers and inks;
925
+ - paper color and reflectivity;
926
+ - display brightness and color profiles;
927
+ - screen glare;
928
+ - moiré patterns;
929
+ - motion blur;
930
+ - very small modules;
931
+ - extreme camera angles;
932
+ - low-light scanning;
933
+ - RGBW confusion under difficult illumination;
934
+ - damaged or partially hidden symbols;
935
+ - standardized recovery percentages;
936
+ - equal-reliability comparison with ISO QR Code;
937
+ - local/non-projective distortion correction using the distributed alignment grid;
938
+ - formal print-quality grading;
939
+ - performance across different phones and camera systems;
940
+ - memory-hard password KDF option such as Argon2id for environments where a small WASM/runtime dependency is acceptable;
941
+ - public/private-key secure payload mode.
942
+
943
+ The project should currently be treated as a research and experimental implementation rather than a replacement for standardized QR Code in production-critical environments.
944
+
945
+ ---
946
+
947
+ ## Roadmap
948
+
949
+ - [ ] Equal-reliability benchmark against standard QR
950
+ - [ ] Automated camera torture-test suite
951
+ - [ ] Blur, JPEG, noise, perspective, and lighting benchmarks
952
+ - [ ] Print-and-rescan dataset
953
+ - [ ] Confidence-based RGBW classification
954
+ - [ ] Improved adaptive color calibration
955
+ - [x] Distributed alignment patterns for large versions
956
+ - [ ] Interleaving tuned for localized physical damage
957
+ - [ ] Real-device benchmark dataset
958
+ - [ ] Formal versioned QuadQR specification
959
+ - [ ] Implementations in additional languages
960
+ - [ ] Independent decoder implementation
961
+
962
+ ---
963
+
964
+ ## Why keep the square design?
965
+
966
+ QuadQR intentionally keeps square modules and a square overall matrix.
967
+
968
+ Square cells:
969
+
970
+ - tessellate without gaps;
971
+ - provide predictable row/column addressing;
972
+ - maximize colored area inside each module;
973
+ - are easy to sample at their center;
974
+ - work naturally with perspective correction;
975
+ - keep the geometry relatively simple for camera scanning.
976
+
977
+ Alternative module shapes may be interesting visually, but the current focus is data density, reliability, and scan robustness.
978
+
979
+ ---
980
+
981
+ ## Is QuadQR a QR Code replacement?
982
+
983
+ Not currently.
984
+
985
+ Standard QR Code has enormous advantages:
986
+
987
+ - decades of deployment;
988
+ - international standardization;
989
+ - extremely mature decoders;
990
+ - broad device support;
991
+ - extensive real-world testing;
992
+ - proven print reliability.
993
+
994
+ QuadQR is exploring a different question:
995
+
996
+ > **What can a QR-inspired matrix code look like if we design its data layer around modern color-capable cameras and displays?**
997
+
998
+ The goal is experimentation, measurement, and learning.
999
+
1000
+ ---
1001
+
1002
+ ## Contributing
1003
+
1004
+ Contributions, experiments, test images, scanner improvements, benchmarking ideas, and independent implementations are welcome.
1005
+
1006
+ If you are contributing changes to the wire format, please also update:
1007
+
1008
+ ```text
1009
+ FORMAT.md
1010
+ ```
1011
+
1012
+ Changes that affect capacity, ECC, scanning behavior, or version selection should include tests where practical.
1013
+
1014
+ ---
1015
+
1016
+ ## Security
1017
+
1018
+ Decoded payloads are untrusted input.
1019
+
1020
+ Do not automatically execute decoded:
1021
+
1022
+ - HTML;
1023
+ - JavaScript;
1024
+ - shell commands;
1025
+ - application commands;
1026
+ - URLs.
1027
+
1028
+ Applications using QuadQR should validate and safely handle decoded content just as they would any other external input.
1029
+
1030
+ ---
1031
+
1032
+ ## License
1033
+
1034
+ AGPL v3.0. See `LICENSE`.
1035
+
1036
+ ---
1037
+
1038
+ ## Project status
1039
+
1040
+ **Experimental / research project**
1041
+
1042
+ QuadQR is actively evolving. Format details may change between versions until the wire format is considered stable.