Base91 Encoder / Decoder
Compact binary-to-text encoding: about 23% overhead, against Base64's 33%.
What is Base91?
basE91 is Joachim Henke's binary-to-text encoding. Its alphabet is 91 of the 94 visible ASCII characters, all but the hyphen, the apostrophe and the backslash, and each pair of its characters carries 13 or 14 bits of the input, where two Base64 characters carry 12. So text and compressed data come out about 23% longer than the input, where Base64 makes them 33% longer, and basE91 is about 8% shorter; runs of zero bytes come out as little as 14% longer. This tool follows Henke's reference implementation. Its decoder skips spaces and line breaks, as the reference does, and lists any other character it skipped, because a stray character shifts every pair after it. Everything runs in your browser.
Built and maintained by Meet Shah · Last updated
What this tool is used for
- Packing binary data into text where every byte of overhead counts and the channel is not a URL.
- Comparing encoding efficiency against Base64 for a size-sensitive payload.
- Decoding a Base91 blob produced by a tool that chose density over compatibility.
- Demonstrating why a larger alphabet buys a better ratio than Base64 manages.
- Encoding a compressed archive for embedding in a text-only transport.
- Peeling a basE91 layer off a capture-the-flag string after Base64 and Base85 have both failed on it.
How it works in practice
A worked example
A capture-the-flag challenge hands you a line of letters and punctuation that neither Base64 nor Base85 will decode.
@iH<,{,Hb%y@_@Elr}df6=8eL%8/<P:m9GH<f,#XBWflag{basE91_is_denser_than_base64}Base64 rejects it at the first character, because @ is not in its alphabet; Ascii85 stops at the brace and Z85 at the comma. The giveaway for basE91 is punctuation such as @, <, { and % among the letters and digits, with no hyphen, apostrophe or backslash anywhere. Decoding reads the characters in pairs, low digit first. Here @ is digit 80 and i is digit 34, so the first pair is 80 + 91 × 34 = 3,174. That is more than 88, so the pair carries 13 bits, and their low eight are 0x66, the f of flag. The 42 characters make 34 bytes, which Base64 would have written in 48.
The edge case that catches people
A straight double quote is a basE91 character, the last in its alphabet, and a word processor or chat app may hand it back curly. The curly quotes are not in the alphabet, so the decoder skips them, as the reference decoder skips any byte it does not know. That costs one character, so every pair after it is misaligned and everything after that point decodes to garbage. The tool lists each non-whitespace character it skipped, with its code point, so a curly quote shows up as the cause instead of leaving a silently wrong result.
When not to use this tool
If the string came off a radio, it may be APRS, whose position reports also use base 91 but write each digit as its value plus 33, the characters ! to {, in fixed-width fields; they decode to nonsense here. And when you are choosing an encoding rather than reading one, basE91 is worth it only when both ends are yours. It is about 8% shorter than Base64 on compressed or random data, Python, Java, Go and JavaScript have no built-in decoder for it, and percent-encoding it for a URL makes it longer than Base64URL.
Frequently Asked Questions
- How much better than Base64 is it?
- About 8% shorter on compressed or random data: basE91 adds 23% to the size of the input, where Base64 adds 33%. Each pair of characters carries 13 bits or 14, against 12 for two Base64 characters. A megabyte of random data comes out roughly 100 KB shorter.
- Why 91 characters?
- 91 is the smallest alphabet in which two characters can hold 13 bits: 91 × 91 = 8,281 pairs cover the 8,192 values, and 90 × 90 = 8,100 do not. The 89 spare pairs let the encoder take a 14th bit when the 13 make a value of 88 or less. The 91 are the visible ASCII characters less the hyphen, apostrophe and backslash.
- Why is Base64 still the default?
- Universality. Base64 is in RFC 4648 and in nearly every language's standard library; basE91 is one author's design, defined by a reference implementation dated 2000–2006. Choosing it means both ends must agree to use it, which is rarely worth an 8% saving.
- Is the output URL-safe?
- No. The alphabet includes
+,&,=,#,?and/, all of which are reserved in a URL, and percent-encoding them makes the result longer than Base64URL would have been. Use Base64URL for anything that goes in a URL. - Why is the output length variable?
- Because a pair of characters carries 13 bits or 14, depending on the data: the encoder takes the 14th bit whenever the 13 it holds make a value of 88 or less. Random data does that about once in 92 pairs, so its overhead stays near 23%; zero bytes do it every time, which brings the overhead down to 14%.
- Is basE91 a standard?
- No. It is Joachim Henke's format, defined by his reference implementation rather than by an RFC. That is a real constraint: unlike Base64, you cannot assume the other end can decode it without shipping a matching implementation.
- What happens to line breaks and other characters outside the alphabet?
- They are skipped, as the reference decoder skips them, so wrapped lines decode as one. Spaces and line breaks pass silently. Anything else is counted and listed with its code point, because a character that stood in for one of the original's, such as a curly quote for a straight one, garbles everything after it.
Common errors and gotchas
- Assuming the output is URL-safe. The alphabet includes characters that must be percent-encoded, which erases the size advantage.
- Expecting a fixed expansion ratio. The encoding is variable-length, so output size depends on the input bytes themselves.
- Pasting the output between double quotes unescaped. The double quote is in the alphabet, so JSON and most languages need a backslash before each one.
- Choosing it for interoperability. Base64 is supported everywhere and Base91 is supported almost nowhere.
- Overestimating the gain. On compressed or random data the output is only about 8% shorter than Base64's, which rarely justifies the compatibility cost.
- Decoding a string that went through a word processor. Curly quotes and the ellipsis character replace characters of the alphabet, and everything after the first one decodes wrong.