The two layers of a QR code
A QR code (defined by the ISO/IEC 18004 standard) is a matrix of light and dark modules. The standard fixes the physical layer: the position patterns in three corners, the alignment and timing patterns that sync the read, the white "quiet zone" around it and, most important, the Reed–Solomon error correction that lets the code be read even when damaged. None of that depends on what is written: the matrix is the envelope. Inside the envelope goes the second layer, the payload, which is just a text string. From a user’s point of view the QR is a text box: you put a string in and the reader hands it back intact.
There is no "Wi-Fi QR" or "Pix QR" at the code level, there is a text that follows a convention, and the phone app decides what to do once it recognizes the prefix. If the text starts with "https://", the browser opens; if it starts with "WIFI:", the system offers to join the network; if it is a "BEGIN:VCARD" block, the address book asks whether to save the contact. That is why the QR code generator has no magic behind each type: it just builds the right string and wraps it in the matrix. That density is what separates the QR (two-dimensional, hundreds to thousands of characters) from a traditional barcode, which is one-dimensional and holds only a short number.
- Module
- Each little black or white square. It is the smallest unit of the matrix; reading turns modules into bits.
- Version
- The matrix size, from 1 (21×21 modules) to 40 (177×177). Each higher version adds 4 modules per side.
- Error correction (Reed–Solomon)
- Extra redundancy modules that reconstruct data lost to dirt, scratches or a logo on top.
- Mask
- One of 8 patterns applied over the data to avoid large blotches that confuse the reader.
- Payload
- The string encoded inside the code, the content that the prefix convention makes the app interpret.
- 1994Denso Wave creates the QR code
Masahiro Hara, at the Japanese maker Denso Wave, designs the code to track auto parts, hence the fast, omnidirectional reading.
- June 2000Becomes ISO/IEC 18004
The QR code is approved as an international standard. Denso Wave waives patent royalties, which triggers worldwide adoption.
- 2015 / 2024Revised editions of the standard
ISO/IEC 18004 is updated (2015 and 2024 editions), yet the version structure and the four correction levels stay the same.
- November 16, 2020Pix goes live
The Central Bank launches Pix, and the BR Code (EMV) payment QR becomes part of everyday life in Brazil.
The physical layer: error correction, version and mask
Some of a QR’s modules do not hold your text: they hold redundancy. The code uses Reed–Solomon, a family of error correction that treats the data as coefficients of a polynomial and adds "check codewords" able to rebuild lost symbols. You pick how much redundancy you want among four levels. The higher the level, the more damage the code tolerates, and the more modules it spends on the same data, making it denser. The percentages below are the fraction of data the code can recover when it is corrupted or covered.
| Level | Approx. recovery | When to use |
|---|---|---|
| L (low) | ~7% | Clean, large print; squeezing in the most data. |
| M (medium) | ~15% | Default for general use (the generator’s default). |
| Q (quartile) | ~25% | Small labels; a discreet logo. |
| H (high) | ~30% | Large center logo; harsh environment. |
The level interacts with size. More correction means more check codewords; to fit everything, the code steps up a version and gains modules. Maximum capacity drops as the level rises, as the chart shows for the largest version (40, at 177×177 modules). Notice that going from L to H cuts capacity by more than half: that is the price of damage resistance.
View the data
| Category | Value |
|---|---|
| L (~7%) | 7,089 |
| M (~15%) | 5,596 |
| Q (~25%) | 3,993 |
| H (~30%) | 3,057 |
Before becoming an image, the data goes through masking. The encoder tests eight masks, patterns that flip certain modules by a rule, and picks the one with the lowest penalty, a score that punishes large single-color blotches, stripes and patterns that mimic the position markers. Without it, an unlucky text could produce a matrix the reader misjudges. That is why two QR codes of the same text, made by different software, can look different: they chose different masks, but they decode to the same content.
Why a QR with a logo in the middle still works
The logo covers some modules, exactly like a scratch or a smudge. Reed–Solomon correction reconstructs what is hidden, as long as the damage stays within the chosen level’s margin. At level H, about 30% of the data can vanish and the code still reads, which is why a discreet central logo lives well with H.
The math has a limit. If the logo is too big, the hole exceeds the recovery margin and there is not enough redundancy to rebuild the data: the code breaks. The rule of thumb is to keep the logo small and raise the correction level, the generator itself recommends Q or H once the logo passes about a fifth of the width.
Masks and the "quiet zone"
Beyond the mask, every QR needs a white border around it, the quiet zone, at least four modules wide. It separates the code from whatever surrounds it so the reader can find the edges. Cropping that margin so the code "fits" a tight space is the number-one cause of a QR that will not scan, even with a perfect matrix.
The payload layer: the content conventions
The table gathers the most common conventions. Almost all reuse URI schemes that already exist on the web (mailto:, tel:, sms:, geo:), the QR did not invent anything, it just packed the text into a scannable square. The spec column shows that each "type" has a source: an RFC, a standard or a de facto reference.
| Type | Prefix / scheme | Example | Specification |
|---|---|---|---|
| Link | https:// | https://jkit.tools | RFC 3986 (URI) |
| mailto: | mailto:[email protected]?subject=Hello | RFC 6068 | |
| Phone | tel: | tel:+14155550132 | RFC 3966 |
| SMS | sms: | sms:+14155550132?body=Hi | RFC 5724 |
| Location | geo: | geo:-23.5505,-46.6333 | RFC 5870 |
| Wi-Fi | WIFI: | WIFI:T:WPA;S:CafeGuest;P:Welcome2026;; | ZXing (de facto) |
| Contact | BEGIN:VCARD | see Wi-Fi and vCard section | RFC 6350 (vCard 4.0) |
| Contact (compact) | MECARD: | MECARD:N:Doe,Jane;TEL:+1415...;; | NTT DoCoMo (de facto) |
| Event | BEGIN:VEVENT | schedules an event | RFC 5545 (iCalendar) |
| Pix | 000201... | 00020126... (copy & paste) | EMV MPM / BR Code (BCB) |
| Plain text | (no prefix) | Table 12, call the waiter | — |
Generate any of these formats below. The tool builds the string, applies the correction level and returns the image, all in the browser, sending your content nowhere. To save the result crisply, it helps to understand image formats for the web: a QR should be exported as PNG or SVG, never JPEG, whose compression blurs the module edges.
Wi-Fi and vCard, with escaping in practice
The Wi-Fi format is one of the most useful: put the network in a QR and the guest joins without typing the password. The syntax is not an ISO standard, it is the ZXing project’s convention, which became the de facto standard nearly every reader follows. The fields are T (security type: WPA, WEP or nopass), S (the SSID) and P (the password), ended by two semicolons. A direct example:
WIFI:T:WPA;S:CafeGuest;P:Bemvindo2026;;The detail that trips people up is escaping. Because a semicolon separates the fields, an SSID or password containing the special characters backslash, semicolon, comma, quote or colon must escape them with a leading backslash, the same rule as MECARD, per ZXing. Here is the guide’s first worked example:
Rede: Rede;Café → Rede\;Café
Senha: sen:ha,123 → sen\:ha\,123
WIFI:T:WPA;S:Rede\;Café;P:sen\:ha\,123;;The contact card uses the vCard standard (RFC 6350, version 4.0). Each line is a field: FN is the display name, N splits family and given name, ORG the organization, TEL the phone. A compatibility note: RFC 6350 obsoleted RFC 2426 (vCard 3.0), yet many phones and contact apps still read 3.0 more reliably. If a 4.0 vCard opens wrong on an older phone, generating the 3.0 version usually fixes it. Here is a full vCard 4.0:
BEGIN:VCARD
VERSION:4.0
FN:Jane Doe
N:Doe;Jane;;;
ORG:Example Studio
TITLE:Product Designer
TEL;VALUE=uri:tel:+5511998877665
EMAIL:[email protected]
URL:https://jkit.tools
END:VCARDPix: the BR Code field by field and the CRC-16
The Pix payment QR follows the BR Code, the Brazilian implementation of the EMV® QR Code Merchant-Presented Mode (MPM) standard, defined by the Central Bank within the Pix arrangement. The clever part is that this text is the "Pix copy-and-paste" itself: when you copy the code, you copy exactly the string that would be inside the QR. The two are interchangeable because they are the same data. The BR Code is not free text: it is a sequence of fields in TLV format, a 2-digit identifier (ID), a 2-digit length and a value, ending in a CRC-16 check in field 63.
The guide’s second worked example is a complete static Pix, with a fictitious email key. Each line below is a TLV field; the indentation under 26 and 62 shows the nested subfields. Read it as ID, length and value:
00 02 01 Payload Format Indicator
26 38 Merchant Account Information
00 14 br.gov.bcb.pix GUI do arranjo Pix
01 16 [email protected] chave Pix (e-mail)
52 04 0000 Merchant Category Code (MCC)
53 03 986 moeda: 986 = BRL (ISO 4217)
54 05 25.00 valor da transação (opcional)
58 02 BR país (ISO 3166-1)
59 13 FULANO DE TAL nome do recebedor
60 09 SAO PAULO cidade do recebedor
62 07 Additional Data Field
05 03 *** txid (identificador da cobrança)
63 04 674F CRC-16 (verificacao final)The continuous string, with no spaces, looks like this, and it is exactly this that the app reads:
00020126380014br.gov.bcb.pix0116joao@exemplo.com520400005303986540525.005802BR5913FULANO DE TAL6009SAO PAULO62070503***6304674FField 63 closes the code with a CRC-16 in the CCITT-FALSE variant. It is not a security signature, it is a check value, like the one that closes a Brazilian CPF or CNPJ (see the CPF and CNPJ validation guide): it lets the app catch a swapped character before trying to pay. The catch: the CRC is computed over the whole string already including field 63’s own ID and length, i.e. including the four characters "6304", but before the four-digit value. The formula starts from a generator polynomial:
G(x) = x¹⁶ + x¹² + x⁵ + 1 = 0x1021- G(x)
- generator polynomial that defines the algorithm
- 0x1021
- the hexadecimal form of the polynomial (x¹⁶ implied)
- 0xFFFF
- initial value of the 16-bit register
- Initialize the registerStart with a 16-bit register set to 0xFFFF (65,535).
- Walk each byteFor each character of the string (from "00..." up to and including "6304"), XOR the byte shifted 8 bits left into the register.
- Eight shifts per byteRepeat 8 times: if the top bit (0x8000) is set, shift left and XOR with 0x1021; otherwise just shift. Keep only 16 bits.
- No reflection, no xoroutThere is no bit reflection on input or output, and the xorout is 0x0000, the result is the register itself.
- Write it in hexFormat it as 4 uppercase hex digits. For the example above, the result is 674F. To check the algorithm, the string "123456789" must yield 0x29B1, the official test vector of CRC-16/CCITT-FALSE.
Because the CRC depends on everything else, changing one cent of the amount changes the whole code, you cannot hand-edit a BR Code without recomputing the check. To find out which institution a key or ISPB belongs to, use the list of Pix participants.
Static vs. dynamic and the "quishing" risk
Static QR
- The final data is baked into the code.
- Cannot change once printed and does not track scans.
- Works forever, with no server in between.
Dynamic QR
- Stores a short redirect URL.
- The destination can be changed and scans counted.
- Depends on the redirect service staying online.
The QR code generator builds static codes: the content (link, Wi-Fi, vCard, Pix) lives entirely inside the image, with no intermediary, which is ideal for privacy and durability. Prefer dynamic only when you truly need to change the destination later or measure clicks, and in that case, remember the code dies if the shortener goes offline, and that each scan passes through a server logging date, time and often your approximate location (the same metadata footprint your photos leave, the topic of the EXIF metadata guide).
Quishing: the phishing that arrives by QR
Quishing (QR + phishing) exploits the fact that the human eye cannot read a QR. You do not see the destination before scanning, so a code can lead to a fake site mimicking your bank or asking for a login. In a dynamic QR, the visible URL is just the shortener’s, the real destination hides behind the redirect.
Practical defense: read the URL preview your reader shows before opening, distrust codes stuck up in public places, and never type a password into a page opened by QR without checking the domain. For payments, the previous section’s rule always holds: verify the payee’s name and institution on the bank’s screen.
Frequently asked questions
Is the Pix copy-and-paste different from the Pix QR code?
How is the Pix CRC calculated?
Which error-correction level should I use?
Why does a QR with a logo in the middle still work?
How much information fits in a QR code?
How do I make a QR that connects to Wi-Fi?
A QR code is two layers: a Reed–Solomon matrix (versions 1–40, levels L/M/Q/H, which explain the center logo) and a standardized text on top. Each format is just a prefix convention, WIFI:, mailto:, BEGIN:VCARD, and Pix is the EMV BR Code, TLV fields closed by a CRC-16 (0x1021, init 0xFFFF) in field 63. Understand that and you generate the right code, compute the CRC and spot a swapped QR before you pay.
Sources & references
- ISO/IEC 18004, QR Code bar code symbology specification
- Denso Wave, Information capacity and versions of QR Code
- Central Bank of Brazil, BR Code Manual
- Central Bank of Brazil, Manual of Standards for Pix Initiation
- EMVCo, EMV QR Code Specification (Merchant-Presented Mode)
- RFC 6350, vCard Format Specification
- ZXing, Barcode Contents (Wi-Fi format, de facto reference)