Cranking up the error correction and putting a picture in the middle of a QR code is good and all, but what if we instead encode our drawing on the pixel data itself?

The tools used to make this post avaliable on the source code repository.

Bits to Data

Using the Wikipedia page on QR code as a starting point, it shows that payload starts at the bottom right corner, then it zig-zags up and down until at some point the payload ends, and the error correction begins, and keeps going on until all the area of the QR code is filled. Disregarding the fixed areas of a QR code, like the three big squares on the corners, the smaller search patterns and the format info, half of the area on a QR code is in theory under our control.

If we could add a prefix to a QR code, for instance an URL “shortener” service prefix like https://heavydeck.net/s/ and then append arbitrary data to it as to make our drawing, we could have a fairly large area of the QR code for use as we please for our artistic purposes.

Encoding

QR codes allow for multiple ways of encoding data which are more efficient for different types of data, and to our advantage, different modes can be mixed for optimal data encoding efficiency.

These modes are:

  • Numeric only
  • Alphanumeric
  • Byte
  • Kanji

Of these, alphanumeric is interesting since it covers just enough characters to build a url prefix (albeit only uppercase) in a compact way, and then the numeric only mode which the whole charset is valid on an URL, it allows control of all of its bits with not a single “quirk” happening on the encoded URL, just a very long number.

Note on numeric encoding: In this mode the payload encodes numbers 000 to 999 using 10 bits of data, which means that some encodings are invalid, namely, anything encoding a value above 999 is invalid, however since we don’t care much about the number itself, simply clearing bit 10 when needed (effectively subtracting 512) will get us back to the acceptable range below 999.

Preparing for the task

So at this point I’ve decided on encoding a URL prefix in the compact alphanumeric mode, and the bitmap on the numeric only mode, so with the help of some python code working around the Project Nayuki qr code generator I made a map of all the QR code dots which would be available for me to modify without breaking the fixed parts (fiducials and error correction) or the URL prefix. The result is the following picture:


The floor is lava!

And since I would be checking every pixel against the payload, I can also get a map between any pixel and the corresponding bit on the numeric payload. The exact details are available on the source code repository and trust me, the code isn’t pretty, with many many sub optimal choices, but it “just works”. Alongside the picture above, a map file with all the data needed to draw a bitmap into an QR code with a URL prefix is created.

{
  "digit_count": 276,
  "ecc": "L",
  "prefix": "HTTPS://HEAVYDECK.NET/S/",
  "version": 6,
  "xy_map": {
    "11_22": 917,
    "11_23": 915,
    "12_1": 375,
    "12_10": 369,
    "12_11": 383,
    "...": "..."
  }
}

With this in hand, we can make some of the gray pixels black or white, and leave all the remaining gray / red pixels as they are.

Drawing with data

With a template picture of what to modify or not modify, and a pixel to bit table, I made a program to do just that. Take a picture (in this case in NetPBM ASCII format) and the pixel to bit mapping then generate a QR code containing the bitmap picture as data.

I am not an artist but a programmer instead, so the only correct thing to test this is saying hello! So I edit the generated picture with red/gray areas wit the drawing I want, making sure NOT to touch any red pixel.


A starting point.

Then the program generates a base QR code consisting of the prefix URL, and a random string of numbers big enough to cover all the data area of the QR code size we chose.


Noisy starting point. (link)

And with the bits to pixels data we generated before, we flip some squares around to have our final result:


Tilted world! (link)

Or rotated for human readability…


Hello! (link)

How far can we go given this power? Would a completely blank QR code be readable at all?


Yeah, it works… (link)

Although since we are at it flipping black and white pixels, why not draw something from way back when mobile phones were not all that smart, but did have games on it already?


Snek >:3 (link)

Standard display device test

No display device would be complete without the industry standard display device test.