Transferring files over the air with a smartphone camera


A barcode is a cool thing for labeling everything from goods to people. Now about two dozen standards of two-dimensional barcodes are in use, and dozens of unsuccessful, tragically misunderstood, self-made and internal corporate options, most of which miserably lose to a regular QR code. The prevalence and ease of implementation made it the most popular among 2D barcodes, but it also has a drawback that is common to all linear counterparts: it contains very little information. You can fit a link or a small piece of text into 2-3 kilobytes, but even a small picture or a regular document will no longer fit even the largest code.

It is clear that the capacity of a regular QR-like square can be increased by increasing the size of the matrix, adding colors and playing with the shape of the cells. There is no single standard for such extended codes, except for the proprietary Microsoft Tag (HCCB) with unclear prospects for development and use. Expanding the palette and changing pixels also makes it harder to read the code in difficult conditions (bad printing, color or insufficient lighting), but still gives a very limited increase in capacity and does not allow even a megabyte of data to be transferred without pain.

A good example of such a homemade standard is Jabcode

It has long been proposed to solve the problem radically: unlike linear barcodes, QRs are found not only in printed form, but also on device screens in applications and on websites. This means that if something in the standard has to be compromised, let it be the possibility of physically printing the code – let’s make it animated! Thus, we instantly turn a static square into a source of an unlimited amount of data that can be transmitted over the air without connecting to any networks. Of course, the transmission speed will be low, and a regular QR reader will not be able to process such a code, but the decoding principle is almost the same and you will not have to reinvent the wheel.


The first ready-made and thoughtful implementation of all that I managed to find was TXQR from Ivan Danilyuk (blog, Github). The project has a specification and PoC in Go with an iOS application.

At first, the TXQR code simply scrolled through normal QR codes in a loop, and the phone tried to read them as accurately as possible, receiving error correction only the next time the loop was repeated. In tests, the author ran the file at 13 kilobytes, reaching a peak speed of 9 KB / s. In the next iteration, the format began to use fountain codes (more precisely, Luby transform code) in order to make it possible, through redundancy, to perform error correction in any frames without waiting for the loop to scroll:

In short, the original data is divided into blocks, which are randomly XORed into encoded blocks, which are already sent to frames. Some blocks may not stick together, which reduces overhead. On the client, XOR blocks received from frames are with already received data, as a result, an original data structure checked for errors is obtained. You can read more at wiki and in article Ivan about the transition to fountain codes. In general, despite the significant overhead, the data decoding speed has increased significantly:

Now at its peak TXQR produces about 25 KB / s with low error correction. Let’s remember this meaning and move on to its successor, created two years later. Meet,


Cimbar (Color Icon Matrix bar codes) moved further away from the QR code standard, retaining only visual similarity. This format squeezes out as much bandwidth as possible, using colors, 8×8 icons instead of regular pixels and, of course, animation. 4 bits of information are encoded into one cell, and another 2 or 3 bits are added due to the use of a color scheme (for 4 or 8 colors), allowing you to store up to 7 bits in one cell:

Encoding in cimbar follows an algorithm similar to image hashing: the decoder compares a 4×4 tile with a dictionary of 16 expected tiles and chooses the one with the closest hash. Similarly, for each tile, the average color is taken and compared with a dictionary of expected colors, then the closest one is selected. Then the received data is checked for errors by the combination of the Reed – Solomon code, for fountain codes it is used wirehair… In the current (not final) format, the grid size is 1024×1024 characters, and when used on small screens or with a bad camera, the code does not always read well, so the developer is considering sacrificing bandwidth for the sake of universality.

The final picture looks quite creepy and epileptic, but it allows you to achieve stable 700-800 KB / s! This is 32 times faster than the TXQR peak rate with underestimated error correction, and you can see for yourself:

  1. Go to and load the heavier file. It’s better not to go to extremes at first, I tried to download a 10MB .apk once and got tired of holding the phone while it was swinging, so it’s worth starting with 500KB-2MB.
  2. Downloading the application for Android
  3. We turn on the flight mode and scan the code. Do not forget to choose the desired color palette, by default there are 4 colors on the website and in the application.
  4. Enjoy the magic!


While the project exists as a PoC, the developer clearly wants to refine it and develop it into a useful standard. Docks and implementation in python lie in main repository, an optimized C / C ++ library live separately, mobile app here… On the site the encoder works through wasm, the repo could not be found.

Air-gapped data transfers are cool and it’s always fun to come up with use cases for them. I have already tried transferring applications through cimbar and I see many interesting possibilities in this. If you also have original ideas, we will discuss them in the comments.


Our company offers rent VPS for absolutely any project. We offer a wide selection of tariff plans, the maximum configuration will allow you to place almost any project – 128 CPU cores, 512 GB RAM, 4000 GB NVMe!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *