QR codes worth keeping
A proposal, a wedding table, a framed photo — some codes deserve better than black-and-white squares. Pick the occasion, choose a template from the gallery, and put a link, a hidden message, or a contact card inside. KeepsakeCodes decorates the code and then proves it still scans: every render is decoded right in your browser and byte-checked against your content before you download it. Nothing you type or upload leaves your device.
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.
The keepsake tools
Proposal QR Code
A heart-framed code with heart-shaped modules — scan it to reveal your question, your song, or your page.
Wedding QR Code
Framed art codes — floral, ring, classic, or confetti — for guest photo collection, invitations, and registries.
Baby Reveal QR Code
The announcement hides inside a confetti-framed code until everyone scans at once — no internet needed.
Gender Reveal QR Code
Boy or girl? The answer hides inside a violet confetti-framed code until the party scans on three.
Valentine QR Code
A love note encoded in a code literally made of hearts — scanning shows your words, offline, forever.
Gift Tag QR Code
A classic-framed tag carrying a hidden message or a video link — recolored to match the wrapping.
Treasure Hunt QR Code
Clue-chain codes with the riddles inside — a hunt that works with zero internet, indoors or out.
Memorial QR Code
A dignified framed code for cards and plaques — the tribute link is in the image, so it can never expire.
Pet Tag QR Code
A collar contact card: finders scan and your number appears, one tap from calling — no subscription service.
Photo QR Code
Blend a photo into the code itself with halftone shading — the picture shows, the code still scans.
Made by the engine, at build time
This heart-framed, heart-module code was rendered when this page was built — by the exact
engine the generator above runs in your browser. It encodes
https://keepsakecodes.net/; scan it from the screen and you land right here. If the
engine ever produced art that could not decode, this page would fail to build.
Three promises before you print
- Verified scannable. Decoration is risk, so we do not guess: the finished artwork is rasterized and run through a real QR decoder on your device. Only an exact byte match earns the badge — and downloads stay locked without it.
- No shortener, no tracking. Your link or message is encoded directly into the image — no redirect through our servers, no subscription keeping it alive. A message is also printed on the keepsake itself, so it can be read by eye at the twentieth anniversary whatever phones have become by then.
- Browser-only. Content and photos are processed entirely on your device and never uploaded. There is no account and nothing is stored unless you choose "Save to this device" — saved keepsakes live in your browser's library, still with no account. How each style stays scannable is documented on the methodology page.
The three styles, and the rules each one is held to
Every keepsake made here is one of 7 selectable styles in three families — 5 frames, heart modules, and the photo-woven halftone — and each family is governed by rules the
engine measured with a real decoder rather than chose by taste. One of those rules applies to all
three: the three large finder squares and the smaller alignment patterns are always drawn as classic
crisp squares, because restyling alignment patterns broke decoding outright in testing; timing tracks
may be styled, and the 4-module quiet zone around the symbol is always
drawn and never covered by frame art. The error-correction rule follows what a style does to
the modules. Hearts reshape every dark module outside the landmarks and the halftone weaves a
photo through the data cells, so both encode at level H — roughly 30% recovery — without
exception, and the engine refuses to draw hearts or a photo over a model encoded at a lower
level. A frame places an unmodified symbol on its panel, so a framed code alone may also carry
level Q: in the generator above that is the opt-in "Smaller code (less error correction)"
control, available only with a frame and no heart modules, and the smaller code must pass the
same decode verification before it can be downloaded. Measured on the showcase link when this
page was built: at level H it is a version 4 symbol,
and re-encoded at level Q it comes out as version 3,
which the engine accepts into a frame; asked to draw hearts over that same model, it refuses
in its own words: Artistic styles require error correction level H — re-encode with ecLevel "H".
Framed codes
The one family that modifies nothing inside the code. The rendered symbol, quiet zone
included, is nested verbatim on an opaque panel drawn above the frame art, so decoration
cannot touch a module by construction: the frame is painted first and the panel is painted over
it. The 5 templates — heart, floral, ring, classic, confetti — share one geometry and differ only in the art, which
is recolourable vector work; a palette change never enters the panel. The showcase code above sits
in the heart frame, a composite 760×880 units with a
460-unit panel, so the panel takes 61% of the artwork's width and
the rest is frame. A caption beneath the panel is limited to 200 characters, and
the engine refuses a longer one outright rather than trimming it: asked to set a fictional
209-character caption under the showcase code, it answers Captions are limited to 200 characters.
A caption within the limit that wraps to more than 4 lines is truncated at the
last line with an ellipsis, so a clipped caption never reads as the whole message and never spills
into the artwork.
Heart-module codes
Dark modules outside the landmarks are redrawn as heart glyphs spanning 0.97
of their cell — 97% — large enough that a scanner's sampling grid still reads each
cell as decisively dark, small enough that neighbouring hearts read as separate shapes to the
eye. The showcase code is a version 4 symbol of 33×33 modules, 1089 cells in all, of which 549 are dark. The engine classifies
each dark cell before drawing it: the 116 inside the finder and alignment patterns (three
finder patterns, and 1 alignment pattern, centred on module (26, 26)) stay square, and the other 433, data cells and
the timing tracks, become hearts. Heart colour is a choice, inside a gate: foreground and
background must measure at least 4.5:1 by the WCAG formula, and the
engine rejects weaker pairs rather than warning: a scanner reduces the image to black and
white before it reads a single module, and weak contrast measurably degrades decoding. The
default heart colour measures
9.57:1 on white. A fictional pale pink, #f9a8d4,
measures
1.81:1, and the renderer refuses it in its own words: Artistic styles require at least 4.5:1 contrast (got 1.81:1) — decoration already spends decode margin.
Photo halftone codes
Every data cell is subdivided into a small grid. The centre 50% of the cell is
drawn in the pure module colour and is authoritative, because decoders sample near cell
centres; that figure is the measured floor; the methodology records that the next step down
failed. The outer ring shows the photo, quantised to three tones at luminance thresholds of 85 and 175 — dark below the first, light above the second — so the picture stays
visible without inventing false modules. Finder, alignment and timing cells and the quiet zone stay
fully pure, and no logo may be stacked on a halftone code — the photo already spends the decode
margin. Because dense symbols shrink the authoritative centres, the style is capped at version 4: about 34 characters of ordinary text, or 50 as
an inscription of uppercase letters, digits and simple punctuation. The photo code page's example encodes as version
3, inside the cap. A longer fictional link,
https://example.com/albums/first-garden/shared?guest=1, needs version 6, and the engine refuses
to weave a photo into it: This content needs a version-6 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.
What decode verification proves — and what it cannot
The rules above lower the odds of a failed scan; verification is what catches one. Once a render finishes, your browser rasterises the finished artwork at 3 sizes, 800, 1200, 1600 pixels, runs jsQR on the pixels of each, and compares the decoded bytes to your exact content. At least 2 of the 3 rasters must match byte for byte to earn the "Verified scannable" badge; anything less shows a clear warning and keeps the downloads locked, so no render leaves the page as a finished keepsake until it has verified. The 3 sizes exist because of a measured effect: a render can fail at exactly one raster size and decode at the others, depending on how the decoder's sampling grid lands on the modules at that scale. One fixed size would turn that resonance miss into a false failure; the 2-of-3 rule absorbs it, and the result reports which sizes passed. A render that decodes to different content from what you typed is reported ahead of one that does not decode at all, because wrong content is never safe to print.
The test suite runs the identical render-rasterise-decode loop on every style — at several raster sizes, rotated, and after lossy JPEG recompression — and the worked examples on every tool page, this one included, are rendered at build time by the same engine, so the documentation cannot silently disagree with the tool. What none of this can promise is the print: on-screen verification proves the artwork decodes cleanly at screen resolution and cannot see your printer, your paper, or the phone that will do the scanning. The decode-verification guide walks through the loop on real renders.
Message keepsakes: how the words travel
A QR code containing bare text carries no scheme, so a phone has nothing to act on. Apple's Camera and Google's ML Kit both 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, so the message is the one thing the reader never sees. Samsung phones and dedicated scanner apps do display it, but a keepsake cannot depend on which phone opens it.
That is why a message keepsake offers two honest modes. A reveal link encodes a
normal URL to the page at /m/ on this site and carries the message in the
URL's fragment — the part after the #. RFC 3986 separates the fragment from the
rest of the URI before any request is made, so it is never sent to our servers, a CDN or a
log; the reader's own device decodes the message. An offline note writes the words
into the code itself, needs no connection ever, and accepts the display caveat above. In both modes,
on a framed keepsake with no caption of your own, the message is printed under the code as the caption,
so it can be read by eye regardless; a reveal message is at most 300
characters before encoding.
The fragment format is published so the codes stay readable by anyone, with or without this
site. It is #, one flag character (A when the payload is the message's UTF-8
bytes, B when it is those bytes raw-deflate compressed), the payload in unpadded uppercase
base32 (RFC 4648), and one base32 checksum character over the payload. Encoded by the engine when
this page was built, exactly as the generator above encodes it — with no compressor passed — the
fictional note Second drawer down, under the maps. - K, 39 characters and
39 UTF-8 bytes, carries flag A: the bytes are base32-encoded as they are, so
every reveal link made on this site carries flag A, and the reveal page parses either
flag so that a future compressed link needs no new format. The payload is
KNSWG33OMQQGI4TBO5SXEIDEN53W4LBAOVXGIZLSEB2GQZJANVQXA4ZOEAWSASY, 63 characters, and the checksum character is
E, so the whole fragment reads
#AKNSWG33OMQQGI4TBO5SXEIDEN53W4LBAOVXGIZLSEB2GQZJANVQXA4ZOEAWSASYE. Base32 is used rather than base64 because some
scanners change the case of what they read and base32 survives that; its alphabet is also a
subset of the QR alphanumeric set, which can encode at 5.5 bits per character instead of 8,
and the fragment above sits entirely inside that set. The full link does not: this site's
encoder picks one mode for the whole string, and because the link also contains the lowercase
scheme and the #, the entire link, fragment included, is encoded in byte mode.
What makes the reveal link the larger symbol is simply what it carries — the URL plus a
63-character payload for a 39-byte note. As a reveal link the note
encodes as version 9, 61 modules across with its quiet zone, where
the same words as an offline note fit version 5, 45 across. Any message
keepsake can also be downloaded as a self-contained HTML file, which decodes this format with no
network and no website at all. The
message-keepsakes guide works the format
through in full.
Printing and testing a keepsake
The methodology's print guidance is short, and this page does not extend it: 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. What the engine adds is a floor computed from the module count of the code you actually made. Its working minimum module is 0.5 mm, so the showcase code — 41 modules across, quiet zone included — must print at least 20.5 mm (0.81 in) wide, and the reveal-link form of the fictional note above, 61 across, must print at least 30.5 mm (1.20 in) wide. A photo halftone code paints only the centre 50% of each cell, so the engine raises the floor by that ratio to keep the scanner-visible module at the same size: the photo page's example, 37 modules across, must be printed 37.0 mm wide rather than the 18.5 mm a plain code of that size would need — 2 times as large. Past 65 modules across, the engine flags a code as oversized for a tag or a card.
Export sizes are in pixels, and a print size follows from them at 300 DPI. The engine's readouts for its square PNG presets are these: 512 px prints at 43.3 mm; 1024 px prints at 86.7 mm; 2048 px prints at 173.4 mm; of those, the generator above offers 1024 px and 2048 px in its PNG download picker. The SVG download is vector and carries the same verified artwork at any size. For a framed code the floor applies to the code inside the panel, quiet zone included, not to the panel edge or the sheet: the frame adds to the print size without counting toward it, and the panel pads the code on every side — in the showcase composite the code spans 94% of the panel's side — so the showcase panel must print at least 21.8 mm across before the code inside it reaches its own 20.5 mm floor. The white margin around the code is not empty space to crop; it is the quiet zone scanners need, so keep it uncropped. Then make one test print at the final size and scan it with a real phone — the only test that includes the paper. The printing guide works the floors through for every style.
Start here: the guides
The 10 tool pages above each apply the engine's rules to one preset, with the numbers computed from it. The guides explain the rules themselves, one mechanism each, with every figure computed at build time by the same engine; all of them are listed at /guides/, and the methodology page holds every measured rule in one place.
- Why decorated QR codes still scan — the anatomy rule — which modules may be restyled and which never are — the error-correction rule (level H for every style that redraws modules; a frame alone may also carry Q), and the 4-module quiet zone always drawn.
- Heart-module codes, explained — the 0.97 cell coverage, the 4.5:1 contrast gate measured on accepted and rejected pairs, and what a heart code cannot carry.
- Photo halftone codes, explained — the 50% authoritative centre, the 85/175 tone thresholds, the version-4 cap and the two character budgets.
- Framed codes, explained — the clean panel, the 5 templates, and why a recolour can never reach the code.
- Decode verification: how the badge is earned — jsQR at 800, 1200, 1600 px, the 2-of-3 rule, the resonance false negative it absorbs, and what the badge cannot see.
- Message keepsakes and reveal links — what a phone does with bare text, the fragment format worked through on a real encoding, the offline note, and the self-contained HTML file.
- Printing and scanning a keepsake — the 0.5 mm module floor, the 65-module comfort limit, the PNG presets at 300 DPI, and the test print.
- Treasure-hunt chains — a chain of 2 to 20 clues numbered by the engine — a fictional 3-clue list comes out captioned "Clue 1 of 3", "Clue 2 of 3", "The prize · 3 of 3".
Common misreadings the guides correct, in one line each: cropping the quiet zone, which is part of the code and not a margin around it; expecting bare text in a code to display on every phone, which is the reason the message modes exist; choosing low-contrast heart colours, which the engine refuses rather than warns about; and reading the badge as a print guarantee, when the test print is the half of the check only you can run.
Keepsake code FAQ
What does the "Verified scannable" badge prove?
That this exact artwork — frame, hearts, photo and all — was rasterised at 800, 1200, 1600 pixels in your browser, decoded with a real QR decoder (jsQR) at 2 or more of those 3 sizes, and matched your content byte for byte. Anything less shows a warning and keeps the downloads locked. It cannot see your printer, your paper, or the phone that will do the scanning; that is what the test print is for.
Which error-correction level do the styles use?
Level H, roughly 30% recovery, for every style that redraws modules. Hearts reshape every dark module outside the landmarks and the halftone weaves a photo through the data cells, so decoration spends decode margin, and the engine refuses to draw hearts or a photo over a model encoded at a lower level. A framed code places an unmodified symbol on the panel, so a frame alone may also carry level Q — the generator's opt-in "Smaller code (less error correction)" control, available only with a frame and no heart modules — and the smaller code must pass the same decode verification before download. Measured on the showcase link: version 4 at level H, version 3 at level Q. The showcase code on this page is encoded at level H, as every rendered example on the site is — checked across the preset registry when this page was built.
Can I pick any colour for heart-shaped modules?
Any pair that measures at least 4.5:1 in WCAG contrast, dark on light. The engine rejects weaker pairs rather than warning: a scanner reduces the image to black and white before it reads a single module, and weak contrast measurably degrades decoding. Measured when this page was built: the engine's default heart colour is 9.57:1 on white and is accepted; a fictional pale pink, #f9a8d4, is 1.81:1 and is refused. Inverted codes — light hearts on a dark background — are refused whatever their ratio.
Why does a hidden message need a "reveal link"?
Because a QR 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; an iPhone offers to copy or search bare text and Google Lens runs a web search, while Samsung phones and dedicated scanner apps do display it. A reveal link encodes a normal URL to the page at /m/ on this site with the message in the URL fragment, which per RFC 3986 is stripped before a request is made and never reaches a server, a CDN or a log. An offline note writes the words into the code and accepts the display caveat. In both modes, on a framed keepsake with no caption of your own, the message is printed under the code as the caption, and a reveal message is at most 300 characters.
How large must a printed keepsake be?
At least 2 × 2 cm for arm's-length scanning, or the engine's floor for your code if that is larger: 0.5 mm per module, quiet zone included. The showcase code is 41 modules across and must print at least 20.5 mm (0.81 in) wide; a photo halftone code paints only the centre 50% of each cell, so its floor is 2 times that of a plain code with the same module count. Keep the 4-module quiet zone uncropped and scan a test print with a real phone before the moment that matters.
How much content fits a photo halftone code?
The style is capped at version 4, because denser symbols shrink the authoritative cell centres below what a decoder can read. That is about 34 characters of ordinary text, fewer with emoji or accents, or 50 characters as an inscription restricted to uppercase letters, digits and simple punctuation. A longer payload is refused outright, in the engine's own words, rather than rendered and left to fail.
Does anything I type or upload leave my device?
No. Encoding, rendering and decode verification all run in your browser; content and photos are never uploaded, and there is no account. Nothing is stored unless you choose "Save to this device", which keeps the keepsake in your browser's own library. The content is encoded directly into the image — no shortener, no redirect — so no subscription keeps a finished code alive. The one dependency to note is a reveal link, which points at the page at /m/ on this site; an offline note writes the words into the code itself, every message keepsake can be downloaded as a self-contained HTML file that decodes the format with no website at all, and in both modes, on a framed keepsake with no caption of your own, the message is printed under the code as the caption.
The right tool for the code
This free site covers the occasions above, end to end. Two siblings cover the rest: more power in the Keepsake Studio — batch sheets, extended template packs, and cross-device libraries — and plain codes at GenerateQRCodes.net when the job calls for a classic black-and-white utility code (WiFi, vCard, SMS, and friends).