# Whole-field QR research

[Open the offline image gallery](gallery.html) for enlarged previews and PNG/SVG downloads.

**Yes: the pattern can occupy the whole QR, including the error-correction region. There does not have to be a picture in the middle.** Six prototypes below do exactly that and encode **`https://jdt.engineering`**, with no redirect, new hostname, query, or fragment. Two additional examples show what conventional URL/mask searching achieves without custom padding. No DNS was created.

[![Eight research prototypes](contact-sheet.png)](contact-sheet.png)

The first six are actual black-and-white module patterns, not artwork behind tiny QR dots. Every nonstructural module receives equal target weight, right to the edges. There is no unconstrained outer artwork border and no reserved central drawing area. The four-module white quiet zone remains outside the symbol.

| Prototype | Whole matrix matching target | Nonstructural field matching | ZXing / OpenCV | Exports |
|---|---:|---:|---|---|
| Woven waves | 81.3% | 86.3% | yes / yes | [PNG](prototypes/woven-waves/qr.png) · [SVG](prototypes/woven-waves/qr.svg) · [target](prototypes/woven-waves/target.png) |
| Diagonal braid | 81.2% | 86.0% | yes / yes | [PNG](prototypes/diagonal-braid/qr.png) · [SVG](prototypes/diagonal-braid/qr.svg) · [target](prototypes/diagonal-braid/target.png) |
| Wave interference | 83.4% | 86.9% | yes / yes | [PNG](prototypes/wave-interference/qr.png) · [SVG](prototypes/wave-interference/qr.svg) · [target](prototypes/wave-interference/target.png) |
| Contour field | 82.9% | 86.9% | yes / yes | [PNG](prototypes/contour-field/qr.png) · [SVG](prototypes/contour-field/qr.svg) · [target](prototypes/contour-field/target.png) |
| Square textile | 82.3% | 87.4% | yes / **no** | [PNG](prototypes/square-textile/qr.png) · [SVG](prototypes/square-textile/qr.svg) · [target](prototypes/square-textile/target.png) |
| Radial lattice | 83.0% | 87.3% | yes / yes | [PNG](prototypes/radial-lattice/qr.png) · [SVG](prototypes/radial-lattice/qr.svg) · [target](prototypes/radial-lattice/target.png) |
| Conventional root URL, mask search | 52.2% | — | yes / yes | [PNG](prototypes/canonical-root/qr.png) · [SVG](prototypes/canonical-root/qr.svg) |
| Conventional URL + mask search | 53.1% | — | yes / yes | [PNG](prototypes/canonical-url-search/qr.png) · [SVG](prototypes/canonical-url-search/qr.svg) |

Target agreement measures resemblance, **not scan reliability or error-correction consumption**. These exact-codeword prototypes intentionally damage zero data/codeword modules. The square textile is retained as a useful failure case: repeated finder-like squares confuse OpenCV at the export size, although ZXing reads it and OpenCV reads its smaller and lightly blurred versions. Prefer braid, interference, or contour as the next direction.

The six art exports use versions 10–14; the two conventional controls use version 6. Consequently **52–53% versus 81–83% is not a controlled solver benchmark**: resolution, targets and search budgets differ. Only the two version-6 URL-search controls are directly paired. The separate byte-versus-bit comparison below uses matched versions/targets, and the manifest retains every candidate's mask and metrics.

## Why the whole field is possible

A short payload leaves spare data bytes. Changing these changes the Reed–Solomon parity, too. Both are linear over bits, so solve a system of equations that specifies selected visual modules across *both* regions. Remaining mismatches can be distributed throughout the pattern instead of collecting at the sides. This is the principle in [Russ Cox's original QArt explanation](https://research.swtch.com/qart).

The existing app/atlas solver already supports this: give its target nonzero weights everywhere outside function patterns. Its central-image appearance was a target/priority choice. Its byte-level solver and this experiment's new bit-level solver both achieve roughly 81–83% whole-matrix agreement on these targets; this bounded experiment does **not** establish that the new solver is generally better. The new solver lets individual bits become constraints and shuffles equal-priority constraints across the field. It greedily accepts independent constraints; it is not a global minimum-error optimizer.

For version 10-L and this exact URL, there are **1,984 free padding bits**. They cannot independently determine every data/parity module. Constraints that conflict with the already selected ones become visual noise, while all parity is recomputed exactly. The [pinned furious-qr source](src/core/upstream/typescript/src/furious-qr.ts) supplies QR layout/RS arithmetic; [bit-field.ts](src/bit-field.ts) contains the new binary linear solver. The upstream author also describes the padding/parity trade at [furious-qr](https://github.com/zhengkyl/furious-qr#pixel-art-solver).

## What remains fixed, and what symmetry means

Finder patterns, separators, timing, alignment, format/version bits and the mandatory dark module remain untouched. Alignment is part of Model 2's distortion handling; it is not safe to erase it just because one decoder sometimes tolerates that. The clear outside margin stays four modules wide. [DENSO: Model 2](https://www.qrcode.com/en/codes/model12.html), [DENSO: quiet zone](https://www.qrcode.com/en/howto/code.html).

Beautiful global texture and exact geometric symmetry are different goals. An exhaustive fixed-layout check over 40 versions × 4 ECC levels × 8 masks found fixed-module conflicts with exact 90° and 180° rotation for **every version 2–40 layout**. Version 1 has a few necessary-condition exceptions; that does not prove any complete payload/parity code exists with that symmetry. None of the tested layouts permits exact main-diagonal reflection. See [analysis.json](analysis.json) and the reproducible [analysis script](src/analyse.ts). Thus the prototypes use approximate symmetry and repeated motifs, not a claim of mathematically perfect symmetry. A circular composition still has a square QR lattice and square quiet zone.

## Degrees of freedom worth using

| Choice | What it buys | Consequence |
|---|---|---|
| Short exact root URL + custom padding | Thousands of adjustable bits at larger versions; same scanned URL | Strongest demonstrated result here; padding is noncanonical |
| Different masks | Eight deterministic arrangements | Helps visual fit; searching masks is cheap; no extra information capacity |
| Higher version | More spare data and finer artwork | More modules to resolve at the same printed size; not automatically easier to scan |
| Lower ECC | More data/padding freedom at fixed version | Less physical damage tolerance; preserve exact parity rather than spending it on painted errors |
| Hostname case | Different encoded bytes, same host | Valid host equivalence; small freedom compared with padding |
| Uppercase URL + alphanumeric encoding | Can reduce payload-bit cost | Promising next experiment; this prototype uses byte mode and did not benchmark this route |
| `#` + long numeric fragment | Large, standards-encoded art payload, potentially near 10 bits freedom per 3 digits | Visible long scanned URL; page scripts/anchors may react; needs constrained numeric solver |
| Query parameter | Searchable legal payload characters | Sent to the server; caching/application behavior must be checked |
| Arbitrary subdomain | More searchable payload bytes, shorter aliases possible | Needs DNS, TLS and host routing; longer names consume capacity; none created here |

Host and scheme case-insensitivity and fragment/query semantics come from [RFC 3986 §§3.2.2, 3.4, 3.5, 6.2.2.1](https://www.rfc-editor.org/rfc/rfc3986.html). A fragment is interpreted client-side, not sent as part of the HTTP resource request; it is therefore preferable to inventing DNS for a first experiment, although it is not guaranteed invisible to website JavaScript. Do not assume arbitrary queries or subdomains behave like the existing root site.

The conventional-padding control searched 128 deterministic hostname-case/fragment URLs × eight masks. Its best full-field target agreement rose from 52.2% to 53.1% relative to a root-only eight-mask control at the same version. This small bounded search is **not a proof of the best possible URL optimization**. Exact winning text is in the manifest. Joint search over URL, encoding segmentation, mask, solver ordering and target phase is the sensible next optimization; creating subdomains first is unnecessary.

A fully conventional-padding alternative is the original QArt approach: encode a URL prefix followed by a numeric fragment, solve its art bits, and repair any 10-bit digit groups above 999. It buys almost the same bit freedom while retaining normal segment/terminator/padding rules. That more involved constrained solver was researched, **not implemented in this bounded batch**. It is the recommended next implementation if strict data-stream construction matters more than keeping the scanned URL short.

At fixed version 10 and the same wave target/mask, measured whole-matrix agreement was L **81.3%** (1,984 free bits), M **73.4%** (1,520), Q **65.0%** (1,024), H **62.2%** (768). This illustrates the capacity tradeoff, not a scanner ranking. DENSO explains the four levels and RS correction in [its error-correction guide](https://www.qrcode.com/en/about/error_correction.html).

## Validation and boundaries

- Eight selected PNGs: **8/8 exact ZXing payloads**, **7/8 exact OpenCV payloads** at eight pixels per module.
- All eight matrices independently reconstructed from raw data, ECC and mask using [Nayuki's independent QR encoder](https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py); every module matches. This checks codeword parity and required structures, not formal certification.
- At four pixels per module, 90° rotation, and mild Gaussian blur, ZXing passes **24/24** additional checks. OpenCV passes **23/24**; the repeated-square design fails rotation at the original scale.
- All six art examples decode exactly `https://jdt.engineering`. The conventional searched example decodes a hostname-case/fragment variant on the same host. Scan tests did not verify the website's JavaScript behavior for that fragment.
- The art examples use **noncanonical bytes after the terminator**, rather than the specification's normal alternating pad bytes. Decoders here ignore them, and parity is correct; do not call this formally ISO-conforming on the basis of decoding alone. Masks were chosen for artwork/decoding rather than minimum standard visual penalty, including the conventional-padding controls.
- This batch has no phone-camera, print, perspective, or damaged-symbol qualification. Earlier phone tests of other designs do not qualify these new ones.

Nested/multiple symbols do not solve this aesthetic problem: they add finders and choose one symbol when scanned. The existing atlas's nested experiments are separate; none of these eight is claimed recursively scannable. Raster overlays, intentional errors, invisible module-center tricks and replacing the scanner with a custom recognizer were not used.

## Reproduce / inspect

The [manifest](manifest.json) contains parameters, exact URLs, codewords, metrics, hashes and decoder results. Each prototype includes PNG, SVG, target PNG, matrix text and full result JSON. `work/` retains all 50 candidate matrices and six byte-solver controls, so the selection is inspectable rather than hidden. This is a small feasibility study, not a new mass-produced atlas.

Run in this directory (native WSL recommended):

```sh
# Generated standalone scripts already included; Node has no runtime dependency.
node src/generate.mjs
node src/analyse.mjs
# Existing workspace Python environment; never install into system Python.
source ../.venv/bin/activate
python src/validate.py
```

For a fresh checkout, create and activate a local venv and install `numpy pillow opencv-python-headless zxing-cpp qrcodegen` before validation. The contact sheet uses the existing `../patterns/fonts/Panton-{Regular,Bold}.otf` files. To rebuild standalone JS after editing TypeScript: `npm ci && npm run build`. Upstream license and pin: [provenance](src/core/UPSTREAM.md). Generation never creates DNS, calls a hosting API, or changes the existing gallery.
