Sep 2024

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.

Me Writing this Blog Post
me escaping from a train full of salarymen with qr code facesOriginal photo by Arnaud DG, CC BY 2.0
The Observable Universe
rush hour in train station where salarymen have qr code facesOriginal photo by erikjohansson, CC BY 2.0

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.

mcdonalds appuber adduolingo profileinstagram profile

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.

blocky designCreated using QRBTF
bubble designCreated using QRBTF

To this day, I still haven't seen anyone else make QR codes like this. I wanted more, so I made my own generator.

stained glassphysicswhiteboard markerneon signlayered patternsbathroom tilebenday dotsabstract art

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).

Miscellaneous

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?

bad qr code stencilAttempt 1 (terrible)
slightly better qr code stencilAttempt 2 (still unscannable)

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.

The upside is that you can probably scan this barcode too.

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

mike wazowski amzing qr codeCreated using Amazing-QR

In the simplest case, you can just place dots over the center of where each pixel would be. A dot at least 1/3 the width and height of a pixel is usually enough for it to decode properly.

mike wazowski visualead qr codeCreated using Visualead

In addition to a center dot, you can partially lighten or darken the entirety of each pixel.

By downscaling the image or using pixel art, the center dot can be hidden in plain sight. Dithering and limiting the color palette (quantization) both increase noise which also provides more camouflage.

The most unique technique I've seen involves generating word clouds. In this case, the overlay dots are used in an intermediate step to drive a text deforming algorithm.

The jump to animation is simple, since the same dot overlay can be drawn on each frame. This example changes over time to display the lyrics though.

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.

mike wazowski qart codeCreated using QArt Coder

URLs can have anchors like test.com#anchor. Usually, this scrolls to a specific part of a page, but an invalid anchor is just ignored. The math used to manipulate pixels by adding an anchor is covered in QArt Codes.

mike wazowski my qr art codeCreated using MY-QR.ART

This site lets you draw the desired output data, which can be directly interpreted as part of the input text. The first half of the text is the url to a link redirect service and the random characters that follow act as a unique key. For details, see Hacking QR code design.

mike wazowski qr pictureCreated using QRpicture

Here, the data pixels match an image, but they are not part of the decoded message. In other words, they are changing the padding bytes. See their HN post.
(EDIT: This padding manipulation method was first described in Expansion of Image Displayable Area in Design QR Code and Its Applications, 2011.)

In terms of animation, reverse-engineering the data for every "frame" is a bit more tedious, and the QR code is unlikely to be scannable. See their behind the scenes video to see how they approached it.

(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

douglas coupland qr codeDouglas Coupland QR Boogie-Woogie No. 5
coupland forgeryCreated using qrframe

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.

qrarts qr codeQrArts
qrarts forgeryCreated using qrframe

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

elephant mosaicMosaic inside the Vatican museum
mosaic qr codeCreated using qrframe

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.

waves on a rocky beachThe Mediterranean Sea
animated waves qr codeCreated using fuqr

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.

  1. 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.
  2. 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
  3. Proprietary scannable codes are kinda funny. Tiktok and Snapchat have already replaced their custom codes with stylized QR codes. four proprietary scan codes
  4. Here are links I don't want to lose. qr code on book cover