Skip to content
VixQR

By the VixQR team · published on · 9 min read

AI and Artistic QR Codes: What Still Scans, and What's Just a Picture

AI-generated QR art looks incredible and fails unpredictably. How decoding really works, why diffusion models are fragile, and what we learned building one.

An artistic QR code where a photograph is rendered through the code's module grid while remaining scannable

Search for "AI QR code" and you'll find galleries of genuinely beautiful images — a code hidden inside a Japanese village scene, a portrait, a coastline — that also, supposedly, scan.

Some of them do. Many of them don't, or scan on one phone and not another, or worked for the person who generated them and fail for everyone else. And almost nothing written about them explains why, because the people writing are selling the generators.

We built an artistic mode into VixQR. Building it meant working out exactly which parts of a QR code you're allowed to destroy and which parts you absolutely aren't. Here's what that process taught us, and how to judge whether a beautiful code is actually going to work.

What a scanner needs, precisely

A QR code is a grid of modules — the small squares. The grid size depends on the "version": version 1 is 21×21, and it climbs to version 40 at 177×177.

A decoder does three things in order, and each one has a different tolerance for interference.

First, it finds the code. Three large concentric squares in the corners — the finder patterns — plus the timing lines, a single row and column of alternating modules that let the decoder work out the grid spacing and correct for perspective. Newer versions add smaller alignment patterns.

This stage is all or nothing. Obscure a finder pattern and the decoder doesn't read a damaged code; it doesn't see a code at all. No amount of error correction helps, because error correction operates on data the decoder has already located.

Second, it samples each module. For every cell in the grid, is this light or dark? The decoder samples around the centre of each module. This is where the tolerance lives — and where artistic codes either work or don't.

Third, it corrects errors. Reed-Solomon error correction reconstructs a proportion of corrupted data: 7% at level L, 15% at M, 25% at Q, 30% at H.

That budget is the entire artistic allowance. And people consistently misunderstand it in one specific way.

The error correction budget is not a design allowance

Level H recovers 30% of the code's data. So you can obscure 30% of the code, right?

Not quite, and the gap matters.

That 30% is a recovery ceiling under ideal conditions — a clean image, even lighting, a camera square to the surface, a good sensor. Real scanning spends part of that budget before your design touches it. Print dot gain eats some. Glare eats some. A five-year-old phone camera in a dim room eats a lot. Scanning at an angle eats more.

If your design consumes 28% of a 30% budget, you've left nothing for reality. The code will scan on your desk, on your phone, under your lamp — and fail in the wild, intermittently, which is the worst possible failure mode because your testing passes.

Treat the recovery level as headroom you're sharing with the physical world, not a quota you get to spend. Our working rule when we built the artistic mode: never let the design consume more than about half the theoretical budget.

How AI QR art actually works

The diffusion-model approach — the one behind most of those gallery images — works roughly like this: a QR code image is fed to an image generator as a structural conditioning input, typically via ControlNet. The model generates an image that follows the code's light-and-dark structure closely enough, in theory, to still decode, while looking like whatever you prompted.

It's a genuinely clever idea. It also has a structural problem: the model has no idea what a QR code is.

It sees a pattern of light and dark regions and tries to produce an image whose luminance roughly tracks that pattern. It doesn't know that the three corner squares are load-bearing in a way the rest isn't. It doesn't know that row six is a timing line. It doesn't know that a single module flipped in the format-information area can invalidate everything.

So it treats a finder pattern and a decorative background module as equally negotiable. Usually it gets lucky, because the finder patterns are large, high-contrast features that a conditioning model tends to preserve anyway. Sometimes it doesn't.

That's why the output is probabilistic. You generate twenty, some scan, you pick those. The workflow is inherently generate-and-test, and the honest vendors say so.

What improved in 2026 is real: better handling of error correction, smarter preservation of structural regions during generation. Vendors now claim scan reliability above 95%, and some claim branded codes see 25–40% higher scan rates than plain ones. Treat both figures with care — they come from companies selling the tools, the reliability number depends entirely on test conditions, and the engagement number is comparing a designed asset against an undesigned one, which measures design effort as much as it measures the code.

What hasn't changed is the structural issue. A model that doesn't understand QR grammar can't guarantee QR validity. It can only get very good at usually being right.

The alternative: guarantee the invariant

When we built VixQR's artistic mode, we started from the opposite direction. Instead of generating an image and hoping it decodes, we made a rule that cannot be broken and then gave the image everything else.

The approach: take the raw module grid, and split each module into a 3×3 sub-grid. The centre sub-module always carries the code's true value — dark if the module is dark, light if it's light. Always. No exceptions, no probability.

That centre is exactly where a decoder samples. The surrounding eight sub-modules are free to carry the image.

We verified the invariant with a script rather than by eye: across full test grids, 841 of 841 and 4,225 of 4,225 module centres came back correct. Not "usually". All of them.

On top of that:

  • Finder patterns stay solid. The 8×8 regions in the three corners are rendered intact, untouched by the image.
  • Timing lines stay solid. Row and column six render as specified.
  • Error correction is forced to H whenever an image is used — the user doesn't get to choose a level that won't survive.

Then three levels control how much of each module the image gets:

Level Structure kept Free for the image
Safe Centre + cross ~50%
Balanced Centre + two diagonals ~56%
Artistic Centre only ~62%

Even at the most aggressive setting, the decoder's sampling points are untouched. The image gets the space between them.

The trade-off is honest: this produces a halftone rendering — recognisable, striking, clearly an image expressed through the code's grid. It does not produce a photorealistic scene with a code hidden inside it. Those are different techniques with different guarantees.

The colour problem, and the number that matters

Black-and-white halftone is straightforward. Colour is where it gets interesting, and where most artistic codes quietly fail.

The naive approach — tint the dark modules with the image's colours — destroys contrast, because the image's mid-tones land somewhere between "dark enough to read as a dark module" and "light enough to read as a light one". The decoder has to threshold, and ambiguous values threshold unpredictably.

What we do instead: keep the image's hue, but push its luminance into one of two bands — dark modules into luminance ≤ 72, light modules into ≥ 198. The colour survives; the ambiguity doesn't.

Measured contrast between those bands: 5.35:1, against roughly 3:1 required. The margin is deliberate. That's the room a bad camera and bad lighting get to consume.

Within that, the pull toward the band is graded. Sub-modules carrying the code are pushed all the way. Free sub-modules are pushed partially — more at Safe, less at Artistic — so the image keeps its texture where it doesn't matter and the code stays crisp where it does.

Two things worth knowing if you generate codes at all

Both came out of building this, and both apply well beyond artistic modes.

Preview and export must be the same computation. Our first instinct was a fast preview path and a high-quality export path. That's a bug factory: the user approves one image and receives another. We ended up with a single function computing the cell grid, used identically by the on-screen preview, the PNG export and the SVG export. If the preview scans, the export scans, because they're the same thing.

The same problem shows up in the two-tone speckle mode. The library we use paints every dot through a single clipping path, so producing a two-colour effect means splitting that path and painting the extracted dots separately. Which dots get extracted is a random draw — so we seed the generator deterministically from the payload. Same code, same speckle, every time. A preview you can't reproduce is not a preview.

Silent failure is worse than loud failure. The first version of the artistic mode rejected certain uploads without saying anything. The exception was caught and swallowed; the user saw a picker that did nothing. It took someone telling us "it doesn't show up" to find it.

Every failure path needs a visible message. This is obvious written down and remarkably easy to get wrong.

How to judge an artistic code before you print it

Whatever tool produced it:

Scan it on at least three phones, including one that's several years old and one Android that isn't flagship. Budget sensors fail first.

Scan it in bad light. Dim indoor lighting, and direct sun if it's going outside.

Scan it at an angle, 30–40 degrees off square. People don't stand perpendicular to posters.

Scan it from the real distance, at the real printed size. Not zoomed in on a monitor.

Print it and scan the print. Screen scanning is easy mode — a backlit display has contrast no paper can match. Ink on matte stock behaves completely differently.

Check that the corners are intact. Look at the three finder squares. If the image has softened, tinted or partially covered them, the code is fragile no matter what your test scan says.

If it fails any of these, don't ship it. An artistic code that works 70% of the time is worse than a plain black one that works 99% of the time, because the failures are invisible to you and land entirely on your audience.

What nobody can automate

There's a category of QR art you'll see on freelance marketplaces — a code integrated into a full illustration, where the modules become buildings in a skyline or leaves on a tree, and the whole thing reads as a poster rather than a code.

Those are made by hand. A designer places every element, tests, adjusts, tests again. It takes hours and it's a genuine craft.

No generator does that, ours included, and any tool promising it is describing something else. Automated artistic modes — including the halftone approach above — produce a recognisable style: the image expressed through the grid. That's a real and useful thing. It isn't bespoke illustration, and it's worth being clear about the difference before you commission or expect one.

The honest summary

AI-generated QR art is real, often beautiful, and structurally probabilistic. If you use it, generate several, test hard on real devices and real prints, and accept that you're selecting for codes that happen to work rather than codes that are guaranteed to.

Deterministic artistic modes trade some visual freedom for a guarantee. You get a halftone style rather than a hidden-code scene, but the sampling points are correct by construction rather than by luck.

And for a lot of uses — a table tent, a shop window, a business card — a plain high-contrast code that works every time on every phone remains the right answer. Decoration is a cost. Sometimes it's worth paying. Just know what you're spending.

VixQR includes an artistic halftone mode alongside standard generation, free and entirely in your browser, with PNG, SVG and PDF export. Whatever you make with it, print one and scan it before you print a thousand.

All articles