Gradient Dense Code
A QR code whose modules hold levels, not bits
Standard QR geometry, but every data module is printed at one of 4, 8 or 16 gray levels — 2 to 3× the data in the same area.
2–4
Bits per module
8,883B
40-L capacity, 8 gray
11
Format revisions
7
Tests
01The problem
A QR module stores exactly one bit: black or white. The largest QR code tops out at 2,953 bytes, however good the printer and camera are.
02The approach
Keep everything that makes QR frameable — finders, timing lines, alignment and format areas, quiet zone — and let each data module carry 2–4 bits as calibrated gray (or RGB) levels, with a decoder built to survive real cameras.
03Architecture — the camera follows the data
How it works
> Decoding runs it backwards through lighting correction, k-means and deblur.
Step 1: Payload
zlib if smaller
Step 2: Header
format · length · CRC-16
Step 3: Reed-Solomon
QR L/M/Q/H shares
Where the work happens
Step 4: Interleave
then whitening
Step 5: Levels
Gray-coded, 4 / 8 / 16
Where the work happens
Step 6: Place
QR's zigzag order
04Perks — why it's built this way
The edge
2–4 bits per module
4, 8 or 16 levels per module; zlib is applied automatically when the payload compresses.
Self-describing
The profile — levels, gray or RGB, ECC — is BCH-coded into QR's own format modules, so the decoder needs no settings.
Built for real cameras
Homography plus a moving-least-squares residual field for lens and paper curl, lighting correction from known modules, per-channel k-means, and decision-feedback deblurring.
Still frames like QR
OpenCV's standard QR detector locates the symbol — a test checks it.
05Tech — and what each part does
The stack
- Python 3.12
- Encoder, decoder, CLI
- Reed-Solomon
- QR-matched L/M/Q/H parity
- BCH + CRC-16
- Profile and payload integrity
- OpenCV
- Finder detection, webcam scan
- k-means (1-D)
- Level clustering per channel
- zlib
- Automatic compression
06Evidence
The proof
- Version 10 (57×57)
- GDC · 4 gray: 421BStandard QR: 213B
- Version 40 (177×177)
- GDC · 4 gray: 4,585BStandard QR: 2,331B
Incompressible bytes at error-correction level M, same symbol version. From the repo's capacity table.


07Known limits — stated plainly
Where it doesn't win
- Only the GDC decoder reads the payload. Phone QR apps recognise the shape but not the levels.
- Robustness numbers come from simulation; real printer and phone validation is the next step.
- 16 levels and RGB need sharp, high-resolution captures — JPEG chroma subsampling hurts RGB in particular.