Base85 (Ascii85) Explained: Why It Beats Base64 on Size
How Base85 and Ascii85 pack 4 bytes into 5 characters for ~25% overhead instead of Base64's 33%, with a worked example and where PDF and Git use it.
Base85 (Ascii85) Explained: Why It Beats Base64 on Size
Most developers reach for Base64 by reflex when they need to turn bytes into printable text. It works, it's everywhere, and the cost feels invisible. But that cost is real: Base64 inflates your data by a third. Base85, also called Ascii85, does the same job with noticeably less padding, which is exactly why Adobe's PDF format and Git's binary patch format chose it over Base64 decades ago. This post walks through how the encoding actually works, where the savings come from, and when it's worth switching.
The 4-bytes-to-5-characters trick
The whole idea of Base85 fits in one sentence: it packs every 4 bytes of input into 5 printable characters, using an 85-character alphabet. That ratio is the entire story.
Here's the arithmetic. Four bytes is a 32-bit number, so it can hold any value from 0 up to 4,294,967,295 (about 4.29 billion). To represent that range in some base, you need enough digits that the base raised to the digit count covers it. With an 85-character alphabet, 85^5 equals roughly 4.4 billion, which is just barely larger than the 4.29 billion you need. Five base-85 digits is the smallest grouping that fits a full 32-bit word. Four digits (85^4 ≈ 52 million) would fall short, and six would waste space.
Compare that to Base64. Base64 turns 3 bytes (24 bits) into 4 characters, because 64 is 2^6 and four 6-bit chunks cover exactly 24 bits. So the expansion ratios are:
- Base64: 3 bytes become 4 chars, a 4/3 ratio, about 33% overhead.
- Base85: 4 bytes become 5 chars, a 5/4 ratio, about 25% overhead.
That eight-percentage-point gap is small per byte but compounds over large payloads. Encode a 1 KB blob and Base64 gives you roughly 1,366 characters while Base85 gives about 1,280. On a 100 KB asset that's tens of kilobytes of pure formatting overhead you never have to ship.
A worked example
Let me make the shape concrete with a tiny input. Take the four ASCII bytes for the word Test — that's 0x54 0x65 0x73 0x74. As a 32-bit big-endian integer those bytes form a single number, and Ascii85 repeatedly divides that number by 85, mapping each remainder to a character in the !-to-u alphabet (character code 33 plus the digit value). The result is a clean five-character group:
Input: Test (4 bytes)
Ascii85: <~<+oue~> (5 chars between the markers)
The <~ and ~> brackets are Adobe's optional start and end delimiters; strip them and you're left with the bare <+oue, five characters standing in for four bytes. That's the 4-to-5 mapping in miniature.
Two details worth noticing. First, when an input isn't a clean multiple of 4 bytes, the final group is partial — a 5-byte input still encodes to a partial trailing group, so don't assume output length always tracks a perfect 4-to-5 ratio. Second, Ascii85 has a neat shortcut: a full four-byte group of all zeros, which would otherwise be five ! characters, collapses to a single z. For sparse binary data full of zero runs, that shortcut alone can shave the output down hard.
Where Base85 actually earns its keep
The size win matters most when the encoded text is embedded in something else and travels at volume. Two long-running examples:
PDF and PostScript streams. Adobe defined an /ASCII85Decode filter so binary stream data (images, fonts, compressed content) could live inside the otherwise text-based PDF and PostScript file structure. Those streams are wrapped in <~ ... ~> markers, and using Ascii85 instead of Base64 trims roughly a quarter of the bloat across every embedded object in a document. In a font-heavy PDF that adds up fast.
Git binary diffs. When you run git diff --binary or Git stores a binary delta in a patch, it doesn't use Base64 — it uses a Base85 variant with a length prefix per line. A binary patch is meant to be applied mechanically and often shuttled through email or review tools, so keeping it compact while staying plain-text-safe is exactly the trade-off Base85 was built for.
The common thread: these are machine-to-machine binary streams, not human-facing URLs. That distinction matters for the next section.
Ascii85, Z85, and when to stay on Base64
There's more than one Base85 alphabet. Adobe's Ascii85 uses characters ! (33) through u (117), which unfortunately includes backslash, backtick, and quote marks — awkward to drop into a source-code string. ZeroMQ's Z85 variant was designed specifically to avoid those troublesome characters, so its output pastes cleanly into JSON, shell heredocs, and double-quoted strings. If you're embedding a small key or lookup table directly in a config file, Z85 is the friendlier choice and still beats Base64 on size.
But Base85 is not a universal upgrade. Both alphabets contain punctuation that gets percent-encoded or mangled in URLs and email bodies. For anything that travels through a URL, URL-safe Base64 remains the safer default — reach for the url-encoder or stick with base64-encoder there. Base85's home is binary streams and source embedding, not query strings.
Trying it yourself
The fastest way to internalize the 4-to-5 mapping is to watch the character counts move. I keep the base85-encoder open in a tab when I'm sizing a serialization format: I'll drop in a 32-byte token and read the outputs back to back — hex comes out at 64 characters, Base64 at about 44, and Base85 at exactly 40. The first time I did that comparison for a packet header budget, the theoretical "25 versus 33 percent" finally clicked into something I could see, and I stopped reaching for Base64 on autopilot for binary payloads.
The tool reads your input as UTF-8 bytes first, so non-ASCII text round-trips correctly: 你好 becomes its six UTF-8 bytes, gets packed into Base85, and decodes back to 你好 byte for byte. It handles both Ascii85 and Z85, recognizes and strips the <~ ~> markers automatically on decode, and points at the exact offending character when input is malformed — which is handy when you're hunting down a truncated line in a binary patch.
Base64 will always be the default people reach for first, and for URLs and email it should be. But for the cases it was meant to solve — bytes embedded in a larger text stream, at scale — Base85 quietly does the same job with a quarter less waste. Once you've seen the character counts side by side, it's hard to unsee.
Made by Toolora · Updated 2026-06-13