Decode verification in the studio
The studio does not conclude that a keepsake scans because its rules were followed. It paints the finished artwork into pixels, hands those pixels to a real QR decoder running in your browser, and compares what came back — byte for byte — with what you typed. The “Verified scannable” badge is the outcome of that comparison, repeated at 3 raster sizes, and exports stay disabled until it is earned. This guide covers what the verifier does, why it checks more than one size, how to read each verdict, and where its promise stops.
What the verifier does, step by step
The verification engine treats the rendered artwork as an image and does the following with it, in order. It loads the artwork; draws it onto a white square canvas at each of the sizes 800, 1200, 1600 px, scaled to fit and centred; decodes each raster with jsQR — a runtime dependency of the studio, not only a test tool — with inversion attempts disabled, so only dark-on-light readings count; compares the decoded bytes with the bytes of your content; and combines the per-raster outcomes into one verdict. Every step runs on your device — as the methodology page puts it, “Nothing you type or place on the canvas is transmitted in order to render a preview or produce a download.” The decoder sees the artwork as rendered, frame and photo included, not the matrix the encoder produced.
Why 3 sizes: sampling resonance
Whether a module reads cleanly depends on how its edges fall against the decoder's sampling grid, so a render can fail at one size and decode at the sizes either side of it. The verify engine's notes record a measured case — a photo-woven torture render that decoded at 800 and 1600 px but not at 1200 px — and the 3 sizes span the realistic scan-size range so that one such miss cannot decide the verdict: 800 px covers module-level detail for plain styles, 1200 px keeps the noise of a photo's halftone ring out of the authoritative cell centres, and 1600 px bounds the fine-raster end.
The fictional payload used throughout this page, https://example.com/albums/ella-and-sam, encodes at error
correction H as a version-5 symbol: 37 modules a side, 45
with the 4-module quiet zone the engine always renders. At each raster a
module spans a different, non-integer number of pixels: 17.78 px per module at 800 px, 26.67 px per module at 1200 px, 35.56 px per module at 1600 px. Those fractional widths are what shift a module's edges against the sampling grid from one
size to the next.
The 2-of-3 rule, run at build time
This page ran the verifier when it was built. The payload above was encoded, painted into pixels
at each verification size, decoded with the studio's verifyPixels, and the outcomes
were combined with combineVerifyResults. The verdict below is the engine's own
string, unchanged:
Verified scannable at 800, 1200, 1600 px (3 of 3 rasters) — decoded back to your exact content.
Had any of the 3 rasters failed to match, the build would have failed. The same combination function was then given four scripted sets of per-raster results — each result real, from decoding the painted symbol, a blank white raster, or the symbol checked against a different expected payload — to show how the rule resolves them:
| Per-raster results (fictional) | Verdict | Engine message |
|---|---|---|
| Decodes at 800 and 1600 px, nothing found at 1200 px | verified | Verified scannable at 800, 1600 px (2 of 3 rasters) — decoded back to your exact content. |
| Decodes at 800 px only | undecodable | Cannot verify: this render decoded at only 1 of 3 raster sizes (at least 2 must match). Do not print it — adjust the style (larger size, stronger contrast, shorter content, or a calmer photo) until verification passes. |
| Nothing found at 800 and 1600 px, different content at 1200 px | mismatch | Cannot verify: the render decoded to DIFFERENT content than you entered. Do not print it — this render is not safe to use. |
| Decodes at 800 and 1600 px, different content at 1200 px | verified | Verified scannable at 800, 1600 px (2 of 3 rasters) — decoded back to your exact content. |
Reading a verdict
The engine's statuses are verified, mismatch, undecodable and error. Verified means at least 2
rasters decoded to an exact byte match; the message names the sizes that passed and how many of the
3 they were. Undecodable means too few rasters found a code at all; the
message gives the pass count and the threshold and says not to print. Mismatch means a code was found but it decoded to content other than what you entered;
the engine treats this as the most serious failure and says the render is not safe to use. Error means a raster could not be produced or decoded — the image failed to load, canvas was unavailable,
or rasterization failed — and too few others matched to verify.
The precedence is fixed: the pass count is checked first, and only when it falls short does the
engine look for a mismatch, then undecodable rasters, then errors. So a single mismatch outranks
any number of undecodable rasters once the count is short, and two exact matches outrank one
raster that read as something else — in the last scripted set the verdict is verified, with the odd raster still listed among the failures as 1200 px (mismatch). The verdict object always records which rasters passed and which did not; only
the verified message lists the sizes.
Bytes, not appearance
The comparison is between byte streams, not between displayed strings. The engine prefers the
decoder's raw bytes and falls back to string equality only when no byte stream is available.
Checked at build: the payload against itself matches; the
payload against the same link with a trailing slash added does not match. Byte length also differs from character count once
text leaves plain ASCII: the fictional message Café on the corner — 7pm is 24
characters but 27 bytes, and the symbol carries the bytes — which is why the photo-woven
cap is given in ASCII characters with the caveat that accented characters and emoji use more of the
symbol's byte capacity.
The rules that run before the verifier
Verification is the last check, not the only one. The artistic engine enforces decode-measured rules that make a pass likely before a pixel is decoded; they never substitute for the decode itself.
- Error correction H for every artistic style. Decoration spends decode margin, and the engine's rule is that only the ~30% recovery of EC-H leaves enough; each artistic entry point rejects a model encoded at any other level. The error-correction guide covers the trade against symbol density.
- Contrast is rejected, not warned about. Artistic styles require at least 4.5:1 by the WCAG 2.x formula. The default heart-module colour on white measures 9.57:1 at build; a fictional pale pink is refused with “Artistic styles require at least 4.5:1 contrast (got 2.16:1) — decoration already spends decode margin.”.
- Inverted codes are rejected. A fictional white-on-rose attempt returns “Artistic styles require dark modules on a light background — inverted codes are rejected.”.
- Finder and alignment patterns stay classic squares; the engine's spike found that restyling alignment patterns breaks decoding outright. Heart modules cover 0.97 of their cell so a sampling grid still reads each cell as decisively dark.
- Photo-woven codes keep a pure centre and a version cap. Each data cell keeps an authoritative module-colour centre 0.5 of the cell side, and payloads encoding above version 4 (about 34 ASCII characters) are refused outright. A fictional 74-character link encodes as version 8 and is turned away with: “Photo-woven codes need short content — use a short link or pick another style. This content needs a version-8 code; photo weaving is decode-proven only up to version 4 (about 34 characters, fewer with emoji or accents).”
What verification cannot promise
The verifier decodes pixels on your screen, flattened onto white. It cannot see your printer, paper, ink, frame glass or room light, and the methodology page is explicit about the gap: “Contrast and sizing guidance follows scanning practice, but printers, papers, and inks vary. Test a proof print before a large run.” A badge is a reason to order a proof, not to skip one. The engine's print-size readout at 300 DPI helps size that proof — its PNG presets come out as:
- 512 px — 1.71 in × 1.71 in (43.3 mm × 43.3 mm) at 300 DPI
- 1024 px — 3.41 in × 3.41 in (86.7 mm × 86.7 mm) at 300 DPI
- 2048 px — 6.83 in × 6.83 in (173.4 mm × 173.4 mm) at 300 DPI
Nor does a verdict say anything about where the code leads. The studio verifies the format of what you encode, not the future of the service it points to, and a static code cannot report scans. Both limits are set out in what the studio does not model.
Common misreadings
“A badge means every print will scan”
It means this exact artwork decoded back to your content at 2 or more of 3 raster sizes on your device. Printing is a different medium; test a proof print.
“Verified once, verified for every version of the design”
The engine's per-raster wording is deliberate: “Verified scannable — this exact artwork was decoded back to your content.” The verdict belongs to the render that was decoded; a different render needs its own.
“Verification checks that the link works”
It checks that the bytes in the symbol equal the bytes you entered. Whether the destination stays live is outside the model — the methodology page recommends pointing the longest-lived keepsakes at destinations you control.
For how the editor arrives at that artwork — occasions, templates, module styles, palettes — see how the studio composes a keepsake, or browse all guides. The verifier itself runs in the Studio as you type.
Frequently asked questions
What does "Verified scannable" actually mean?
The exact artwork on screen was painted into pixels at 3 sizes (800, 1200, 1600 px), each raster was decoded with jsQR in your browser, and at least 2 of them decoded to a byte-for-byte match of the content you entered. It is a statement about a decode that happened, not about rules that were followed.
Why does the studio check more than one size?
Because a single fixed raster can false-negative through sampling resonance: a render that fails at exactly one size can decode cleanly at the sizes either side of it. Requiring 2 of 3 rasters means one resonance miss cannot flip the verdict on its own.
Why am I told not to print a render that decoded at one size?
One pass is below the 2-of-3 threshold, so unless another raster read as different content (reported as a mismatch instead) or the other rasters could not be produced at all (reported as an error), the verdict is "undecodable". The remedy the engine names is to adjust the style — larger size, stronger contrast, shorter content, or a calmer photo — until verification passes.
Is a "mismatch" worse than "cannot verify"?
Yes. A mismatch means a code was found but it decoded to different content than you entered; the engine reports it ahead of any undecodable raster because a render that reads as something else is never safe to use. Both verdicts keep downloads disabled.
Does the badge guarantee my printed keepsake will scan?
No. Verification decodes pixels on your screen, flattened onto white. It cannot see your printer, paper, ink, glass or lighting, and the methodology page is explicit that printers, papers and inks vary — test a proof print before a large run.
Is anything sent to a server during verification?
No. Rasterizing and decoding run in your browser; the methodology page states that nothing you type or place on the canvas is transmitted to render a preview or produce a download.
A note on scope: this guide describes the verification engine's rules as implemented and tested; every figure above was produced at build time by that engine on fictional inputs. Composition and verification happen in your browser. See the methodology page.