Photo QR Code
Put the picture inside the code: halftone shading blends a photo into the code's own cells, so the framed print shows your faces and still opens your link. The photo never leaves your browser, and the finished artwork is decoded back to your content before you can download it.
Keepsake print checklist
- Keep the white margin around the code — it is the quiet zone scanners need, not empty space to crop.
- Size for the moment: 2 × 2 cm of code is the floor for cards and tags; photo-woven codes reward 4 cm or more; distant reveals want one tenth of the scanning distance.
- Matte paper over gloss — glare is the most common keepsake scan failure.
- Scan a test print with a real phone before the moment that matters.
A worked example, rendered by the real engine
Generated at build time by the same halftone pipeline as the tool above — here blending a synthetic gradient instead of a photograph (your photos never touch our build servers either). It encodes:
https://example.com/us
Version 3 at 29×29 modules, error correction H, pure centers at 50% of each cell. Scan it from the screen.
What keeps a photo code scannable
Read the guide for the mechanism end to end: Photo halftone codes, explained walks through how a picture is quantised into a code's cells and why the centres stay pure. This page applies those rules to the photo preset itself — what it encodes, what the engine measured, and what the numbers on the example above mean.
The photo preset starts on the Photo-woven style, the only one of the site's 7
styles that draws something other than the module colour inside a data cell. Its content type is
a link: the worked example encodes a fictional link, https://example.com/us, 22 characters, which the encoder places in a version 3 symbol of 29×29 modules at error correction H. Add the mandatory quiet zone of 4 modules on each side and the artwork is 37 modules across. Those figures, like every
engine figure on this page, are computed when the page is built — by the same engine that runs in
your browser, on the same preset — so they cannot quietly disagree with the tool.
- Authoritative centres. The middle 50% of every data cell's side is pure code colour — 25% of the cell's area — and that square sits where a decoder samples, near the centre of the cell. The methodology records it as a measured floor, not a preference: the next setting down that was tried fails to decode. Sampling near the centre is what lets the photo own the ring around it.
- Three honest tones. The ring shows the photo quantised to dark, mid and light at fixed luminance thresholds — dark below 85, light above 175, mid between, on the 0–255 scale — so a ring pixel can never pose as a confident module.
- Clean landmarks. Finder corners, alignment squares, timing tracks and the quiet zone carry no photo at all. In the example, 202 of the 841 cells are landmarks drawn pure; the other 639 are data cells that carry the picture.
- Maximum error correction, no logo. Level H — roughly 30% recovery — always, because decoration spends the margin error correction provides; and never a logo on top, because the photo already spends the margin a logo would need. The engine rejects both a lower level and a logo rather than warning.
Inside one cell of the example
It helps to see the rule at the scale the engine works at. The engine maps the symbol onto a centred square crop of the photo and splits each data cell into a 3×3 grid of subcells, 9 per cell. For every subcell it averages the luminance of the photo pixels underneath and picks one of three tones: subcells darker than 85 are drawn in the module colour, subcells up to 175 in the mid tone, and lighter subcells are left as the background. Then it draws the centre square — 50% of the cell side — on top, in the pure module colour for a dark module and the pure background for a light one. The ring is whatever shows around that square: 75% of the cell's area belongs to the picture, 25% to the data.
Counted back out of the artwork above, which the engine rendered when this page was built: the example has 639 data cells, so 5751 photo subcells. On the synthetic gradient, 2446 of them fell below the dark threshold, 2716 landed in the mid band and 589 were light enough to stay as background — which is why a gradient was chosen: it exercises all three tones, so the pipeline the page proves is the whole pipeline. The 639 centre squares match the 639 data cells exactly, and the 202 remaining cells — 147 in the three finder corners, 25 in 1 alignment pattern, 30 in the timing tracks — were left pure: the dark ones drawn as whole squares, the light ones as untouched background, with no photo in any of them. The page refuses to build if that census stops adding up.
The mid tone deserves a sentence of its own, because it is the only colour in the artwork that is allowed to be ambiguous. The module colour measures 21:1 against the background by the WCAG formula, far above the 4.5:1 gate every artistic style must clear — the engine rejects weaker pairs rather than warning, because scanners binarise the image. The mid tone, measured the same way, sits at 3.36:1 against the background. It is never a module colour and is never held to the gate; the engine keeps it in the ring and out of every centre square and every landmark, which is the arrangement that lets a picture be visible without inventing modules.
Why a photo code refuses long content
Every data cell gives 75% of its area to the picture, so a decoder is working from centres with 25% of the area it would see on a plain code. That is survivable at low versions and measurably not at high ones: as the version rises the cells shrink, and with them the pure centres, until they fall below what a decoder can sample. The engine's decode sweep rendered photo codes across a range of versions with three kinds of photo — a smooth gradient, blocky noise and per-pixel noise — at several raster sizes, rotated, and after lossy JPEG recompression, judged with the same multi-raster verification rule the site uses. Version 4 and the version below it passed every condition for every photo and every payload variant under that rule — the engine's notes add that rotated smooth-gradient renders are marginal at every version, a property of the mid-tone ring rather than of version density, which is why that condition carries no cap signal; the next version up failed part of the sweep after JPEG recompression; the version after that failed outright; and further up the smooth photo stopped decoding at every verification raster size even clean and unrotated. So the cap is 4: the highest version at which it and everything below it passed everything.
In characters, the cap is about 34 of ordinary text or link — the byte-mode capacity
of a version-4 symbol at level H — and fewer with emoji or accented letters,
which take several bytes each. The example link is 22 characters and lands
at version 3, under the cap. A fictional link of 38 characters,
https://example.com/albums/summer-2026, needs version 5; asked to weave a photo into it,
the engine refuses, and the refusal is worth reading in full because it is the message you
would see:
“This content needs a version-5 code, and photo weaving is decode-proven only to version 4 (about 34 characters, fewer with emoji or accents). Three ways forward: pick a different style, use a short link instead of a message, or switch to an inscription — uppercase letters, digits and simple punctuation fit 50 characters in the same photo code.”
That refusal is the engine working as designed. It does not clamp the content, shorten the link or quietly drop the photo; it returns an error rather than a render, so there is no artwork to verify or download. The third way forward it names — an inscription — is the one worth knowing about: folded into the QR alphanumeric character set, the same version-4 symbol holds 50 characters instead of 34, with no change to the decode margin at all.
A message instead of a link
The preset's own placeholder puts it plainly: the picture is the message, but you can hide one here too. Give the code a message instead of a link and it becomes a message keepsake with the two modes the methodology describes — a reveal link or an offline note, the note taking either its plain form or its inscription variant — with one extra constraint: the version cap above applies whichever form the message takes.
The modes exist because a code containing bare text carries no scheme, so a phone has nothing
to act on. Apple's Camera and Google's ML Kit recognise links, phone numbers, email addresses,
contact cards, calendar events and Wi-Fi joins — and no plain-text type. Faced with bare text,
an iPhone offers to copy or search it and Google Lens simply runs a web search; Samsung phones
and dedicated scanner apps do display it, but a keepsake cannot depend on which phone opens
it. A reveal link therefore encodes an ordinary link to this site's reveal page
and carries the words in the part after the #, which is stripped before a request
is made and so never reaches a server, a CDN or a log. An
offline note writes the words into the code itself, needs no connection ever and
accepts the display caveat. An inscription is not a third mode but the offline
note folded into the alphanumeric character set — uppercase, accents flattened, a few substitutions,
anything else removed — which buys capacity and changes nothing about display.
Worked through with a fictional 39-character message, The four of us at the lake, summer 2026: as a reveal link it becomes a 94-character link whose fragment alone
runs to 65 base32 characters, and it needs version 9 — the reveal format itself allows up to 300 characters, so it is the photo cap,
not the format, that this message hits. As an offline note it needs version 5, also over the cap. As an inscription it becomes THE FOUR OF US AT THE LAKE SUMMER 2026 — 38 characters, uppercased with the comma folded away — and fits at version 4, which the engine accepts and weaves. In a photo code, then, the two modes meet the cap
differently: an offline note of up to 34 plain characters always fits, or up to
50 as an inscription; a reveal link spends a fixed 29-character prefix — scheme, host, path and the # — before the message even begins,
against a cap of about 34 characters. One engine fact is worth knowing whichever
form fits: the halftone renderer draws no caption of its own; captions belong to the frame templates.
What verification proves for a photo code, and what it cannot
Every render on this page is decode-verified before it can be downloaded. Your browser rasterises the finished artwork — rings, centres and all — at 3 sizes, 800, 1200, 1600 pixels, runs a real QR decoder (jsQR) on each, and compares the decoded bytes with the exact content you entered. The badge is earned when at least 2 of the 3 rasters match byte for byte; anything less shows a warning and keeps the downloads locked. The 3-raster rule exists for a measured effect the engine calls sampling resonance — a render can fail at exactly one raster size and decode cleanly at the others — and the render it was measured on was a photo code: the engine's notes record one at the cap, over per-pixel noise, that failed at exactly one of the 3 sizes and decoded cleanly at the rest. A single fixed raster would turn that one miss into a false failure.
What that proves: this exact artwork, at screen resolution, decodes back to this exact content. What it cannot see: your printer, your paper or the phone that will do the scanning. The methodology's guidance is short. Print at least 2 × 2 cm for arm's-length scanning, keep the quiet zone uncropped, and scan a test print with a real phone before the moment that matters. A photo code adds one measured refinement to the first point. The engine's minimum-size readout keeps the part of a cell a scanner actually sees at or above 0.5 mm. On a plain code that is the whole cell; on a photo code it is the 50% centre, so the nominal module must be 1 mm and the example's 37 modules come out at 37.0 mm (1.46 in) wide, against 18.5 mm for the same symbol drawn plain. That it is 2× larger for the same reliability is the plain arithmetic of a centre 50% of the cell side. If a verified photo code will not scan in print, that is exactly the report the contact page asks for; confirmed issues are fixed in the engine and locked in with a test.
Common misreadings
- Cropping the quiet zone. The clear margin is part of the code: 4 modules on every side, always drawn, never optional. At the example's minimum print size that is 4.0 mm per side of apparently empty paper — trimming it to make the picture larger removes part of the code.
- Expecting the photograph. The ring shows a three-tone halftone of a centred square crop, sampled 9 times per data cell — 5751 samples in the example. The eye assembles the rings into the picture; the scanner reads the centres. It is not a print of the photo, and detail finer than a subcell is averaged away by design.
- Expecting the photo to be in the code. It is not. The code carries the link or the message; the rings carry the picture; a scanner samples near the centre of each cell, where the pure square is. Nothing about the photo is encoded, and nothing about it leaves your browser — it is read, sampled and blended on your device.
- Expecting bare text to display. An offline note or inscription in a photo code is bare text, and as above an iPhone offers to copy or search it while Google Lens runs a web search rather than showing it. The reveal link exists for that reason, and in a photo code its fixed 29-character prefix is spent before the message begins, against a cap of about 34 characters — so an inscription in a photo code carries the display caveat in full.
- Asking for a logo. The engine rejects a logo on a halftone code outright: the photo already spends the decode margin a logo would need. A link, a picture and a logo on one symbol is not a style this engine will render.
- Worrying about contrast. The halftone renderer's default module colours are black
on white: the worked example passes it no colours of its own, and read back out of its artwork
they are
#000000on#ffffff, measuring 21:1 against the 4.5:1 gate — so the gate every artistic style must clear is cleared by the defaults. The three tones in the ring are not the code's contrast; the centres are.
The rules above are the methodology's, measured and pinned by tests, and the photo halftone guide covers the mechanism without the preset. The other guides cover the pieces this page leans on: decode verification, printing and scanning, and message keepsakes and reveal links.
Photo code FAQ
What does the photo preset actually encode?
A link. The preset's content type is a URL, and the worked example on this page encodes a fictional link, https://example.com/us — 22 characters, placed in a version 3 symbol at error correction H. The photo is never encoded: it is drawn into the outer ring of each data cell, and a scanner samples near the centre of each cell, where the pure square is. Change the link and the code changes; change the photo and only the rings change.
Is my photo uploaded to your servers?
No. The photo is read directly by your browser, sampled on your device, blended into the artwork locally, and discarded when you close the page. It never crosses the network — this site has no photo storage at all.
What photos work best?
Ones that survive three tones. The engine maps the symbol onto a centred square crop of the photo and averages the luminance under each subcell, calling it dark below 85, light above 175, and mid between — so a subject with clear light-and-dark structure stays recognisable while a busy texture reads as noise. A wide photo loses its sides to the square crop, so crop toward the subject before choosing the file. If verification fails on a photo, a calmer crop is the first thing to try.
Why does the code look denser than a plain one?
Two reasons. The first is a measured rule of this engine: every style that redraws modules — hearts and the photo halftone — encodes at level H, roughly 30% recovery, because decoration spends the margin that error correction provides, and the engine refuses to weave a photo over a lower level. Only a framed code without heart modules — the one style that leaves the symbol untouched on its panel — may carry level Q when the generator's opt-in smaller-code setting is chosen; the engine's notes record that as buying back one to two versions. A photo code can never take that saving — the blend refuses any model below level H — and for this page's example link there is none to take: asked for level Q, the encoder returns level H at version 3, because it raises the level whenever that costs no extra version, and the photo weaves over that model exactly as it does over the one requested at H. Then the halftone style fills only the centre 50% of each data cell's side with pure code colour and gives the rest to the photo, so the eye sees a finer, busier texture than a plain code of the same version. The landmarks — finder corners, alignment squares, timing tracks, and the quiet zone — stay fully clean, and no logo can be added on top: the photo already spends the margin a logo would need.
Why was my link or message refused as too long?
Because it needed a code above version 4, the cap the engine measured for photo weaving: denser symbols shrink the pure centres below what a decoder can sample reliably. In practice that is about 34 characters of ordinary text or link, fewer with emoji or accents, and up to 50 characters as an inscription (uppercase letters, digits and simple punctuation). The refusal is deliberate — the engine rejects rather than renders a code it has measured to fail — and the message it shows names the three ways forward: a different style, a shorter link, or an inscription.
Can a photo code carry a message instead of a link?
Yes. The preset's content type is a link, but the content can be a message instead, and a message keepsake here takes the two modes the methodology describes: a reveal link, which encodes a link to this site's reveal page with the words carried in the part after the #, so they never reach a server; or an offline note, which writes the words into the code itself. An offline note can also be set as an inscription — the same note folded into the QR alphanumeric character set so more of it fits. The photo code's version cap applies whichever form the message takes. For the fictional 39-character message worked through on this page, only the inscription fitted: the reveal link needed version 9, the offline note version 5, the inscription version 4.
How large should a photo code be printed?
Larger than a plain code of the same version, by construction. The engine's minimum-size readout keeps the part of each cell a scanner actually sees at or above 0.5 mm; a photo code's pure centre is only 50% of the cell side, so its nominal module must be 1 mm and the worked example's 37-module width comes out at 37.0 mm (1.46 in) — against 18.5 mm for the same symbol drawn plain. The methodology's floor of 2 × 2 cm for arm's-length scanning still applies, the quiet zone must stay uncropped, and the only final test is a test print scanned with a real phone: on-screen verification proves the artwork decodes at screen resolution and cannot see your printer, your paper, or the phone.
Prefer the photo beside the code instead of inside it? Use a framed code next to your print, or the proposal code for the question itself. Photo codes also make striking memorial pieces and pet keepsakes.