QR Code Generator - The Symbol Built Under Your Hands, Module by Module
Mode chosen by bit cost, version sized by arithmetic, Reed-Solomon check words computed in GF(256), all eight masks scored - and then the finished symbol is read back out of its own modules to check the symbol that came out.
How the bits were spent
| Step | Cost | Where that number comes from |
|---|
All eight masks, scored
| Mask | Dark runs and solid 2×2 blocks | Finder-like patterns | Dark against light | Total and verdict |
|---|
What this path does on the way
| Change | What happens to it | This run |
|---|
Read back out of the finished grid
How mode, version and mask are picked
Seven steps, each one arithmetic
Seven steps, and each one of them is arithmetic rather than a lookup: your text becomes UTF-8 bytes, the modes that could carry it are priced in bits and the cheapest one that fits is chosen, the smallest version whose data bits cover that cost is computed, the bits are cut into blocks, a Reed-Solomon remainder is divided out of each block in GF(256), the blocks are interleaved and written along the zigzag path that skips the three corner patterns and the two timing tracks, and all eight mask patterns are flipped onto the result and scored so the winner is the one with the lowest measured penalty.
The format area and the version area are the last two things placed, and both come out of the same short polynomial division: fifteen bits for the error correction level and the mask, eighteen for the version number from version 7 up. Nothing here is a library, and nothing is uploaded - the machine you are reading this sentence on did all of it.
The tables underneath the picture are not reference material. The bit ledger, the eight mask scores and the read-back are all numbers from the run you just made, which is why they change when you change the level and why they are empty until the first symbol exists.
What an error correction level actually buys
A level is a split of the codewords between your data and the repair words. A version 1 symbol holds 26 codewords either way; at level L, 19 of them carry your text and 7 are check words, and at level H, 9 carry your text and 17 are check words. That is the whole difference, and it is why the same sentence at H sits in a bigger version than it does at L: fewer data words per block means more blocks.
The figures you usually see quoted - about 7% for L, 15% for M, 25% for Q, 30% for H - are estimates of how much damaged ground a reader can still work through, not the check-word fraction above, and they are not a licence to stick a logo over a third of a code. Three reasons. The ratio is an average over the whole symbol, so it says nothing about where the damage lands. Damage arrives in clusters - a scuff, a coffee ring, a sticker - and a decoder reconstructs a run of destroyed words far worse than the same number of words scattered across the grid. And the parts that carry none of your text are not spread out at all: the three finder patterns, the two timing tracks and the format area are fixed, named places, and a symbol that has lost one of them is not readable at any level.
What you trade for repair capacity is room and size. The version and the level are printed together, with the modules per side and the pixels per module, because the practical cost of level H is a denser grid, which on paper means a smaller module - and a module under roughly 0.4 mm is a thing a phone camera at arm’s length will not resolve, however good its error correction is.
Why the mode is chosen by price, not by appearance
The three modes cost different amounts per character, and the difference is large. Numeric mode spends 10 bits on every three digits, which is about 3.3 bits a character. Alphanumeric mode spends 11 bits on every pair of characters from its set of forty-five, so 5.5 apiece. Byte mode spends a flat 8 bits per UTF-8 byte. On top of the data itself each mode pays a header: four bits for the mode indicator, then a character count whose width depends on the version - it jumps when the symbol passes version 9 and again past version 26.
So “auto” prices every mode that could carry your text, then takes the one that lands in the smallest version, and the header is why the answer is not always the cheapest per character: a mode with a wider count indicator can cost more bits overall than a longer payload in a narrower one. It is also why a lower-case URL is a byte-mode symbol rather than an alphanumeric one - the alphanumeric set is upper case letters, digits and space, and there is no lower case in it. Forcing a mode is allowed, and a refusal is preferred to help: if a character your chosen mode cannot represent is in the text, that character and its position are named and the run stops, because quietly re-encoding into another mode, or folding the case to make the button work, would hand you a symbol that reads back as something other than what you typed.
Two absences worth naming. There is no kanji mode here, so Chinese goes through byte mode at eight bits a byte, which is about 24 bits per han character and is why a short Chinese sentence fills a version on its own. And one mode indicator is written for the whole string, where a segmenting encoder splits mixed content into a chain - one alphanumeric run and two digits of numeric, say - and gets a smaller symbol out of it. Splitting is a real technique and a real gain; it is also a claim about your text that is not made on your behalf here, so what you pay for a mixed string is a version or two more than a splitting encoder would have needed.
Decoding the finished symbol back
Once every module is placed, the finished grid gets decoded by a second path through the same file: both copies of the format area lifted and compared against each other, the version bits read out where the version is big enough to carry them, the mask number and the level taken from the BCH value that was divided onto those modules, then the mode indicator, the character count, the data words in the order the encoder visited them, and the Reed-Solomon remainder recomputed from what the grid now says each block holds.
Be clear about what that proves. It is the same page’s understanding of the specification used twice, so a shared misreading round-trips perfectly - the real test of whether this is a QR code is a scanner you do not own. What a round trip does catch is the family of bugs a placement encoder is actually written with: a bit cursor that desynchronises between the header and the payload, an interleaving order that does not match the one used to write, a mask flipped onto a function module, an off-by-one in the block split. Those break the read-back even when both halves live in one file and agree about everything else.
Where your browser offers a barcode reader of its own, it gets asked, and the answer is a second opinion written by people who never met this file. Where it does not - and that is a real number of browsers, on purpose rather than by accident - the row says so plainly instead of staying silent, because a table with the interesting column missing looks exactly like a test that passed.
The limits, stated
The largest symbol there is holds 3706 codewords in a grid 177 modules on a side, and at level L that is 2,953 bytes of data, so the text is stopped at 4,000 characters: past the largest symbol, a longer input is a file, and a code you cannot photograph is not much use. The drawing stops at 4,096 pixels a side, which is the canvas ceiling most browsers enforce rather than one invented here; the symbol is identical either way, so the fix is the module size or the quiet zone.
The mask winner is a heuristic about how a camera copes, not a correctness rule. Four penalties are summed - long runs of one colour, solid two-by-two blocks of it, runs shaped like a finder pattern, and too much dark ground overall - and a symbol that scans with mask 6 will scan with mask 3 as well, just with a worse margin. When two masks score the same the lower number wins, which is the tie-break the worked examples use, and it is also why all eight scores are listed rather than a single number: you can see how close the race was. The finder-shaped rule is measured across each row and each column of the module grid itself, without the quiet zone appended, which is the common convention and not the only one; a reader that counts the blank ground as part of the line would score it slightly differently.
Not implemented, and no attempt to hide it: ECI labels, which tell a reader which character set the bytes are in, so the UTF-8 written here is a convention a scanner has to guess at; FNC1 and the GS1 symbol variants; structured append, which splits long data across several codes; and micro QR, which is a different geometry with no finder patterns at all. Anything that needs one of those gets byte mode or a refusal here, not a symbol that lies about which format it is. The quiet zone defaults to four modules because four is what the specification asks for, not because four is comfortable on a printed poster, and the inverted colours switch is a drawing decision your scanner is under no obligation to accept.