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 is built-in on most 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 works because QR codes have error correction built-in.
Beyond simply placing a logo on top, there have also been many attempts to create "artistic" QR codes. Making a picture is the obvious thing to do, and there are various ways of combining the QR code pixels with an image for a unique look.
While these might require some upfront technical work, it's trivial to change the image to create any number of new QR codes. This is in stark contrast to the labor intensive process of having artists design fully bespoke QR codes from scratch.
There are more amazing examples than I could possibly fit on one page. These artists' have many other works, and Pinterest is full of examples that are otherwise impossible to find.Obviously, human-made QR code's aren't practical for anyone without lots of time or money since each new URL would require a redesign.
The natural progression is beating the dead elephant in the room called generative AI. These AI QR codes promise beautiful and customizable designs at scale, and when using carefully engineered prompts the results appear good. But I've no prompting expertise, so I've generated naught but nightmares.
I shall not beleaguer thy weary eyes,
nor further burden the web, world wide,
with pages from this Babelian picture tome.
This is not a place of honor,
there are thirteen fingers on her,
anime girl big boobs ancient rome.
I'm more interested in programmatically generated QR codes anyways.
Before they invented AI QR codes, the guys behind QRBTF also created these beautiful style options for their generator.
To this day, I still haven't seen anyone else make QR codes like this. I wanted more, so I made my own generator.
It's available at qrframe.kylezhe.ng, but it's more of a tool for crafting QR codes. It comes with presets that are simple to modify or create from scratch, because it's just Javascript that generates an SVG or renders to an HTML canvas.
Here's the source code for the perverts.
Anyways, if you have the time, I'd like to share what I learned about QR codes along the way.
The Anatomy of a QR Code
Finder patterns are the big squares shapes in three corners. These are used to determine the code's orientation, dimensions, perspective and more. The ratio of black and white pixels through the center is key (roughly 1:1:3:1:1), so the corners aren't essential.
To aid detection, finder patterns need a separator or "quiet zone" around them, but this doesn't have to be a specific size.
Alignment patterns are smaller squares used to account for distortion. There's usually one in the last corner, but the smallest QR code doesn't have any, while very large codes have multiple.
Timing patterns are the horizontal and vertical belts of alternating black and white pixels. These supposedly help with aligning rows and columns while decoding.
Unlike the finder patterns, the timing patterns and alignment patterns are not strictly necessary in practice. A QR code will mostly* scan fine without them.
*The bottom right alignment pattern does help, especially for larger sizes, while the timing pattern seems to be completely useless.
Format information stores the error correction level and the mask pattern applied to the data (explained below). 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 a section for version information. Version means size, and it ranges from 1–40. One copy is in the top right, and the other copy is in the bottom left.
For smaller codes, size can be accurately calculated just using the distance between the finder patterns.
The remaining space is for the data.
Perhaps surprisingly, it starts at the bottom right and zigzags up and down, from the right to the left.
The bits are organized in blocks of 8 (aka bytes), so the last few pixels will be unused if the number of data pixels isn't divisible by 8.
There are 4 levels of error correction which allow roughly 7%, 15%, 25%, or 30% of all the bytes to be recovered. See how increasing the error correction also reduces the data capacity.
If you further break down the data, you'll see it starts with a header and is padded to fill the unused capacity. You can play around with the error correction level, version (size), and input text to see how the space is used.
THIS MAY LOOK VERY WEIRD. After the certain size, the data bytes are interleaved so they are spread throughout the space rather than placed continguously. Also, the header isn't a multiple of 8 bits, so the data isn't byte aligned.The data header describes the encoding mode and the character count. There are efficient encoding modes for numbers and alphanumeric text, but the only relevant mode for URLs is Byte mode*, which just means UTF-8.
*Alphanumeric mode can encode URLs, but it only supports uppercase characters, so no one uses it.Finally, the mask is one of 8 patterns XOR-ed with the data to break up undesirable pixel arrangements (like the finder pattern shape).
QR codes can be rotated, mirrored, and the "dark" and "light" pixels can be inverted.
Here's all you need to remember to make an artistic QR code: the finder patterns must be the right shape, and the rest of the QR code just needs to be dark or light enough in the right place.
That's 80% of it, but unfortunately there's one major gotcha we've gotta cover.
Convenience at a Cost
Can you tell what I did wrong with these QR codes I drew using chalk?
The first one is janky, but I was legitimately confused why my second attempt failed.
Computer vision is commonly used in code scanners nowadays, but the tradeoff of non-dedicated scanners (like the default smartphone camera app) is that they are less sensitive and much pickier about what they recognize.
I'm no expert in this domain, and computer vision is somewhat of a black box anyway, so the only way I know to tell if a QR code will scan is to try scanning it.
And when a code doesn't scan immediately, I've found moving farther away tends to help.
In my experience, solid colors and high contrast are good indicators (and what my chalk drawing lacked), but once a QR code strays from black and white pixels, its fate is partially in the hands of God.
With that in mind, let's cover existing ways people have successfully created artistic codes programmatically.
Image Techniques
There is a wide breadth and depth of research into creating images with QR codes, so I'll only briefly cover these two categories I came up with.
Dot overlay
Reverse-engineering
These techniques start with the desired output and modify the input data to match. These are basically incompatible with a normal QR code generator and require deep custom logic.
(EDIT: I ended up making my own reverse-engineered Bad Apple. Here's the scannable and unscannable version.)
Non-Image Techniques
Since there aren't too many examples of these, I'll be showing my own work and giving my thoughts.
While each unique design will have unique challenges, hopefully these examples can give you some some idea of steps you can take.
Forgery
Theft is the best type of complement... or something like that. Often times human-made designs are possible to implement using code. Here, Douglas Coupland has replaced 2x2 squares of black and white with a corresponding dark or light colored square. Not only is this easy to code, it demonstrates a common tactic of replacing groups of pixels with a larger design element. See Vancouver Codes (2012) for more examples of his work.
However, not all that glitters is a scannable QR code. The original design is slick, but the gap between dots is somewhat problematic. Scanning from a distance works, but for my recreation I opted to fill the gaps with a gray shadow. This is admittedly much less cool, but many such compromises are inevitable.
In the best case scenario, you can copy the code directly. This style option, along with many others, is found in the react-qrbtf
repository under the MIT license. Building on top of existing code is a great way to make a new design, so feel free to use my code in the qrframe
repository as well. The psychological damage you'll suffer is cost enough.
Touching grass
While it had nothing to do with this mosaic in particular, exposure to all types of art is probably the best source of creative energy. The design is quite simple, but I love its character. If there's a lesson to be learned here, it's that rules are made to be respectfully broken. You CAN mess with the finder patterns, but notice how the cross shape is completely preserved in two of them.
Animation is generally a bad idea since you want every frame to be scannable. But the call of the sea is unrelenting. In this example, each pixel only changes in scale, and the center 1/3 of each pixel is always the correct color like in the dot overlay technique. And that's the final lesson, that none of these techniques are mutually exclusive.
Congratulations, you now know too much about QR codes!
Thanks for reading!
I hope you learned something interesting, even if it's not useful.
If you'd like to design a QR code, and you're not afraid of writing a little code, give qrframe.kylezhe.ng a try.
Once again, my code is available at qrframe
.
If you're on the fence then please know, I knew much less than you do now when I started working on fuqr
, my QR code generator in Rust that started it all (i use windows btw).
You can do it if you try, but also your time is precious.
See ya! 🫡
Random Tangents
Here are some QR code thoughts I had to get off my mind.
- There are only 3 encoding modes.
- The QR code standard lists encoding modes that are not relevant to ordinary users which is fine, but every QR code explainer mentions Kanji mode, a legacy mode using a legacy character encoding for Japanese, when it just isn't supported by generators or scanners in normal use. I'm sure some Japanese company uses them, but that doesn't make it relevant. Ditto for Structured append, ECI, and FNC1 encoding modes.
- The mask selection step is deeply unserious.
- The best mask is chosen by scoring the masked data, but the scoring conditions are arbitrary, and all 8 masks will work just fine anyway.
- This interative step by step walkthrough showcases this in Steps 7 and 8. Choose the worst mask and see that it doesn't matter.
- www.nayuki.io/page/creating-a-qr-code-step-by-step
- Proprietary scannable codes are kinda funny. Tiktok and Snapchat have already replaced their custom codes with stylized QR codes.
- Here are links I don't want to lose.
- www.thonky.com/qr-code-tutorial is a great resource and helped me understand the error correction math.
- segno is probably the only QR code generator that supports Kanji mode.
- I've found two 3d galleries of QR code art. If I had a nickel...
- QRazyBox is a super cool QR code recovery tool that I used to decode this book cover and find the original stock image from 2011.