quadqr-js 1.3.0 → 1.5.2

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/FORMAT.md CHANGED
@@ -1,4 +1,4 @@
1
- # QuadQR Format v5
1
+ # QuadQR Format v6
2
2
 
3
3
  ## Status
4
4
 
@@ -50,7 +50,7 @@ Three 7×7 black/white finder structures are placed at top-left, top-right, and
50
50
 
51
51
  ## Alignment patterns
52
52
 
53
- QuadQR always keeps exactly three primary 7×7 finder patterns. Larger versions do **not** add more primary finders. Instead, versions 2 through 40 use distributed black/white alignment markers following the same center-position schedule used by standard QR Code versions. Exactly one bottom-right **primary alignment marker remains 5×5**. Every additional distributed alignment marker is **3×3**, encoded as a black outer ring with a white center.
53
+ QuadQR always keeps exactly three primary 7×7 finder patterns. Larger versions do **not** add more primary finders. Instead, versions 2 through 40 use distributed black/white alignment markers following the same center-position schedule used by standard QR Code versions. **Format v6 uses a full 5×5 nested black/white/black alignment eye at every scheduled alignment position**, matching the strong structural signature used by proven QR detectors.
54
54
 
55
55
  The three alignment positions that would overlap the primary finder corners are omitted. This produces progressively more alignment references as the matrix grows. Examples:
56
56
 
@@ -64,7 +64,7 @@ v40 -> 46 alignment patterns
64
64
 
65
65
  Version 1 is a QuadQR-specific exception. Standard QR v1 has no alignment pattern, but QuadQR keeps one legacy 5×5 bottom-right bootstrap alignment marker with a one-cell white separator so the camera scanner still has a fourth projective reference point.
66
66
 
67
- For versions 2 through 40, the scanner uses the 5×5 bottom-right member of the distributed alignment grid as the primary fourth homography reference and then scores the full expected grid, including the 3×3 secondary markers, to strengthen version/geometry validation.
67
+ For versions 2 through 40, the scanner uses the bottom-right 5×5 member of the distributed alignment grid as the primary fourth homography reference, then scores and refines against the full 5×5 alignment grid. A fast nested-eye locator is attempted before broader template recovery so perspective geometry can be established as soon as the three primary finder eyes are visible.
68
68
 
69
69
  ## Timing structures
70
70
 
@@ -361,13 +361,13 @@ The decoder also tries legacy physical order as a fallback for older QuadQR matr
361
361
 
362
362
  Unused data positions are filled with deterministic pseudo-random values in the range 0..3. Padding is not semantically decoded.
363
363
 
364
- ## Confidence-aware error/erasure decoding
364
+ ## Spectrum ECC 2.0 confidence-aware and soft decoding
365
365
 
366
- For image/camera scans, classification retains more than the winning RGBW state. Each sampled module also receives a confidence score derived from the separation between its nearest and second-nearest calibrated palette states.
366
+ For image/camera scans, classification retains more than the winning RGBW/Triangle16 state. Each sampled data cell receives a confidence score and a bounded second hypothesis derived from the calibrated palette classification. Triangle16 also incorporates the measured disagreement between its three interior samples per triangle so edge bleed or sub-module geometry instability lowers confidence instead of becoming false certainty.
367
367
 
368
- One GF(256) symbol corresponds to four 2-bit data cells. The symbol confidence is the minimum confidence of those four constituent cells because an error in any one cell changes the reconstructed byte.
368
+ In normal RGBW mode, one GF(256) byte symbol corresponds to four 2-bit cells. In High Density Mode, a body byte corresponds to two 4-bit Triangle16 cells, while the protected header remains four solid RGBW-equivalent cells per byte. Symbol confidence is the minimum confidence of its constituent cells because an error in any constituent cell changes the reconstructed byte.
369
369
 
370
- Decoding first attempts normal hard-decision Reed-Solomon correction. If that fails, low-confidence byte positions are progressively promoted to known erasures and the decoder retries error/erasure RS correction. Valid correction requires syndrome verification and the complete QuadQR payload still must pass CRC-32.
370
+ Decoding first attempts normal hard-decision Reed-Solomon correction. If that fails, low-confidence byte positions are progressively promoted to known erasures and the decoder retries error/erasure RS correction. If that still fails and scanner second hypotheses are available, Spectrum ECC 2.0 performs a bounded Chase-style search over the least-confident cells, trying single alternate substitutions and then a small pair set. Every candidate is passed through the unchanged RS and CRC validation path.
371
371
 
372
372
  The RS budget follows the usual error/erasure relationship:
373
373
 
@@ -391,11 +391,13 @@ RGB frame
391
391
  -> distributed alignment-grid validation
392
392
  -> projective module sampling
393
393
  -> observed black/white/R/G/B calibration
394
- -> nearest calibrated RGBW classification + confidence
394
+ -> observed-palette / white-balance / affine calibration attempts
395
+ -> RGBW or Triangle16 classification + confidence + second hypothesis
395
396
  -> four-state XOR unmasking
396
397
  -> reverse spectral-spatial permutation
397
398
  -> protected header GF(256) hard RS decode
398
399
  -> confidence-guided error/erasure retry when needed
400
+ -> bounded second-hypothesis soft retry when needed
399
401
  -> body block deinterleaving
400
402
  -> body GF(256) error/erasure correction
401
403
  -> CRC-32 verification
@@ -409,7 +411,7 @@ Matrix decoding tries 0°, 90°, 180°, and 270° rotations.
409
411
 
410
412
  ## Compatibility
411
413
 
412
- Format v5 is intentionally incompatible with standard QR scanners and with the project's older ternary prototypes. The current encoder writes codeword cells using spectral-spatial placement. The current decoder also tries the pre-interleaver physical order as a compatibility fallback for older RGBW QuadQR matrices. Version 5 keeps the distributed alignment-center schedule introduced in v4, but shrinks every non-primary alignment marker from 5×5 to 3×3 while retaining the bottom-right primary marker at 5×5. Because reserved-cell geometry changed, v4 and v5 large-symbol matrices are not wire-compatible.
414
+ Format v6 is intentionally incompatible with standard QR scanners and with the project's older ternary prototypes. The current encoder writes codeword cells using spectral-spatial placement and uses 5×5 nested alignment eyes at every scheduled alignment position. Format v5 used compact 3×3 secondary alignment markers while retaining the bottom-right primary marker at 5×5. The current decoder keeps a legacy Format v5 alignment-profile fallback, so existing v5 RGBW QuadQR symbols remain readable. The decoder also retains the pre-interleaver physical-order fallback for older RGBW matrices.
413
415
 
414
416
  ## Rendering profiles are not part of the wire format
415
417
 
@@ -417,11 +419,11 @@ The canonical QuadQR matrix is independent of presentation style. Renderers may
417
419
 
418
420
  ## Compression and signature flags
419
421
 
420
- Format v5 keeps the physical matrix and ECC framing unchanged while reserving two protected-header flags for optional internal payload metadata:
422
+ Format v6 keeps the physical matrix and ECC framing unchanged while reserving two protected-header flags for optional internal payload metadata:
421
423
 
422
424
  ```text
423
425
  bit 4 = internal payload-extension metadata present
424
426
  bit 5 = signed-payload hint
425
427
  ```
426
428
 
427
- These bits are not a public payload-type mode. Applications continue to encode normal UTF-8 text or arbitrary bytes. Compression and Ed25519 signing use a compact internal extension envelope only when metadata is required. The envelope, print profile, diagnostics, and scanability test model are specified in [`SPECIFICATION.md`](./SPECIFICATION.md). None of these features changes RGBW cell mapping or Spectrum ECC.
429
+ These bits are not a public payload-type mode. Applications continue to encode normal UTF-8 text or arbitrary bytes. Compression 3.0 (fast Auto, CPU-heavy Smart, explicit LZ/DEFLATE/Brotli levels with legacy LZ wire compatibility) and Ed25519 signing use a compact internal extension envelope only when metadata is required. The envelope, print profile, diagnostics, and scanability test model are specified in [`SPECIFICATION.md`](./SPECIFICATION.md). None of these features changes RGBW cell mapping or Spectrum ECC.
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  <p align="center">
12
12
  <a href="https://akanshsirohi.github.io/QuadQR/demo/"><strong>Try the Live Demo</strong></a>
13
13
  ·
14
- <a href="https://akanshsirohi.github.io/QuadQR/docs-site/"><strong>Documentation Site</strong></a>
14
+ <a href="https://akanshsirohi.github.io/QuadQR/documentation/"><strong>Documentation Site</strong></a>
15
15
  ·
16
16
  <a href="https://www.npmjs.com/package/quadqr-js">npm</a>
17
17
  ·
@@ -19,11 +19,9 @@
19
19
  ·
20
20
  <a href="#getting-started">Use the Library</a>
21
21
  ·
22
- <a href="docs/README.md">Markdown Docs</a>
22
+ <a href="https://akanshsirohi.github.io/QuadQR/documentation/format.html">Format Reference</a>
23
23
  ·
24
- <a href="FORMAT.md">Matrix Format</a>
25
- ·
26
- <a href="SPECIFICATION.md">Technical Specification</a>
24
+ <a href="https://akanshsirohi.github.io/QuadQR/documentation/specification.html">Technical Specification</a>
27
25
  </p>
28
26
 
29
27
  **QuadQR** is an experimental open-source 2D matrix code that uses RGBW color states instead of the two states used by a traditional black-and-white QR module. Normal mode stores **2 bits per RGBW data cell**. This experimental branch also includes an optional **High Density Mode**, implemented with Triangle16 split cells, that stores **4 raw bits per body cell**. High Density Mode is disabled by default.
@@ -59,7 +57,7 @@ const code = encodeText("High-density QuadQR", {
59
57
 
60
58
  Image and camera scanning automatically detect High Density Mode, so a separate scanner mode is not required. High Density Mode is experimental and should be stress-tested at the intended physical size and camera distance.
61
59
 
62
- See [`docs/HIGH_DENSITY_MODE.md`](docs/HIGH_DENSITY_MODE.md) for the physical cell mapping, protected-header strategy, scanner sampling rules, and reliability caveats.
60
+ See the [High Density Mode documentation](https://akanshsirohi.github.io/QuadQR/documentation/high-density.html) for the physical cell mapping, protected-header strategy, scanner sampling rules, and reliability caveats.
63
61
 
64
62
  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.
65
63
 
@@ -72,12 +70,12 @@ Below are QuadQR symbols generated with different error-correction profiles. The
72
70
  <table>
73
71
  <tr>
74
72
  <td align="center">
75
- <img src="assets/quadqr-v2-L.png?raw=true" alt="QuadQR example using ECC profile L" width="320"><br>
76
- <sub><strong>ECC L</strong> · Higher payload capacity</sub>
73
+ <img src="assets/quadqr-normal.png?raw=true" alt="QuadQR example using ECC profile L" width="320"><br>
74
+ <sub><strong>QuadQR</strong> · Normal (v3)</sub>
77
75
  </td>
78
76
  <td align="center">
79
- <img src="assets/quadqr-v2-M.png?raw=true" alt="QuadQR example using ECC profile M" width="320"><br>
80
- <sub><strong>ECC M</strong> · More error-correction redundancy</sub>
77
+ <img src="assets/quadqr-high-density.png?raw=true" alt="QuadQR example using ECC profile M" width="320"><br>
78
+ <sub><strong>QuadQR</strong> · Higher Data Density (v2)</sub>
81
79
  </td>
82
80
  </tr>
83
81
  </table>
@@ -158,13 +156,13 @@ So at the raw data-cell level:
158
156
 
159
157
  This is a **2× raw symbol-density advantage**.
160
158
 
161
- ### Spectrum ECC: QuadQR-specific reliability without capacity loss
159
+ ### Spectrum ECC 2.0: confidence-aware + soft decoding
162
160
 
163
- 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**.
161
+ QuadQR uses the fact that a color scanner knows more than only the winning state. For each sampled data cell it now retains the selected state, a confidence score, and a bounded second hypothesis. Triangle16 does the same after classifying both color regions.
164
162
 
165
- 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.
163
+ The first recovery layer is still confidence-aware GF(256) Reed-Solomon: if normal hard-decision decoding fails, the least-confident byte symbols can be promoted to **known erasures**, allowing the existing parity budget to be spent more efficiently.
166
164
 
167
- 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.
165
+ **Spectrum ECC 2.0** adds a bounded soft-decision fallback. If hard decoding and erasure decoding still fail, the decoder tries the second hypothesis for a small number of the least-confident data cells, first singly and then in tightly bounded pairs. Every candidate must still pass the normal Reed-Solomon checks and final CRC-32, so soft decoding does not relax integrity validation or add parity overhead.
168
166
 
169
167
  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**.
170
168
 
@@ -223,6 +221,24 @@ const compressed = encodeText("repeated repeated repeated", {
223
221
  ecc: "M"
224
222
  });
225
223
 
224
+ // Smart is CPU-heavy and only escalates when stronger compression can
225
+ // realistically reduce the physical QuadQR version.
226
+ const smart = encodeText(largeStructuredText, { compression: "smart" });
227
+
228
+ // Explicit codecs can choose their encoder level.
229
+ const lz = encodeText(largeStructuredText, {
230
+ compression: "lz",
231
+ compressionLevel: 9
232
+ });
233
+ const brotli = encodeText("hello ".repeat(1000), {
234
+ compression: "brotli",
235
+ compressionLevel: 11
236
+ });
237
+ const deflate = encodeText(largeStructuredText, {
238
+ compression: "deflate",
239
+ compressionLevel: 9
240
+ });
241
+
226
242
  const keys = await generateSigningKeyPair();
227
243
  const signed = await encodeSignedText("verified offline", {
228
244
  compression: "auto",
@@ -238,7 +254,7 @@ console.log(verified.signatureVerified); // true
238
254
  console.log(verified.signatureTrusted); // true
239
255
  ```
240
256
 
241
- Compression modes are `none`, `auto`, and `lz`. `auto` keeps the original payload untouched when compression would not save space. Ed25519 signing stores the signature plus an optional compact `keyId`; the public verification key stays outside the QuadQR by default. Applications do not need to choose or maintain content types.
257
+ Compression modes are `none`, `auto`, `smart`, `brotli`, `deflate`, and `lz`. `auto` is the fast default: it compares LZ level 6, DEFLATE level 6, and Brotli quality 6 once, including envelope overhead, and keeps the smallest final representation. `smart` is an opt-in CPU-heavy mode. It starts with the same balanced pass, checks the resulting QuadQR version, and only escalates to DEFLATE 8 / Brotli 9 and then DEFLATE 9 / Brotli 11 when a smaller physical version is realistically reachable; LZ stays at its default level in Auto/Smart. Explicit `lz` accepts `compressionLevel: 1..9` with default 6, explicit `deflate` accepts `1..9` with default 6, and explicit `brotli` accepts `0..11` with default 11. Compression level is an encoder-only setting and is not stored in the symbol because the decoder does not need it. All codecs are synchronous and bundled with QuadQR, so the same compression path works in browsers and server-side Node.js without `node:zlib`, `CompressionStream`, or a runtime dependency. The demo keeps these CPU-heavy operations in module Web Workers so the browser UI remains responsive. See the [compression documentation](https://akanshsirohi.github.io/QuadQR/documentation/compression.html) for the exact Smart escalation policy and level API. Ed25519 signing stores the signature plus an optional compact `keyId`; the public verification key stays outside the QuadQR by default. Applications do not need to choose or maintain content types.
242
258
 
243
259
  Signing can also be composed with Secure Payload. QuadQR compresses if requested, signs the normal payload with the private key, then encrypts the protected bytes with AES-256-GCM. A verifier supplies the trusted public key separately, or resolves it from `keyId`.
244
260
 
@@ -250,7 +266,7 @@ Scanner results include normalized diagnostics such as `confidence`, `geometryCo
250
266
 
251
267
  For regression and demo testing, `runImageStressTest()` / `assessScanability()` apply deterministic blur, brightness, exposure, shadow, contrast, perspective, JPEG-like artifacts, and downscaling. The browser demo exposes the same tools as an interactive stress-test lab and shows an overall scanability rating.
252
268
 
253
- The interoperability details are documented in [`SPECIFICATION.md`](./SPECIFICATION.md).
269
+ The interoperability details are covered in the [technical specification](https://akanshsirohi.github.io/QuadQR/documentation/specification.html).
254
270
 
255
271
  ---
256
272
 
@@ -424,7 +440,7 @@ QuadQR currently uses:
424
440
  - square modules;
425
441
  - three 7×7 black-and-white finder patterns;
426
442
  - black-and-white timing structures;
427
- - one 5×5 primary alignment reference plus compact 3×3 secondary alignment markers on larger symbols;
443
+ - distributed 5×5 nested alignment eyes on versions that use alignment patterns;
428
444
  - RGB calibration swatches;
429
445
  - structural black/white references;
430
446
  - a two-column zig-zag physical data-position path;
@@ -437,7 +453,7 @@ QuadQR currently uses:
437
453
 
438
454
  White is a valid data state.
439
455
 
440
- 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.
456
+ QuadQR still uses exactly three large finder patterns, just like standard QR. Starting at version 2, Format v6 alignment markers follow the standard QR version-dependent center schedule and every scheduled alignment marker is a full 5×5 nested black/white/black eye. These distributed references give the detector stronger anchors for high-version and projectively distorted symbols. Version 1 keeps one QuadQR-specific 5×5 bottom-right bootstrap marker because it otherwise would have no fourth projective reference. The decoder retains the compact 3×3 secondary-marker profile used by Format v5 so existing v5 symbols remain readable.
441
457
 
442
458
  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.
443
459
 
@@ -465,7 +481,7 @@ White → (255, 255, 255)
465
481
 
466
482
  Real camera input is not expected to match those exact values.
467
483
 
468
- 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. The clean-frame path stays fast: QuadQR tries the normal detected geometry and observed palette first. Dense versions can refine an imperfect four-point homography with reliable secondary alignment markers already present in the matrix, without reserving any new cells. If a steep angle leaves exactly two strong finder patterns, a bounded looser third-finder pass runs before heavier color recovery. Only after geometry/color decoding still fails does QuadQR progressively try stronger recovery, including white balancing, spatial normalization, Auto Tone / Auto Contrast / Auto Color-style enhancement, and bounded sub-module geometry refinement. For live video, QuadQR scans the CSS-visible `object-fit: cover` camera region instead of the hidden full sensor frame, so the code keeps the same apparent size/resolution the user sees in the guide. When a dense frame already exposes at least two finders, the camera scanner can also retry the visible ROI at up to 1600 px before expensive color recovery. If finder geometry is already strong but color decoding fails, a QR-only rectified pixel enhancement retry is performed immediately; whole-frame enhancement remains reserved for harder locator failures.
484
+ 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. The clean-frame path starts with a streaming 1:1:3:1:1 finder detector on the RGB value channel, direct cross-checks, local-threshold fallback, and directional module-size/version estimation. Once the three finder eyes form a valid geometry, QuadQR immediately tries the highest-ranked perspective candidate instead of waiting for every lower-ranked geometry. Near-front-facing symbols can use a three-finder affine fast path, while projectively distorted symbols continue through the full homography/alignment solver. Format v6 distributed 5×5 alignment eyes provide stronger local perspective anchors, and a cheap nested-eye locator is tried before the broader alignment search. If a steep angle leaves exactly two strong finder patterns, the bounded looser third-finder pass still runs before heavier color recovery. Only after geometry/color decoding still fails does QuadQR progressively try the existing stronger recovery, including white balancing, a 3×4 affine color-calibration model learned from the known black/white/R/G/B references, spatial normalization, Auto Tone / Auto Contrast / QuadQR Auto Color-style enhancement, soft-decision ECC, and bounded sub-module geometry refinement. For live video, normal detection works from the CSS-visible `object-fit: cover` camera region at a 640 px working dimension. A bounded 960 px retry is reserved for difficult dense frames that already show useful finder evidence, rather than being part of the ordinary camera loop. If finder geometry is already strong but color decoding fails, a QR-only rectified pixel enhancement retry is performed immediately; whole-frame enhancement remains reserved for harder locator failures.
469
485
 
470
486
  ---
471
487
 
@@ -537,17 +553,17 @@ module-grid reconstruction
537
553
 
538
554
  fast observed-RGB decode attempt
539
555
  ↓ (only if needed)
540
- white balance + spatial normalization
556
+ white balance + affine cross-channel calibration + spatial normalization
541
557
  ↓ (only if still needed)
542
- Auto Tone / Auto Contrast / Auto Color-style recovery
558
+ Auto Tone / Auto Contrast / QuadQR Auto Color-style recovery
543
559
  ↓ (only if still needed)
544
560
  sub-module geometry refinement
545
561
 
546
562
  RGB + structural black/white calibration
547
563
 
548
- nearest-color classification + confidence scoring
564
+ RGBW/Triangle16 classification + confidence + second hypothesis
549
565
 
550
- four-state unmasking
566
+ unmasking
551
567
 
552
568
  reverse spectral-spatial permutation
553
569
 
@@ -555,6 +571,8 @@ protected header Reed-Solomon hard decode
555
571
 
556
572
  confidence-guided erasure retry when needed
557
573
 
574
+ bounded Spectrum ECC 2.0 soft-hypothesis retry when needed
575
+
558
576
  body deinterleaving + error/erasure Reed-Solomon decode
559
577
 
560
578
  CRC-32 verification
@@ -644,7 +662,7 @@ Logo overlays intentionally consume some ECC margin because they cover encoded c
644
662
 
645
663
  **Live demo:** https://akanshsirohi.github.io/QuadQR/demo/
646
664
 
647
- The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded. The generator keeps only payload, version, and ECC visible by default; optional capabilities are grouped into independent advanced accordions.
665
+ The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded. The generator keeps only payload, version, and ECC visible by default; optional capabilities are grouped into independent advanced accordions. CPU-heavy encoding/compression, rendered-image verification, uploaded-image scanning, scanability tests, Reliability Lab runs, perspective sweeps, and codec benchmarks are dispatched to module Web Workers. Generation shows explicit progress instead of freezing the browser, repeated UI changes are coalesced, and render-only changes reuse the already encoded matrix instead of recompressing the payload.
648
666
 
649
667
  ### Generator & Image Scanner
650
668
 
@@ -764,7 +782,7 @@ console.log(result.text);
764
782
  The `quadqr-js` package can be loaded directly from npm-backed CDNs:
765
783
 
766
784
  ```html
767
- <script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.1.0/dist/quadqr.min.js"></script>
785
+ <script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.5.1/dist/quadqr.min.js"></script>
768
786
  <script>
769
787
  const code = QuadQR.encodeText("Hello from a script tag");
770
788
  </script>
@@ -798,7 +816,7 @@ npx quadqr-js encode "Private data" --password "my-password" -o secure.png
798
816
  npx quadqr-js decode secure.png --password "my-password"
799
817
  ```
800
818
 
801
- See [`docs/CLI.md`](docs/CLI.md) for all CLI options, including compression, raw 256-bit key mode, signing, print mode, and scanner diagnostics.
819
+ See the [CLI reference](https://akanshsirohi.github.io/QuadQR/documentation/cli.html) for all CLI options, including compression, raw 256-bit key mode, signing, print mode, and scanner diagnostics.
802
820
 
803
821
  ### Run from source
804
822
 
@@ -820,6 +838,16 @@ Build the distributable browser, Node.js, CDN, and WASM files:
820
838
  npm run build
821
839
  ```
822
840
 
841
+ `npm run build` verifies the checked-in WASM binary against `wasm-src/quadqr_core.c`, the WASM compiler flags, and the recorded binary hash. If everything matches, the verified prebuilt binary is reused. If the C source or build flags changed, the build automatically recompiles WASM with LLVM/Clang. If WASM is stale and `clang` is unavailable, the build fails instead of silently shipping an old binary.
842
+
843
+ To force only the WASM rebuild:
844
+
845
+ ```bash
846
+ npm run build:wasm
847
+ ```
848
+
849
+ A forced WASM rebuild requires `clang` in `PATH`. No Rust, Cargo, or Emscripten toolchain is required. The build records its verification data in `wasm/quadqr-core.build.json`.
850
+
823
851
  Start the interactive demo:
824
852
 
825
853
  ```bash
@@ -840,7 +868,7 @@ npm run benchmark
840
868
 
841
869
  ### Optional WASM acceleration
842
870
 
843
- The package ships a prebuilt WASM helper but never requires it.
871
+ The package ships a prebuilt WASM helper but never requires it. It accelerates CRC-32 plus the scanner's RGBA-to-grayscale, Otsu-threshold, and binary finder preprocessing hot path.
844
872
 
845
873
  ```js
846
874
  import { initWasm } from "quadqr-js";
@@ -848,7 +876,7 @@ import { initWasm } from "quadqr-js";
848
876
  await initWasm();
849
877
  ```
850
878
 
851
- If WASM cannot load, the normal JavaScript codec remains available.
879
+ If WASM cannot load, the normal JavaScript codec/scanner remains available. For camera or repeated image scanning, initialize WASM once during application startup.
852
880
 
853
881
  ---
854
882
 
@@ -883,7 +911,8 @@ wasm-src/
883
911
  quadqr_core.c Small portable WASM accelerator source
884
912
 
885
913
  wasm/
886
- quadqr-core.wasm Source-tree WASM build output
914
+ quadqr-core.wasm Source-tree WASM build output
915
+ quadqr-core.build.json Source/build/binary verification metadata
887
916
 
888
917
  dist/
889
918
  index.js ESM package entry
@@ -896,25 +925,29 @@ dist/
896
925
 
897
926
  demo/
898
927
  index.html Interactive generator, image scanner, camera scanner, benchmark
899
- app.js
928
+ app.js Responsive UI + worker orchestration
929
+ compute-worker.js Background encode/scan/reliability/benchmark tasks
900
930
  styles.css
901
931
 
902
- docs-site/
903
- index.html Standalone documentation website
904
- app.js
905
- styles.css
932
+ documentation/
933
+ index.html Documentation home
934
+ getting-started.html
935
+ browser.html
936
+ node.html
937
+ scanning.html
938
+ compression.html
939
+ security.html
940
+ high-density.html
941
+ reliability.html
942
+ api.html
943
+ cli.html
944
+ wasm.html
945
+ format.html
946
+ specification.html
947
+ app.js Navigation, search, copy, and theme behavior
948
+ search-index.js Client-side documentation search index
949
+ styles.css Responsive documentation UI
906
950
 
907
- docs/
908
- README.md Markdown documentation index
909
- GETTING_STARTED.md
910
- API.md
911
- BROWSER_CDN.md
912
- NODE.md
913
- SECURITY.md
914
- CLI.md
915
- WASM.md
916
-
917
- types/ TypeScript declarations for JavaScript consumers
918
951
  bin/ `quadqr` CLI (`npx quadqr-js`)
919
952
  scripts/ Build, benchmark, and local server scripts
920
953
  tests/ Codec and package distribution tests
@@ -1059,7 +1092,7 @@ Returns a standalone SVG string using the same exact `imageSize`, render styles,
1059
1092
 
1060
1093
  ### `scanImageData(imageData, options?)`
1061
1094
 
1062
- Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Dense versions use distributed alignment markers to refine a plausible but imperfect projective solution, and a two-finder recovery pass can rescue a third locator that has been stretched by perspective. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
1095
+ Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Dense versions use distributed alignment markers to refine a plausible but imperfect projective solution, and a two-finder recovery pass can rescue a third locator that has been stretched by perspective. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / QuadQR Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
1063
1096
 
1064
1097
  ### `scanFile(file, options?)`
1065
1098
 
@@ -1071,7 +1104,7 @@ Scans one frame from an HTML video element. By default, if the video is displaye
1071
1104
 
1072
1105
  ### `startCameraScanner(video, options?)`
1073
1106
 
1074
- Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. A normal frame always gets the fast RGB-value finder pass first. If a miss still exposes at least two strong finder patterns, QuadQR retries the visible camera ROI at up to 1600 px by default so dense symbols retain more pixels per module. This high-resolution retry is bounded and does not run on empty frames. If it still fails, the **same captured frame** enters a QR-guide recovery path: QuadQR progressively crops away 8%, 16%, and 22% of the surrounding camera frame (then tries the full frame as a final fallback), applies the Photoshop-style Auto Color correction inside that code-centric region, and runs finder detection again. This matters because a live preview can contain dark room pixels, browser chrome, a monitor bezel, or other content that completely changes global Auto Color/Otsu statistics even though a manually cropped screenshot scans instantly. Normal scanning stays unchanged and fast because these recovery paths run only after a miss. Finder-only recovery also tries multiple center-weighted Auto Color histograms before threshold bracketing. `cameraHighResolutionMaxDimension` defaults to 1600, `cameraHighResolutionEvery` defaults to 2, and `cameraAutoColorEvery` defaults to 1. Multi-frame voting remains enabled by default with a four-frame history. The optional `onDiagnostic(event)` callback exposes finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact raw decoded camera frame and, when Auto Color was used, the enhanced recovery pixels and their crop rectangle, so UIs can keep the frozen frame and finder overlay aligned.
1107
+ Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. Modern browsers use a **dual-worker camera engine**: a lightweight fresh-frame worker continuously runs normal finder/geometry/decode attempts, while an independent recovery worker retains the complete high-resolution, Auto Color, precise-alignment, perspective, multi-frame, ECC, and damaged-code recovery stack. A slow recovery attempt therefore cannot prevent the fast worker from inspecting a newer camera frame. Finder detection remains JavaScript; optional WASM accelerates grayscale/binary preprocessing and CRC beneath the same detector. The scheduler uses `requestVideoFrameCallback()` when available and does not queue stale fast-path frames. Normal camera acquisition requests an environment camera around 1280×720 and crops/resizes the visible preview to a 640 px working bitmap **before** transferring it to the worker. Once a candidate validates structure, Spectrum ECC, and CRC, scanning returns immediately. If the fast worker misses, full recovery runs concurrently on a fresh frame at up to 960 px. Strong finder evidence dispatches recovery quickly; finder-less frames still receive periodic full recovery so severe color casts or damaged locators retain the same rescue paths. QuadQR Auto Color crop profiles, center-weighted histograms, threshold bracketing, precise alignment, projective recovery, QR-region enhancement, multi-frame confidence fusion, and soft-decision Spectrum ECC are unchanged. `cameraHighResolutionMaxDimension` defaults to 960. The optional `onDiagnostic(event)` callback exposes whether an event came from the fast or recovery worker, finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact frame that decoded, including enhanced recovery pixels when applicable, so UIs can keep the frozen frame and finder overlay aligned.
1075
1108
 
1076
1109
  ### `getVersionInfo(version, options?)`
1077
1110
 
@@ -1112,7 +1145,10 @@ The current test suite covers areas including:
1112
1145
  - color-cast scanning;
1113
1146
  - dirty-camera stress scanning with strong yellow cast, haze, blue-channel suppression, and blur;
1114
1147
  - low-contrast warm-camera regression where normal scanning fails but progressive Auto Tone / Contrast / Color recovery succeeds;
1115
- - multi-frame classification voting;
1148
+ - multi-frame confidence fusion and tracked-symbol consistency;
1149
+ - Spectrum ECC 2.0 bounded soft-decision recovery;
1150
+ - affine cross-channel color calibration;
1151
+ - multi-point Triangle16 region sampling with instability-aware confidence;
1116
1152
  - benchmark reference data;
1117
1153
  - timed codec round trips;
1118
1154
  - password-mode secure round trips and wrong-password rejection;
package/SPECIFICATION.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  QuadQR is an experimental RGBW matrix symbology. Normal mode uses 4-state RGBW cells, while the optional **High Density Mode** is experimental and uses the 16-state Triangle16 physical layout. It is not ISO/IEC QR Code and is not intended to be decoded by standard QR readers.
6
6
 
7
- The physical matrix format remains **QuadQR Format v5**. Normal application data is always treated simply as UTF-8 text or arbitrary bytes. Compression, signatures, encryption, rendering, and diagnostics are optional features layered around that stable matrix codec.
7
+ The physical matrix format remains **QuadQR Format v6**. Normal application data is always treated simply as UTF-8 text or arbitrary bytes. Compression, signatures, encryption, rendering, and diagnostics are optional features layered around that stable matrix codec.
8
8
 
9
9
  For exact matrix geometry and Reed-Solomon framing, see [`FORMAT.md`](./FORMAT.md).
10
10
 
@@ -17,7 +17,7 @@ Application bytes / UTF-8 text
17
17
  ├─ optional Ed25519 signature metadata (internal)
18
18
  ├─ optional Secure Payload v1 (AES-256-GCM)
19
19
 
20
- └─ QuadQR Format v5
20
+ └─ QuadQR Format v6
21
21
  ├─ protected header
22
22
  ├─ CRC-32
23
23
  ├─ GF(256) Reed-Solomon Spectrum ECC
@@ -42,7 +42,7 @@ Structural black is separate from the data alphabet. One encoded byte maps to ex
42
42
 
43
43
  ### High Density Mode
44
44
 
45
- When `highDensity: true` is selected, each payload module has a fixed `/` diagonal and two independently classified RGBW regions. This creates 16 states and 4 raw bits per body cell. The protected header stays solid-color at 2 bits per cell for robust bootstrap recovery, then header flag bit 6 tells the decoder that the ECC/body stream uses Triangle16 packing. Scanner sampling uses two points well inside the triangles and excludes the diagonal boundary.
45
+ When `highDensity: true` is selected, each payload module has a fixed `/` diagonal and two independently classified RGBW regions. This creates 16 states and 4 raw bits per body cell. The protected header stays solid-color at 2 bits per cell for robust bootstrap recovery, then header flag bit 6 tells the decoder that the ECC/body stream uses Triangle16 packing. Scanner sampling uses three interior anchors per triangle, robust aggregation, and a spatial-instability penalty while excluding the diagonal boundary.
46
46
 
47
47
  ## 3. Matrix sizing
48
48
 
@@ -53,9 +53,9 @@ version = 1..40
53
53
 
54
54
  Three 7×7 finder patterns are always present. Version 1 has the legacy QuadQR 5×5 bottom-right alignment marker. Versions 2–40 use the distributed alignment schedule defined in `FORMAT.md`.
55
55
 
56
- ## 4. Format v5 header flags
56
+ ## 4. Format v6 header flags
57
57
 
58
- The protected Format v5 header uses:
58
+ The protected Format v6 header uses:
59
59
 
60
60
  ```text
61
61
  bit 0 UTF-8 text flag
@@ -87,7 +87,7 @@ Version 2+ parity profiles:
87
87
  | Q | 36 | 18 |
88
88
  | H | 48 | 24 |
89
89
 
90
- The decoder retains per-cell color confidence. Low-confidence bytes can be promoted to known erasures, allowing Reed-Solomon recovery to use the existing parity budget more efficiently.
90
+ The decoder retains per-cell color confidence and one bounded alternate hypothesis from image/camera classification. Low-confidence bytes can first be promoted to known erasures, allowing Reed-Solomon recovery to use the existing parity budget more efficiently. If hard and error/erasure decoding still fail, Spectrum ECC 2.0 may try the alternate state for a bounded set of the least-confident cells. Candidate search is limited to single substitutions and a small pair set by default, and every successful path must still satisfy Reed-Solomon verification and CRC-32.
91
91
 
92
92
  ## 6. Internal payload extension envelope
93
93
 
@@ -104,12 +104,12 @@ Fixed header size: **16 bytes**.
104
104
  | Offset | Size | Field |
105
105
  |---:|---:|---|
106
106
  | 0 | 4 | ASCII magic `QPX1` |
107
- | 4 | 1 | Extension version (`2`; decoder also accepts legacy `1`) |
107
+ | 4 | 1 | Extension version (`3`; decoder also accepts legacy `1` and `2`) |
108
108
  | 5 | 1 | Flags |
109
109
  | 6 | 1 | Compression ID |
110
110
  | 7 | 1 | Signature algorithm ID |
111
111
  | 8 | 4 | Original application payload length, big-endian |
112
- | 12 | 1 | Signing key-ID length (v2); legacy signer-label length in v1 |
112
+ | 12 | 1 | Signing key-ID length (v2+); legacy signer-label length in v1 |
113
113
  | 13 | 1 | Optional embedded public-key length |
114
114
  | 14 | 1 | Signature length |
115
115
  | 15 | 1 | Reserved (`0`) |
@@ -128,7 +128,7 @@ bit 1 compressed
128
128
  bit 2 public key embedded
129
129
  ```
130
130
 
131
- The envelope contains no semantic content type. The Format v5 text flag still determines whether the recovered application payload should be decoded as UTF-8 text.
131
+ The envelope contains no semantic content type. The Format v6 text flag still determines whether the recovered application payload should be decoded as UTF-8 text.
132
132
 
133
133
  ## 7. Compression
134
134
 
@@ -136,33 +136,48 @@ Compression IDs:
136
136
 
137
137
  ```text
138
138
  0 = none
139
- 1 = QuadQR portable LZ
139
+ 1 = QuadQR portable LZ (legacy)
140
+ 2 = QuadQR portable raw DEFLATE
141
+ 3 = bundled Brotli
140
142
  ```
141
143
 
142
- The portable LZ stream is an LZSS-style format with groups of up to eight tokens. Each group begins with one flag byte. A flag bit of `0` means a one-byte literal. A flag bit of `1` means a two-byte back-reference:
144
+ Compression ID `1` is the original LZSS-style stream with groups of up to eight tokens. Each group begins with one flag byte. A flag bit of `0` means a one-byte literal. A flag bit of `1` means a two-byte back-reference:
143
145
 
144
146
  ```text
145
147
  12-bit offset: 1..4095 bytes
146
148
  4-bit length: stored value + 3, therefore 3..18 bytes
147
149
  ```
148
150
 
151
+ Compression ID `1` keeps the original QuadQR LZSS-style stream. Encoder levels `1..9` control candidate-history depth and bounded lazy-match effort; level 6 preserves the historical 32-candidate search depth. The 4095-byte window, 3..18 byte back-reference format, and decoder remain unchanged, and the level is not serialized.
152
+
153
+ Compression ID `2` is a raw RFC 1951 DEFLATE stream. QuadQR's portable encoder uses a deterministic fixed-Huffman block, a 32 KiB LZ77 window, distances up to 32768 bytes, and matches up to 258 bytes. Encoder levels `1..9` control candidate-chain depth and lazy-match effort; the level is not serialized and does not change decoder behavior. The QuadQR decoder accepts the stored/fixed block forms emitted by the library.
154
+
155
+ Compression ID `3` is a standard Brotli stream produced and decoded by QuadQR's bundled synchronous JavaScript codec. It uses no Node `zlib`, browser `CompressionStream`, DOM API, native addon, or network-loaded codec at runtime. Brotli is especially effective for repetitive text and structured payloads, while DEFLATE and legacy LZ remain available for deterministic selection and backward compatibility.
156
+
149
157
  Public compression modes are:
150
158
 
151
159
  ```text
152
160
  none
153
161
  auto
162
+ smart
163
+ brotli
164
+ deflate
154
165
  lz
155
166
  ```
156
167
 
157
- `compression: "auto"` uses compression only when it meaningfully reduces payload size. When it does not help and the payload is not signed, QuadQR stores the original payload directly with **no extension-envelope overhead**.
168
+ `compression: "auto"` is the fast balanced policy. It evaluates LZ level 6, DEFLATE level 6, and Brotli quality 6 exactly once and chooses the smallest complete stored representation. For unsigned payloads that comparison includes the 16-byte extension-envelope cost, so Auto remains zero-overhead when compression is not useful.
169
+
170
+ `compression: "smart"` is the opt-in CPU-heavy policy. It begins with the Auto candidates, computes the resulting Format v6 QuadQR version using the selected ECC/profile/version bounds, and only escalates when the next smaller matrix is plausibly reachable. The strong stage tests DEFLATE 8 and Brotli 9. If the symbol remains close to a smaller-version boundary, the maximum stage tests DEFLATE 9 and Brotli 11. With an explicitly requested fixed version, Smart does not chase a smaller matrix, but it may escalate when the balanced result does not fit and stronger compression can plausibly make that requested version fit. Signed and secure pipelines include their fixed envelope overhead when evaluating those version boundaries.
171
+
172
+ Explicit `compression: "lz"` and `compression: "deflate"` accept `compressionLevel` `1..9` and default to 6. Explicit `compression: "brotli"` accepts `compressionLevel` `0..11` and defaults to 11. Compression levels are encoder-only parameters and are intentionally not stored in the extension envelope because LZ, RFC 1951, and Brotli decoders do not require them. Auto and Smart use LZ level 6; Smart's staged escalation remains focused on DEFLATE/Brotli.
158
173
 
159
- Compression occurs before signing, encryption, and Format v5 ECC.
174
+ Compression occurs before signing, encryption, and Format v6 ECC.
160
175
 
161
176
  ## 8. Signed QuadQR
162
177
 
163
178
  Signature algorithm ID `1` is **Ed25519**.
164
179
 
165
- Signed payloads in extension v2 store:
180
+ Signed payloads in extension v2/v3 store:
166
181
 
167
182
  ```text
168
183
  optional compact key ID
@@ -196,13 +211,13 @@ application payload
196
211
  → optional compression
197
212
  → optional Ed25519 signature metadata
198
213
  → AES-256-GCM Secure Payload v1
199
- → Format v5 ECC/matrix
214
+ → Format v6 ECC/matrix
200
215
  ```
201
216
 
202
217
  After scanning:
203
218
 
204
219
  ```text
205
- Format v5 decode
220
+ Format v6 decode
206
221
  → AES-GCM authentication/decryption
207
222
  → internal compression/signature metadata processing
208
223
  → application payload
@@ -249,7 +264,7 @@ Recommended general-purpose starting module size is **0.40 mm/module**. Real pri
249
264
 
250
265
  ## 12. Logo safety
251
266
 
252
- Logos are rendering overlays and never modify Format v5 data structures.
267
+ Logos are rendering overlays and never modify Format v6 data structures.
253
268
 
254
269
  `size: "auto"` estimates a conservative logo ratio from:
255
270
 
@@ -305,7 +320,7 @@ These scores are regression/testing aids. They do not replace validation with re
305
320
 
306
321
  ## 15. Capacity planning
307
322
 
308
- Capacity is determined from the actual Format v5 layout, protected header, CRC, and Spectrum ECC plan. Compression can reduce stored payload bytes, while signatures and encryption add metadata bytes before the matrix codec.
323
+ Capacity is determined from the actual Format v6 layout, protected header, CRC, and Spectrum ECC plan. Compression can reduce stored payload bytes, while signatures and encryption add metadata bytes before the matrix codec.
309
324
 
310
325
  The benchmark helper can report:
311
326
 
@@ -325,7 +340,7 @@ Implementations should follow these rules:
325
340
 
326
341
  1. Keep RGBW mapping exactly `R=00, G=01, B=10, W=11`.
327
342
  2. Keep GF(256) Spectrum ECC and its errors+erasures behavior.
328
- 3. Preserve Format v5 decoding for normal and Secure Payload symbols.
343
+ 3. Preserve Format v5 decoding as a legacy compatibility path while encoding new symbols as Format v6.
329
344
  4. Keep application semantics outside the QuadQR codec. Do not require a growing content-type registry.
330
345
  5. Treat compression/signature metadata as internal transport metadata, not a separate user payload mode.
331
346
  6. Treat `keyId` only as an identifier. Signer trust comes from an external trusted public-key binding.