QR codes have invaded our world.
Their prolific penetration is no surprise. They store information efficiently, scan fast and reliably, and now that QR code scanning capabilities are built-in on phone cameras, there is nothing that even comes close to their convenience.
They are simply irreplaceable to anyone looking to share a URL (read: everyone).
Suffice it to say, I've seen a few QR codes in my day to day.
With how widespread they are in consumer facing spaces like advertising, it's also no suprise that companies would want to brand their QR codes. The most common practice is placing a logo in the center, which relies on error correction to recover the missing data.
Beyond simply placing a logo on top, there have been many attempts to create "artistic" QR codes, which use the entire code as a canvas. Some techniques use the pixel grid, some work around it, while more recent developments involve using generative AI to create scannable anime women.
While limited, these techniques are not specific to a single QR code and generalize to any image and URL. This is in stark contrast to the labor intensive but fully bespoke QR codes made by human designers.
These were suprisingly hard to find. I started from this Flickr group featuring QR code art from the early 2010's, but Pinterest proved an invaluable resource that surfaced obscure work from Behance, Dribbble, and random blogs.Where does this leave us if we want a unique QR code, but we don't want to design each one from scratch for every URL?
I'm not exactly a designer, but this blog post from the creator of QRBTF convinced me that QR code generators can create beautiful QR codes. So I got inspired by stained glass, Persian rugs, comic books, and retro logos, and locked in for minutes at a time between months of procrastination.
The end result is qrframe.kylezhe.ng, a web app that lets you define parameters and write code to render QR codes however you want. Here's the source code for the perverts.
It comes with many example presets, based on existing styles and my original designs, which I hope others can use and build upon to create their own custom QR codes.
Feel free to play with it, but in order to craft QR codes with intention, we need to learn how QR codes actually work.
How do QR codes work?
Around few years ago, I had the idea to draw QR codes using chalk and a stencil.
This was a failure for many reasons (it takes forever to draw and smears instantly), but the biggest issue was that the end result couldn't be scanned.
I failed because I didn't understand two important things.
-
Smartphone camera apps are not dedicated QR code scanners. They don't even attempt to scan until after a QR code is detected by a computer vision model. I suspect they favor solid colors and high contrast, and my chalk didn't contrast enough against or completely cover the gray concrete. This is (probably) also why some "artistic" QR codes scan better from farther away; the pixel colors are more solid on a smaller image.
-
The finder patterns have to be somewhat precise for decoding to succeed
There are finder patterns in three corners. These allow scanners to detect the code. Specifically the ratio of 1:1:3:1:1 black and white pixels through the vertical and horizontal center. A white separator or "quiet zone" around each finder pattern is generally necessary for scanners to detect it.
There is usually one alignment pattern in last corner, but the smallest code has none, and very large codes have multiple. These help scanners account for distortion and perspective.
There are two timing patterns consisting of alternating black and white pixels. These help scanners align rows and columns.
The timing patterns and alignment patterns are technically optional, in the sense that a QR code without them will still be scannable, albeit less reliably.
There is format information, storing the error correction level and the mask pattern applied to the data. One copy is in the top left, and the other copy is split between the top right and bottom left.
Very large QR codes will have version information. Version means size, and it ranges from 1 - 40. Most QR codes fit in Versions 1 through 6, where size can be accurately calculated using the distance between the finder patterns. One copy is in the top left, and the other copy is in the bottom left.
The remaining space is for the data. It starts with a header describing the encoding mode and the data length.
There are efficient encoding modes for numbers, alphanumeric strings, and Japanese characters, but the only relevant mode for URLs is Byte mode. This just means UTF-8.
The header is followed by the actual data, padded to fill the capacity, and then error correction codewords fill the remaining space.
QR codes can be rotated, mirrored, and the "dark" and "light" pixels can be inverted.
These are not explicitly supported, but it's pretty hard to make a working QR code scanner that doesn't also scan these artistically disfigured QR codes.
Prior work
One way to add an image is to manipulate the input data.
For URL's specifically, arbitrary data can be added as an ignored URL anchor. This data can be calculated to precisely manipulate pixels in the QR code in limited areas. This approach doesn't rely on error correction at all, but it is limited in the images it can create (Russ Cox, QArt Codes).
A more common approach is to change how each QR code pixel is represented.
One strategy, possibly popularized by the Halftone QR Codes paper, involves replacing each QR code pixel with a 3x3 grid of pixels that best match the image while still decoding as the correct color.
Generally, as long as the center of each QR code pixel is correct, it can be decoded correctly. In other words, this approach is basically equivalent to overlaying the correct center pixels on top of a image.
The low resolution black and white dithered effect is just a smart stylistic choice that hides the data as noise. There are many implementations online 1 as well as a version of Bad Apple.
Moreover, the image doesn't have to be black and white or low resolution at all.
A more subtle effect can be achieved by only adjusting the image color vCode Uses color thresholds to reduce number and intensity of overlayed pixels.
QRpicture Uses color quantization and a high-contrast color palette to maximize scannability of color images. Really good at hiding the QR code's noisy "texture".
Micrography QR Codes The weirdest of the bunch, but super cool.
Perhaps unsuprisingly, they also created the first AI QR codes, which they documented in this blog post.
Following that, Anthony Fu (@antfu) made a series of blog posts detailing his experiments (first, second), culiminating in a definite guide antfu.me/posts/ai-qrcode-101 co-authored by 赛博迪克朗, wangcai, and 代々木.
Try moving your phone away from the screen (farther than you think), and maybe increase your brightness.
Here's the original blog post in Chinese as well as a more complete guide if you're interested in that.
Troy Ni (@ciaochaos) created QRBTF, a generator used to create truly beautiful QR codes like the ones below. The original inspiration is detailed in this blog post.
Tangents
Tangent -> Creating QR code that matches a string marienraat.nl/hacking-qr-codes.html There's a 100 page PDF describing the QR code standard (ISO/IEC 18004) in detail, but here's the truth.
A QR code is whatever a QR code scanner can scan.
Here are some undocumented behaviors I discovered while making a simple QR code generator.
- UTF-8 is implicitly supported when using the byte encoding mode.
- Kanji, Structured append, EC1, and FNC1 encoding modes aren't relevant for normal use. Scanners might not even support them (I can't find generators that support them either).
Oversimplifiying to the point of misinformation, there are 2* parts of a QR code decoder we need to swindle.
- A QR code detector (based on computer vision) gets a bounding box
- A binarizer algorithm converts an image to a matrix of on/off bits
We can do anything to a QR code as long as those two parts still function mostly correctly.
Google's scanner is part of their MLKit. Apple's is part of their AVFoundation.
Neither implementation has available source code, not that it would do us much good.
Whether it's an app with a shareable code, an advertisement meant to catch your eye, or someone with too much time, the possibilities of beautiful QR codes awaits.
The QR code standard (ISO/IEC 18004) suggests how a QR code can be created and decoded. It is up to the QR code scanner to convert an image into this decodable 2d matrix of black and white pixels. This makes everything possible.
Thankfully, the Wechat scanner is seemingly open source. It's available as an OpenCV extension. It's a fork of the cpp port of the popular zxing library.
I found this book in a random library I stopped in. Too much of the QR code was missing for it to scan, but I thought it might contain an Easter egg. I used QRazyBox to recover what data remained which was only about half the URL. This was the first strange finding. The code was almost entirely padding! The error correction was set to low too! Why wasn't the error correction set higher if the space was all wasted?
Using an ASCII chart to decode some partially obscured bytes, I figured out it pointed to an iStockphoto page, but unfortunately iStockphoto had changed their URL structure. I had what looked like half a username, but there was no way to search for users. With no other option, I brute-forced all 80,000 possible QR codes and compared the error correction pixels to find a match. I was shocked to find nothing. In a desperate last try, I checked for near matches and finally found the matching username which led me to the original stock image from 2011.
It turns out the QR code incorrectly has a last byte filled with 1s instead of 0s.
Why was this used for the book cover? The book came out in 2021. Did the designer just have it on hand? Even though they could just get a QR code from anywhere? I guess it didn't matter, because who would notice? Kind of an anticlimax, but my takeaway is that damaged QR codes are probably all decodable with effort.
Some companies have even gone so far as to develop proprietary scannable codes to maintain their brand identity, to varying success2.
Notice how they're all mostly gimmicks since they can only be scanned with a specific app (or device).
Footnotes
-
Lachlan Arthur's original JSFiddle implementaion
-
Tiktok and Snapchat have already replaced their custom codes with stylized QR codes. ↩