A scanner does not see bars. It sees a strip of light coming back off a surface, converts that to a waveform, and measures the distance between the points where the signal crosses from light to dark and back. Everything a linear barcode encodes lives in those distances. That single fact explains most of what looks arbitrary about barcodes: why the white margin around the symbol is part of the specification, why a code stretched by one corner handle in a layout tool stops working, and why the answer to "how small can I print this" is a number in millimetres rather than in pixels.
The Number Is in the Widths
Every linear symbology divides its symbol into a grid of equal columns called modules, and the width of one module is the X-dimension. A bar is one or more dark modules in a row, a space is one or more light ones, and a character is a fixed pattern of them.
Code 128 makes this unusually tidy: every character is exactly 11 modules wide, split into three bars and three spaces, and ISO/IEC 15417 lists all 107 of the patterns it uses. Code 39 works differently. Each of its characters is nine elements, three of them wide and six narrow, which is where the name comes from. EAN and UPC give every digit seven modules arranged as two bars and two spaces.
None of this depends on absolute size. A scanner reading a symbol has no idea how large it is or how far away it sits; it works entirely in ratios between the widths it measures. That is why the same artwork scans on a bottle label and on a warehouse sign, and why scaling a barcode is safe as long as both dimensions scale together. Stretch it horizontally only and the ratios still hold, which is why a barcode survives being dragged wider; squash it enough and print artefacts start to matter more than the shape.
Why the Last Digit Is Not Yours
A misread barcode is worse than an unread one. An unread code makes someone scan again; a misread code sells the wrong product at the wrong price and nobody notices. Check digits exist to make the second failure improbable.
The modulo 10 rule GS1 uses across EAN-8, UPC-A, EAN-13 and GTIN-14 is the same in all four: counting from the right-hand end of the data, multiply the digits alternately by 3 and 1, add them up, and the check digit is whatever brings the total to the next multiple of ten. For the EAN-13 data 590123412345 the weighted sum comes to 83, so the check digit is 7 and the full code is 5901234123457.
That scheme catches every single-digit error and most transpositions of adjacent digits, which are the two mistakes a human keying a number in tends to make. It does not catch a transposition of two digits that differ by 5, a known blind spot of alternating 3-and-1 weighting, which is one reason the code is meant to be scanned rather than keyed.
Code 128 does something different and stronger. Its check character is a weighted sum modulo 103 in which each symbol counts by its position, so an error in the fifth character and a compensating error in the ninth cannot cancel out. Code 39's modulo 43 check character is optional, and mostly unused, because a lot of the systems that still read Code 39 would report it as an extra character of data.
How Each Symbology Protects Itself
Every symbology here also carries per-character structure, such as a fixed module count or a fixed number of wide elements. A reader checks that long before it reaches the check digit.
| Symbology | Check character | Added automatically? |
|---|---|---|
| EAN-13, EAN-8, UPC-A, UPC-E | Modulo 10, weights 3 and 1 | Yes, it is part of the number |
| ITF-14 (GTIN-14) | Modulo 10, weights 3 and 1 | Yes, it is part of the number |
| Code 128 | Modulo 103, weighted by position | Yes, and never printed |
| Code 39 | Modulo 43 | Optional, off by default |
| ITF (generic) | None defined | No, the application decides |
| Codabar | None defined | No, the application decides |
The white space is part of the barcode
The blank margin either side of a symbol is called the quiet zone, and it is specified, not decorative: 10 X-dimensions for Code 128, Code 39 and ITF, 9 for UPC, 11 on the left of an EAN-13. A scanner uses it to establish what "light" looks like before it starts measuring anything. Crop it to make a code fit a label and you get a symbol that reads perfectly on a white screen and fails against a printed background, which is one of the harder faults to diagnose because the barcode itself is untouched.
The Trick That Makes Code 128 Short
Code 128 covers the whole ASCII set in 11 modules per character, which is dense, but it has a second gear. The symbology defines three character sets that share one table of bar patterns: A holds capitals, digits, punctuation and the ASCII control codes, B swaps the control codes for lower case, and C encodes nothing but digits, two of them per symbol.
That last one is why a numeric Code 128 is about half the width you would expect. A twelve-digit code in subset B needs twelve symbols; in subset C it needs six. Switching sets costs one symbol, so the break-even is four digits: a run of four or more pays for the switch and then keeps saving.
A good encoder does this without being asked. It starts in whichever set the data opens with, drops into C the moment a long enough digit run appears, comes back out for the letters, and where a single stray character needs the other set it uses the shift code rather than switching twice. Two encoders can make different legal choices here and both be correct: the symbol will differ, the decoded string will not.
How Small You Can Print It
This is the question that decides whether a barcode works, and pixels cannot answer it. What matters is the X-dimension on paper.
The GS1 General Specifications set the floor for a retail point-of-sale symbol at 0.264mm and call 0.33mm the nominal size, with a permitted range up to 0.66mm for larger packaging. Below that floor, two things go wrong at once: the printer's ink spreads and starts closing the gaps between bars, and the scanner's aperture stops resolving individual elements cleanly. ISO/IEC 15416, the print quality standard, grades a finished symbol from A to F on exactly those properties (edge contrast, modulation, decodability) by running a reference scan across it.
Height matters too, and it is the dimension people cut first. A scanner sweeping across a symbol at an angle needs to cross every bar in the same pass, so the taller the symbol, the more angles work. Roughly 15% of the symbol's width is the usual guidance, and EAN and UPC have a nominal height fixed outright. A truncated barcode still decodes; it just needs the beam to cross it squarely, which is why the person at the till passes it over the glass three times.
What the X-Dimension Buys You
Figures for a retail EAN or UPC symbol, from the GS1 General Specifications. Other symbologies set their own ranges, but the failure modes below the floor are the same.
| X-dimension | Magnification | Where it belongs |
|---|---|---|
| 0.264mm | 80% | The smallest a point-of-sale symbol may be printed |
| 0.330mm | 100% | Nominal size, the default unless something forces otherwise |
| 0.495mm | 150% | Larger packaging, or printing onto corrugated board |
| 0.660mm | 200% | The largest a point-of-sale symbol may be printed |
When the Number Needs to Say What It Is
A GTIN identifies a product. It does not carry a batch, an expiry date or a weight, and a warehouse usually needs all four. GS1-128 is the answer: an ordinary Code 128 that opens with the FNC1 character and then holds a sequence of element strings, each beginning with a two to four digit Application Identifier naming what comes after it. (01) is a GTIN, (17) an expiry date as YYMMDD, (10) a batch, (3103) a net weight in kilograms to three decimal places.
The hard part is not the identifiers, it is knowing where each field stops. Some AIs have a predefined element string length, so a reader counts out the right number of characters and moves on. Every other AI is variable length and has to be closed with an FNC1 separator when something follows it. Miss one and the symbol still scans: it just hands the receiving system a batch number with the next field glued onto the end.
Two details catch people out. Predefined length is decided by the AI’s first two digits rather than by the AI, so (402) needs a separator despite carrying a fixed seventeen digits, because the prefix 40 is not a predefined-length prefix. And the last element never takes a trailing separator, so the order you write the fields in changes how long the symbol comes out: put the shortest variable-length field last and you save a character.
A barcode reads the same upside down in brightness
Code 128, Code 39, ITF and Codabar are all decoded from the widths of their bars and spaces, and inverting a symbol leaves every one of those widths untouched. A photographic negative of a Code 128 therefore decodes to exactly the same string. That is why light bars on a dark background cannot be caught by decoding a symbol, and why a reader calibrates on the quiet zone instead: the blank margin is the light reference, and if it comes back dark the reader knows it is looking at the wrong polarity. EAN and UPC escape this because they match literal guard bit patterns rather than run lengths.
Which Symbology You Actually Need
Most of the time this is not a preference. Either something outside your control has already specified the format, or the code is internal and one answer is clearly best.
Code 128: anything internal
Full ASCII, the densest numeric encoding of the common symbologies, a strong check character, and universal scanner support. If nobody is forcing your hand, this is the answer.
EAN-13 and UPC-A: retail products
Not a choice and not something you can invent. The leading digits identify the company that owns the number, and they are issued by GS1 member organisations. EAN-13 outside North America, UPC-A within it, though modern tills read both.
ITF-14: the shipping carton
Carries the GTIN-14 of a case rather than an item. It is built for direct printing onto corrugated board, where the surface is rough and the print is coarse, and it is normally drawn with a bearer bar around it.
Code 39: older industrial systems
Digits, capitals and a handful of symbols, no more. It is wider than Code 128 for the same data and has no mandatory check character, so it survives mainly where a standard written decades ago still names it.
Codabar: libraries, blood banks and labs
Predates most of the others and persists in specific industries because their existing databases and readers assume it. Each symbol is wrapped in one of four start/stop letters, which those systems use to signal what kind of number it holds.
Why ITF symbols get a frame around them
ITF interleaves a pair of digits into one block, half in the bars and half in the spaces, with nothing marking where one character ends. A beam that crosses the symbol diagonally and exits partway along can therefore collect something that looks like a complete, valid, shorter number. That is a short scan. The bearer bar is the solid frame around the symbol, and a scan that leaves through it is thrown away rather than reported. On a carton read in motion, that frame is doing real work.
Draw one and check the numbers
Eleven symbologies, check digits worked out or verified, quiet zones drawn in, and the printed size in millimetres before you commit to a print run.
Frequently Asked Questions
Related Tools
Keep Reading
How QR Codes Actually Store Your Data
Every module is doing a specific job: some hold your data, some hold Reed–Solomon error correction, and a few fixed patterns just help a camera find the code at all.
Why the Same SVG Exports at Three Different Sizes
width and height are optional, and an icon set ships neither. What a rasterizer falls back to, and why a file with no viewBox cannot be scaled at all.
Why HSL Says Yellow and Blue Are Equally Light
Two colors, both hsl 50% lightness, eight to one apart in contrast. Where the sRGB gamma curve hides the difference, and how OKLCH puts it back.
Which UUID Version to Use, and Why v7 Changed the Answer
A random primary key splits B-tree pages and scatters an InnoDB table. RFC 9562 fixed that in 2024 by putting the clock first. What each version really holds.