Code 39 Barcode Generator

Code 39 is the elder statesman of alphanumeric barcodes — simple, forgiving to print, readable by practically anything ever sold with a scan head, and still required by plenty of industrial and legacy systems. Its price is a hard 43-character alphabet and a wide footprint. Both are stated below, not glossed over.

Code 39

Code 39 settings

Uppercase A–Z, digits, space, and - . $ / + % only. 1–60 characters.

The check-digit readout appears here once a barcode is generated.

Enter content above to generate a barcode.

Output options

Both presets retain the required quiet zones and must pass the independent decoder before downloads are enabled.

Hiding the text changes only the caption. The bars still encode the same validated value.

For scanning and print, prefer PNG, SVG, PDF, or EPS — JPEG compression can soften bar edges. ZPL downloads as a text file of native printer commands, not an image. Need a different image format? Convert the PNG at fileconverters.ai.

Before print or production use: test the downloaded barcode with your own scanner or scanner app. This tool verifies the mathematics (check digits, character sets) and the rendered bars — it cannot verify your printer's contrast, size, or quiet-zone reproduction.

Read next: Code 128 code sets and the Code 39 charset walks through the analysis this validator performs — which characters force a Code 128 code set, when set C compression applies, and how the Code 39 table is enforced. More in the guides to the verification layer.

The character set — all of it

Standard Code 39 encodes exactly these 43 characters (␣ marks the space):

ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789␣-.$/+%

Counted from the validator's own table: 26 letters, 10 digits, 1 space, and 6 symbols — -.$/+% — so 26 + 10 + 1 + 6 = 43. The start/stop asterisk is absent because it is a delimiter, not data; a typed * is just another unencodable character, as the walk-through below shows.

No lowercase, no commas, no #, no accents. When your input contains lowercase letters this tool uppercases them and reports the change in the readout; when it contains anything else, generation stops and the offending characters are named. A "full ASCII" Code 39 variant exists that pairs characters to reach the rest of the ASCII table, but it needs a scanner configured to expect it — this tool sticks to the standard set that every reader understands.

What the validator does before a single bar is drawn

Every generator on this site follows the same order — normalise the input, enforce the symbology's length and character rules, compute or verify any check digit, stop on a mismatch — before an accepted payload reaches the renderer. Code 39 has no check digit, so here the pipeline has these stops. First, an empty box is refused. Second, if any lowercase letter is present, the whole input is converted to uppercase and a note is recorded. Third, every character is checked against the 43-character table; each distinct offender is collected once, in the order it first appears, and a non-empty list stops the flow with those characters named. Fourth, the length cap is enforced. A payload that clears every stop is passed on exactly as it stands — nothing dropped, substituted, or trimmed in silence.

The readout beneath the generator prints the notes from that pipeline. PART-4402 needs no normalisation, produces 0 notes, and reaches the renderer as PART-4402, byte for byte. part-4402 produces 1 note, quoted here exactly as the engine writes it — “Code 39 has no lowercase letters — your input was converted to uppercase.” — and becomes PART-4402. That note is the disclosure policy in one line: something changed, and the readout says what.

Case is the only substitution this validator makes, because it is the only one whose intent is unambiguous: a lowercase letter has exactly one Code 39 counterpart. A comma, a hash, an accented letter, or a stray asterisk has none, and choosing a replacement for you would launder an input error into a valid wrong label — the same reasoning that makes the GTIN generators report a wrong check digit instead of repairing it. The charset check itself knows nothing of case: run on the raw lowercase text it reports “p”, “a”, “r”, “t” as unencodable, so the conversion must happen one step earlier, in the validator, and that is where the disclosure is written.

Worked walk-through: fictional inputs

None of these is a real part number. Each row is computed at build time by the validator that runs in your browser, so the messages are the engine's own words.

Input What the validator does What the readout or error says
PART-4402 Accepted unchanged — 9 characters, 0 notes. Payload PART-4402; no note.
part-4402 Uppercased; the change is disclosed. Payload PART-4402; note: “Code 39 has no lowercase letters — your input was converted to uppercase.”
␣PAD␣ Accepted exactly; both edge spaces kept — 5 characters, 0 notes. Payload ␣PAD␣; no note.
AB#CD Refused; the offender is named. “Code 39 cannot encode “#”. Its character set is A–Z, 0–9, space, and - . $ / + % — for anything richer, use Code 128.”
*AB* Refused; the delimiter is not data. “Code 39 cannot encode “*”. Its character set is A–Z, 0–9, space, and - . $ / + % — for anything richer, use Code 128.”
CAFÉ Refused; no Code 39 counterpart exists. “Code 39 cannot encode “É”. Its character set is A–Z, 0–9, space, and - . $ / + % — for anything richer, use Code 128.”
a!a! Uppercased first, then refused; the strict charset check on the raw text would have listed “a” and “!”. “Code 39 cannot encode “!”. Its character set is A–Z, 0–9, space, and - . $ / + % — for anything richer, use Code 128.”
A repeated 61 times Passes the character check, then refused by the length cap — the last rule. “Code 39 is a wide symbology; this tool caps input at 60 characters because longer symbols become impractical to print and scan.”

If the validator's rules changed, this table would change with them or the build would fail.

Nine elements, three wide

The name is the design: each character is nine elements — five bars, four spaces — of which exactly three are wide. That fixed 3-of-9 structure makes every character self-checking (a single misread element cannot produce another valid character), which is why Code 39 traditionally ships without a check digit. It is also why the symbology is so wide: nine elements plus an inter-character gap per character adds up fast, which is why this tool enforces a cap of 60 characters. The validator's own words: “Code 39 is a wide symbology; this tool caps input at 60 characters because longer symbols become impractical to print and scan.”

Reading the readout: common misreadings

  1. “The tool changed my data.” Only lowercase letters, and only to their uppercase counterparts; the note quoted above is written every time it happens. A payload with no note is your input, byte for byte.
  2. “A space at the edge does not count.” It does. The GTIN generators strip the spaces and hyphens people paste into digit strings because a GTIN contains neither; here the space is one of the 43 characters, so ␣PAD␣ stays 5 characters with 0 notes, and only a genuinely empty input is refused. A legacy system that expects a padded field gets the padding you typed; one that does not will see a leading space you did not notice. Read the payload in the readout, not the input box.
  3. “The error named one character, but I typed several bad ones.” Each distinct offender is listed once, in first-appearance order, after the uppercase step — which is why a!a! is refused for “!” alone.

What this generator does not add

No mod-43 check digit (optional in the standard, surprising to systems that don't expect it), no full-ASCII extension, no start/stop asterisks in your data — the renderer applies the delimiters itself. What you type, uppercased if needed, is exactly what a scanner will beep back. The downloads include a ten-module quiet zone on each side; keep it.

Because no check digit exists, the payload cross-check the GTIN generators run after validation — re-deriving the check digit with independent arithmetic — has nothing to recompute here and passes by construction: for PART-4402 it returns true. The symbol's integrity rests on the charset check plus the self-checking 3-of-9 structure, and the readout shows no check-digit line because there is none.

The ZPL download keeps the same discipline: the exporter re-runs the charset check on the validated payload before building the label document, and its first note for PART-4402 reads “Code 39 is printed without a check digit, matching the on-site generator.” How it treats the GTIN symbologies differently — stripping the check digit for the printer to recompute, and saying so — is in ZPL and EPS export explained.

Verified after rendering — and where that stops

Validation is the first of two checks. After the renderer draws the accepted payload, the page rasterises the SVG you are looking at and asks an independent decoder to read the pixels. For Code 39 the comparison is strict: the decoded text must equal the validated payload exactly. Computed at build time, a decode of PART-4402 matches (true) and a decode of PART-4401 does not ( false); the only equivalences the matcher tolerates are the leading-zero spellings of UPC-A and EAN-13, which cannot arise here. Two outcomes withhold the preview and keep the downloads disabled: the decoder cannot read the bars, or it reads a different payload. The PNG is rendered to its own canvas and decoded again before export; the vector PDF is translated from the decoded SVG's rectangles and caption rather than embedding a screenshot.

Where that stops is where every page on this site stops: math, not optics. The decoder reads a clean rasterisation on your screen; no browser check can certify ink, substrate, scaling, contrast, or quiet zones after printing, and a browser cannot stand in for the legacy reader that is usually the reason Code 39 was chosen — the production sample must be scanned by it. How the verifier decodes what you see covers the reasons the decoder can report and the bulk-PDF exception; the methodology page draws the full boundary.

Code 39 or Code 128?

The two symbologies share one validation layer with different tables, and the difference shows in what it says. Through the Code 128 analyser, PART-4402 is accepted as typed with the advice “Uppercase text with digit runs — code set B (or A) for the text, switching to code set C where four or more digits run consecutively.” The lowercase form is accepted as typed too — “Contains lowercase letters, which only code set B provides — the encoder will use code set B (switching to C for long digit runs).” — where Code 39 could only uppercase it and say so. Code 128 also carries an internal check character inside its bars, never typed or displayed; standard Code 39 has none. When you control both label and reader, choose by the data: lowercase, punctuation beyond the 6 symbols, or anything near the 60-character cap points to Code 128; a system that expects Code 39 gets Code 39. The guide Code 128 code sets and the Code 39 charset walks through the analysis behind both verdicts; all guides lists the series.

Code 39 FAQ

Exactly which characters does Code 39 support?

43: the 26 uppercase letters A–Z, the 10 digits 0–9, space, and the 6 symbols - . $ / + %. That is the whole menu. This tool converts lowercase to uppercase (and tells you it did); any other character is rejected by name rather than silently dropped.

Why is my Code 39 so wide?

Each character spends nine elements, three of them wide — plus a gap — so Code 39 typically needs half again to twice the width of Code 128 for the same text. That inefficiency is structural; the fix, when you control both ends, is switching symbology, not squeezing the print.

What are the asterisks I see in some Code 39 text?

Start and stop delimiters. The symbol begins and ends with a reserved * pattern so scanners know the boundaries and direction. The renderer adds them automatically — never type them into your data.

Does Code 39 have a check digit?

Not by default — most Code 39 deployments rely on the symbology’s self-checking bars alone. An optional mod-43 check digit exists for systems that demand it; this tool does not append one, and says so, because a check digit the receiving system does not expect reads as a stray extra character.

Why use Code 39 at all in 2026?

Compatibility. Decades of industrial, logistics, and government systems (including formats like the DoD’s LOGMARS conventions) were built on Code 39, and labels that feed them must match. For anything new, Code 128 encodes more, in less space, with a mandatory internal check.

Why does this tool stop at 60 characters?

Because every Code 39 character spends nine elements and a gap, long payloads become very wide symbols. The validator enforces the cap as its last rule, after the character check, and its message says why: “Code 39 is a wide symbology; this tool caps input at 60 characters because longer symbols become impractical to print and scan.” It is a limit of this tool, not of the symbology; longer data belongs in Code 128.

Does the independent decode prove the printed label will scan?

No. The decoder reads the pixels of the SVG rendered on this page and withholds the preview when it cannot read them or reads a different value — that catches rendering errors, not printing problems. Contrast, size, substrate, and the quiet zones that survive printing are outside what any browser check can certify, so test the printed label with the scanner that will actually read it.

Verify before production: test the printed label with the scanner that will actually read it — especially with legacy hardware, which is the usual reason Code 39 is chosen. Everything runs locally in your browser; see the methodology page.