The WiFi password on the card by the till is set in nine point and contains a lowercase L, a capital i and the digit one. Every few guests, somebody types it wrong twice and then asks. A wifi qr code generator fixes that in under a minute. Most cafes still do not have one, because the first three results wanted an email address, or handed back a code that dies when a trial lapses.
The other end of the same problem is the invoice. A SEPA payment code, the one German and Austrian banking apps read off a bill, is a plain text block with your IBAN sitting in the middle of it. Pasting that into a site you found ten seconds ago sends your account details through somebody else’s server for no reason at all.
Both jobs are the same underneath. Take a short string with a documented grammar, encode it into a grid of dark and light squares, print it at a size that scans. The grammar sits in standards nobody reads. The printing is where most codes quietly fail, usually because somebody cropped the white border off to make it fit.
What is inside the grid
A QR code is a square of modules, the cells that read as dark or light. Version 1 is 21 by 21 and every version adds four modules per side, so version 10 is 57 by 57. Three corners carry finder patterns, the nested squares a scanner locks onto. Two lines of alternating modules, the timing patterns, run between them, and from version 2 there are alignment patterns to correct for a code photographed at an angle.
Everything left over is data. In byte mode the bitstream opens with the mode indicator 0100, then a length field (8 bits up to version 9, 16 bits from version 10), then your bytes, a terminator, and padding alternating 0xEC and 0x11. Error correction codewords are computed over that and interleaved with it. Then one of eight mask patterns is applied to the data area, scored against four penalty rules, and the lowest scoring mask wins.
That is the whole machine. Here is the working version.
QR code generator
A QR code encoder that runs from scratch in this browser tab: choose what goes in, set how much of the code is spent on error correction, and put a logo in the middle if you want one. Nothing is uploaded, not the payload and not the logo, because there is no request for this tool to make.
EPC069-12, version 002, UTF-8. A reference that starts with RF and two digits goes into the structured field, anything else into the unstructured one, because the standard allows only one of the two.
A logo pins the level to H, which recovers 30 percent of the codewords. Its width is capped at 30 percent of the code, so the patch behind it covers at most 16 percent of the area and stays inside what H can rebuild. The file never leaves the tab: it is redrawn through a canvas and embedded as a PNG this tool produced.
Barcodes are a different family with different rules, so they get a tool of their own. Same idea, one symbology at a time, with the check digit worked out for you.
Barcode generator
EAN-13, Code 128 and Code 39, drawn module by module in this browser tab. The check digit is worked out for you, the symbol comes out as SVG or PNG, and a value the symbology cannot carry is named rather than quietly dropped. Nothing is uploaded: there is no request for this tool to make.
A quiet zone is the empty margin a scanner needs to tell the first bar from whatever sits next to it. EAN-13 asks for 11 modules on the left and 7 on the right, Code 128 and Code 39 for 10 on each side, and that is what Standard draws. Wide puts 20 on each side. Drop it only when the layout around the code already leaves that space.
Red bars on a white ground are a classic way to make a code that no laser scanner can read, because a red laser sees red as white. Dark on light, and plenty of it.
Error correction, and what each level costs
The redundancy is Reed-Solomon over GF(256) with the primitive polynomial 0x11D. The four levels, L, M, Q and H, recover roughly 7, 15, 25 and 30 percent of the codewords. Those percentages count codewords, not printed area, and they describe the error case, where the decoder does not know which codewords are wrong. That is the expensive case, and what a coffee ring produces.
Version 10 at level H shows the real structure. The symbol holds 346 codewords, split into six blocks of 43 and two of 44, each carrying 28 error correction codewords. Twenty eight parity codewords correct fourteen unknown errors, so every block tolerates 14 wrong codewords out of 43. The blocks are interleaved across the grid, which is why one blot damages a few codewords in every block rather than destroying one outright.
The cost is capacity. Raising the level does not enlarge the code, it takes bytes away: at version 10 the gap between L and H is 152 bytes. When the payload stops fitting, the encoder steps up a version, and a bigger version at the same printed width means smaller modules. So read the choice as L for screens, M for general use, Q or H for anything laminated, folded, on a window, or carrying a logo.
A logo in the middle is not vandalism
Designers get told that covering the centre of a QR code breaks it. It does not, as long as the correction level was raised to pay for it. The modules under the logo are still encoded and still counted; the decoder reads them as damage and rebuilds them from the parity.
The arithmetic keeps it safe. Adding a logo pins the level to H, and the patch width is capped at 30 percent of the symbol, which covers at most 16 percent of the area. Against a budget of roughly 30 percent of codewords, that leaves real headroom for print damage on top. The slider runs from 10 to 30 percent and there is no reason to sit at the top of it.
Two cautions generators rarely mention. From version 7 there are alignment patterns near the centre, and a large patch can land on one, which costs tolerance for being photographed at an angle. And a logo with busy edges confuses the binarisation step, so the patch sits on a solid plate rather than straight on the modules. If the mark needs cleaning up first, stripping an SVG down to what it needs is the place to start.
Colour is the other quiet failure. Scanners threshold the image into dark and light, so what matters is luminance difference, not hue. The colour fields warn below a 3 to 1 ratio using the WCAG formula, the same arithmetic behind treating contrast as a number rather than an opinion. Light on dark is decoded by many readers but is not what the standard describes.
The payload formats, written out
A QR code has no idea what it contains. It carries bytes, and the camera app pattern matches the first characters to decide whether to offer “Join network” or “Add contact”. Six formats cover almost everything.
WiFi. One line, ending in two semicolons:
WIFI:T:WPA;S:Cafe Nord;P:sommer2026;H:false;;
T takes WPA (covering WPA and WPA2), WEP, or nopass. S is the SSID, P the password, and H:true marks a hidden network so the phone does not wait for a beacon that never arrives. Inside a value, a backslash escapes the five characters that would otherwise end a field: backslash, semicolon, comma, colon, double quote. A network called Cafe;Nord becomes S:Cafe;Nord. A password of only hex digits should be wrapped in double quotes, or a strict reader may take it for a raw WEP key.
vCard 3.0. Properties separated by CRLF. N is five semicolon separated components in a fixed order (family, given, additional, prefix, suffix) and the empty ones still need their semicolons. FN is the display name, required in 3.0, which is why a vCard carrying only N lands blank in some address books.
BEGIN:VCARD
VERSION:3.0
N:Weber;Anja;;;
FN:Anja Weber
ORG:Nordlicht Design
TEL:+493012345678
EMAIL:anja@example.de
URL:https://example.de
END:VCARD
Email and SMS. A mailto is a URL, so everything after the question mark is percent encoded. A literal ampersand in the body has to be %26, or it starts a new parameter and the rest of the message vanishes.
mailto:hallo@example.de?subject=Angebot%20Fassade&body=Guten%20Tag%2C
SMSTO:+4915112345678:Bitte um Rueckruf
SEPA credit transfer. The EPC069-12 layout, behind every GiroCode on a German invoice. Twelve fields, one per line, separated by LF, whole payload capped at 331 bytes.
BCD service tag
002 version: 001 needs the BIC, 002 does not
1 character set, 1 is UTF-8
SCT SEPA Credit Transfer
COBADEFFXXX BIC, optional under 002
Musterverein e.V. beneficiary, max 70
DE89370400440532013000 IBAN, no spaces
EUR12.50 amount, EUR plus a dot decimal
purpose code, 4 chars, usually empty
RF18539007547034 structured reference, ISO 11649
or unstructured text, max 140
beneficiary to originator info
Two rules trip people up. The structured reference and the unstructured text are mutually exclusive: fill one, leave the other empty. And the amount is a dot decimal between 0.01 and 999999999.99, so a German comma there produces a code the banking app refuses without explaining why. The IBAN is checked with mod-97 first: move the leading four characters to the end, replace letters with numbers where A is 10 and Z is 35, and the remainder modulo 97 must be 1.
All of this happens in your browser. The tool makes no network request of any kind, so the IBAN, the WiFi password and the phone number in the vCard never leave the tab they were typed into.
The quiet zone, and how big the code has to be
ISO/IEC 18004 requires a quiet zone of four modules on all four sides. It is not a margin you can trade away: the scanner uses it to establish what light looks like and to find the outer edge of the finder patterns. Crop tight to the dark modules, or drop the code onto a coloured band with no gap, and you have broken it as surely as by deleting a corner. The export fixes it at four modules, so place the file without trimming.
For printed size the working rule is ten to one: a camera or laser scanner reads a code from about ten times its width. That is a planning figure, not a guarantee. Run it backwards from the distance you actually need.
| Read from | Code width | Module at version 4 (33 modules) | Width with quiet zone |
|---|---|---|---|
| 30 cm, card in hand | 3 cm | 0.9 mm | 3.7 cm |
| 1 m, shelf edge or till | 10 cm | 3.0 mm | 12.4 cm |
| 3 m, poster in a window | 30 cm | 9.1 mm | 37.3 cm |
| 10 m, banner or wall | 100 cm | 30.3 mm | 124 cm |
The quiet zone adds eight modules, so a version 4 code is 41 modules wide on paper against 33 of data, a quarter more space than the artwork suggests. The other limit is ink: below roughly 0.4 mm per module, dot gain on uncoated stock starts closing the light cells. If the layout will not give you the width, shorten the payload rather than shrinking the code, and check what else changes on paper.
Send SVG to the printer
A QR code is the worst possible candidate for a raster export. It is nothing but hard edges on a strict grid, and every resample smears them. A PNG placed at an awkward scale is interpolated by the layout program, then again by the RIP, and the light gaps between modules pick up grey fringes that push a marginal code over the edge. An SVG has no pixels to interpolate: exact rectangles, rendered on the output device’s own grid.
So take the SVG for print and the PNG for signatures, slides and screens. If you need a PNG, pick a pixel size that is a whole multiple of the module count including the quiet zone. A version 4 code is 41 modules across, so 1968 pixels (41 times 48) gives every module exactly 48 clean pixels, while 2000 gives 48.78 and a soft edge on most of them. If the SVG is headed for a WordPress page instead, note that core blocks SVG uploads by default.
A short URL is a loan against your own poster
Shorteners look like free capacity. A 56 character campaign URL needs version 6 at level H, 41 by 41 modules. Shortened to 24 characters it fits version 3, 29 by 29, so at the same printed width every module is 40 percent larger and reads from further away. The temptation is real.
The trade is that you have printed a permanent object whose destination lives in somebody else’s database. Shorteners get bought, shut down, rate limited, blocked by corporate filters, and free tiers acquire expiry dates. The code does not then fail loudly, it lands on a parking page, and you cannot recall three thousand stickers already on lamp posts. Second cost: the scan preview shows a domain the reader has never heard of.
Point the code at a short path on a domain you control and set up a redirect. https://example.com/go/sale is 27 bytes, version 4 at level H: most of the saving, none of the dependency, and you can repoint it without reprinting anything. Keep the scheme in the payload, because some readers treat a bare domain as plain text. One thing this encoder cannot help with: an all uppercase URL packs far more densely in alphanumeric mode, which is not implemented here.
Barcodes and the check digit
Linear barcodes are older and stricter, and the strictness sits in one digit. An EAN-13 carries twelve digits plus a check digit computed from them. Weight the twelve alternately 1 and 3 starting with 1 on the left, add them up, and the check digit is whatever takes the total to the next multiple of ten. For 400638133392 the weighted sum is 86, so the check digit is 4 and the full code is 4006381333924.
Change that last digit to 5 and the barcode still prints, still looks normal, and scans as nothing at all. The decoder recomputes the check digit, finds a mismatch, and discards the read without reporting anything, so the till does not beep. People blame the printer, the label stock or the scanner for days. It is almost always the digit. The tool computes it from twelve digits, or verifies it if you supply thirteen.
An EAN-13 also never draws its first digit: twelve are encoded directly and the thirteenth is carried by the parity pattern of the left half. Code 128 has three subsets, A for control characters, B for printable ASCII, C for two digits per symbol, and since every switch costs a symbol the best encoding of a mixed string is a shortest path across three states. Code 39 is the simple one: 43 characters (digits, uppercase, space and - . $ / + %), narrow to wide 1 to 2, asterisks as delimiters that are not your data, and an optional mod-43 check character.
One boundary matters more than the rest: you cannot invent an EAN-13. The number has to come from a GS1 prefix licensed to you. A valid symbol built around a number you made up scans perfectly and points at somebody else’s product.
Where the tool stops
- Versions 1 to 10, which is 271 bytes at level L and 119 at H. A longer payload is refused with the capacity named, not silently truncated.
- Byte mode only: no numeric, alphanumeric or kanji mode, so a payload of pure digits is larger than it needs to be, and no structured append.
- No ECI. Bytes go out as UTF-8, which nearly every reader detects, but the default for byte mode is ISO-8859-1, so a name like Müller is technically ambiguous. Test on a phone first.
- The logo must be a raster file (PNG, JPEG, WebP or GIF). SVG is refused on purpose: the upload is redrawn through a canvas before being embedded, and a vector would defeat that.
- Code 128 covers ASCII 0 to 127 with no FNC1, so no GS1-128. EAN-13 only: no EAN-8, no UPC-A, no add-ons.
- The batch field takes 50 lines and gives one download per row rather than a zip, because a browser will not let a page start fifty saves at once. For bulk work on the files afterwards, that is the batch image processor.
- One instance per page. The element ids are fixed.
None of the interesting parts here are secret. The WiFi string is a documented one liner, the SEPA block is twelve lines in a public specification, the check digit is arithmetic you can do on paper, and the quiet zone is four modules because a standard says so. What the generators sell is twenty seconds of typing, and increasingly a subscription attached to a redirect they own.
The failures cluster in two places. One is the payload: a comma where a dot belongs, an unescaped semicolon in an SSID, a check digit off by one, and the code encodes flawlessly while doing nothing useful. The other is size: a code that read fine on a monitor, printed at 2 cm with the border cropped, on a poster meant to be read from across a room. Neither failure announces itself.
So generate it, then test it the way it will be used: at the distance, on the material, in the light of the room, with a phone that is not yours. Print one proof before the run. And when the code carries an IBAN or a network password, prefer the one that never sends it anywhere.