How Character Encoding and Unicode Actually Work
From Baudot telegraph tape and the 7-bit ASCII standard to code page mojibake and the universal UTF-8 byte scheme
“How did human alphabets, scripts, and emojis turn into binary numbers, and how did UTF-8 unite global digital text?”
Computers do not store letters, accents, or symbols; they store only binary numbers. In the early decades of computing, digitizing text was an anarchic patchwork of regional hacks. Telegraph Baudot code evolved into 7-bit ASCII, which could encode only the Latin alphabet. As computing went global, manufacturers created hundreds of conflicting code pages where the exact same byte represented completely different letters in different countries, producing rampant textual corruption known as 'mojibake'. The Unicode Consortium solved this by mapping every character in every human script—from ancient Egyptian hieroglyphs to modern Devanagari and emojis—to a unique abstract integer called a code point. In 1992, Ken Thompson and Rob Pike designed UTF-8: a brilliant, variable-length byte format that remained 100% backward-compatible with ASCII while universalizing all human language across the internet.
To understand the failure modes and edge cases detailed in this piece, we recommend familiarizing yourself with these foundational mechanisms first:
The Illusion of the Alphabet on Screen
In How the Alphabet Was Invented, we traced how ancient Semitic miners in Sinai transformed hundreds of Egyptian hieroglyphs into 22 phonetic consonants, making it possible for any spoken word to be captured with a tiny set of symbols.
For nearly four thousand years, the physical medium of the alphabet was physical: carved in stone, pressed in wet clay, penned on papyrus, or stamped with lead type onto paper.
Then, in the mid-twentieth century, human civilization made a momentous technological transition: We began storing human thought inside electronic computers.
As we saw in How Binary and Logic Gates Became Computation, digital computers have no eyes, no hands, and no understanding of vowels or letters. A computer is an array of electronic switches. It can store only one thing: voltages representing the numbers 0 and 1.
When you type the letter A on your keyboard, no miniature image of an A is sent to your computer's memory. Instead, a physical electrical circuit produces a number:
$$\text{'A'} \quad \longrightarrow \quad 01000001_2 \quad (\text{Decimal } 65)$$
That mapping—from a human symbol to a binary number—is called a Character Encoding.
Today, you can copy text containing English sentences, Arabic calligraphy, Japanese kanji, Devanagari script, mathematical formulas, and whimsical emojis, paste it into an email, and send it to a phone in Tokyo or Nairobi. It arrives looking identical.
This universal harmony seems obvious, but it is one of the newest and most hard-won engineering triumphs in human history.
For the first forty years of computing, text was a fragmented nightmare of incompatible regional standards where sending an email across an international border frequently turned the message into unintelligible digital garbage.
Here is how humanity solved the digital Tower of Babel.
The Origin: Baudot and 7-Bit ASCII
The earliest digital character encoding did not begin with computers. It began with the mid-nineteenth-century teleprinter: the Baudot Code (invented by Émile Baudot in 1870).
Baudot used paper tape punched with 5 holes per row:
BAUDOT 5-BIT TELEGRAPH PUNCH TAPE
[ • • ○ • ○ ] ──► Represents a single letter
With 5 bits, you can represent $2^5 = 32$ possible combinations. That was barely enough for the 26 letters of the English alphabet, with six codes left over for spaces and line feeds. It could not support punctuation, lowercase letters, or numbers without clumsy "shift" codes.
In 1963, the American Standards Association (led by Bob Bemer) published ASCII (American Standard Code for Information Interchange).
ASCII was built on a 7-bit architecture, allowing $2^7 = 128$ distinct numerical values (0 to 127):
THE CLASSIC 7-BIT ASCII MATRIX (0–127)
Hex 0 1 2 3 4 5 6 7 8 9 A B C D E F
┌───┬───────────────────────────────────────────────────────────────┐
│ 0 │ NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI │ (Control)
│ 1 │ DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US │ (Control)
│ 2 │ SPC ! " # $ % & ' ( ) * + , - . / │ (Symbols)
│ 3 │ 0 1 2 3 4 5 6 7 8 9 : ; < = > ? │ (Digits)
│ 4 │ @ A B C D E F G H I J K L M N O │ (Uppercase)
│ 5 │ P Q R S T U V W X Y Z [ \ ] ^ _ │ (Uppercase)
│ 6 │ ` a b c d e f g h i j k l m n o │ (Lowercase)
│ 7 │ p q r s t u v w x y z { | } ~ DEL│ (Lowercase)
└───┴───────────────────────────────────────────────────────────────┘
ASCII was brilliantly engineered:
- Values 0 to 31 were invisible control characters used to operate physical teletype printers:
LF(Line Feed: advance paper),CR(Carriage Return: move typing head to left margin),BEL(physically ring a mechanical brass bell to alert the operator). - Lowercase letters differed from their uppercase counterparts by exactly one bit in the 6th position (
A=01000001,a=01100001), making case-insensitive software comparisons a trivial one-cycle bitwise operation.
ASCII became the bedrock of modern software, C compilers, and the early ARPANET.
There was only one catastrophic problem: ASCII was completely, unapologetically American.
It contained English letters, digits, and dollar signs. It had no German umlauts (ä, ö), no French accents (é, ç), no Spanish tildes (ñ), no Scandinavian vowels (ø, å), and zero support for Arabic, Cyrillic, Greek, Hebrew, Hindi, Chinese, or Japanese.
It assumed the entire world spoke unaccented mid-century American English.
The Chaos: "Extended ASCII" and Code Pages
By the late 1970s, computers standardized on the 8-bit byte ($1\text{ byte} = 8\text{ bits}$).
An 8-bit byte can represent $2^8 = 256$ values.
Because ASCII only used 7 bits (values 0 to 127), the highest 8th bit was unused. This left 128 empty slots (values 128 to 255) waiting to be claimed.
Instead of collaborating on a single international system, every hardware company and country grabbed those 128 slots and filled them with whatever symbols they needed locally. These regional mappings were called Code Pages.
THE CODE PAGE BABEL (BYTE VALUE 0xC4)
In ISO-8859-1 (Western Europe): 0xC4 = Ä (Latin Capital A with diaeresis)
In ISO-8859-5 (Cyrillic): 0xC4 = Д (Cyrillic Capital De)
In ISO-8859-7 (Greek): 0xC4 = Δ (Greek Capital Delta)
In IBM Code Page 437 (DOS): 0xC4 = ─ (Horizontal box-drawing line)
In Windows-1256 (Arabic): 0xC4 = ؤ (Arabic letter Waw with hamza)
Look at the table above. What did the raw byte 11000100 (0xC4) mean?
- If you were in Berlin reading DOS, it was a horizontal line to draw borders around a window.
- If you were in Paris running Windows, it was the German letter
Ä. - If you were in Moscow, it was the Russian letter
Д. - If you were in Athens, it was the Greek letter
Δ.
The byte itself had no intrinsic meaning. It could only be deciphered if both the sender and the receiver agreed in advance on which hidden code page was in effect.
Mojibake: The World Collapses
When files crossed borders via the early internet, software guessed the wrong code page.
The result was an epidemic of incomprehensible garbled text:
Original German Text: Über die Brücke fahren
Rendered on Greek PC: Ϊber die Brόcke fahren
Rendered on Russian PC: бber die BrЎcke fahren
The Japanese coined a word for this widespread technological disease: Mojibake (文字化け, literally "character transformation" or "ghost characters").
Even worse, Asian writing systems (Chinese, Japanese, Korean—collectively CJK) use tens of thousands of unique characters. They could not possibly fit into 128 empty slots.
Companies invented complex multi-byte hacks (Shift-JIS, Big5, EUC-KR) where some bytes acted as "escape switches" that told the computer to read the next byte as part of a double-byte pair. Software became bloated with hundreds of brittle encoding translation tables.
The digital world was fracturing into unbridgeable linguistic silos.
The Master Solution: The Unicode Standard
In 1987, engineers from Xerox (Joe Becker and Lee Collins) and Apple (Mark Davis) met to formulate an audacious vision:
What if we assigned a single, permanent, unambiguous number to every character in every language on Earth?
This project became the Unicode Consortium, founded in 1991.
Unicode established a crucial intellectual distinction that cleared away forty years of confusion: Separating the abstract character from its storage format.
THE FOUR LAYERS OF DIGITAL TYPOGRAPHY
1. GLYPH (Visual Art) A A 𝑨 𝐀 (Rendering font)
│
2. CHARACTER (Semantics) "LATIN CAPITAL LETTER A" (Human meaning)
│
3. CODE POINT (Abstract #) U+0041 (Universal ID)
│
4. ENCODED BYTES (Storage) [ 0x41 ] (Hardware silicon)
- A Glyph is the visual shape drawn on a screen (calligraphy, serif, sans-serif, bold). Unicode does not care about fonts or visual style.
- A Character is the abstract grammatical atom of human writing (e.g., "Latin Capital Letter A", "Devanagari Letter Ka", "Smiling Face with Sunglasses").
- A Code Point is a permanent, immutable integer assigned to that character, written in hexadecimal notation:
U+0041=A(Latin Letter A)U+0915=क(Devanagari Letter Ka)U+4E16=世(Chinese Hanzi: "World")U+1F600=😀(Grinning Face)
The Unicode Code Space contains 1,114,112 possible code points, spanning from U+0000 to U+10FFFF.
This massive numeric space is divided into 17 Planes of 65,536 code points each:
- Plane 0: The Basic Multilingual Plane (BMP): Contains virtually all characters used in modern human languages (English, Chinese, Arabic, Hindi, Cyrillic, Hebrew).
- Plane 1: The Supplementary Multilingual Plane (SMP): Historic ancient scripts (Egyptian Hieroglyphs, Linear B, Cuneiform wedges), musical notation, and emojis.
- Plane 2: The Supplementary Ideographic Plane (SIP): Rare and historical CJK Han characters.
Once Unicode assigned U+0915 to the Devanagari character क, that number belonged to क forever. It will never mean anything else on any computer in the universe.
The Encoding Crisis: UTF-32, UTF-16, and the Diners' Breakthrough
Having a list of numbers is not enough. You must store those numbers on disk and transmit them across network wires.
How do you turn a code point like U+1F600 (which is the integer $128,512$) into bytes?
1. The Naive Solution: UTF-32
The simplest way is to give every code point a fixed 4-byte (32-bit) integer:
A(U+0041) $\to$00 00 00 41😀(U+1F600) $\to$00 01 F6 00
UTF-32 is mathematically clean, but practically catastrophic.
Every single English text document, HTML file, or source code file instantly quadruples in size. A 10-megabyte text file becomes 40 megabytes, filled with 75% useless zeros. Operating systems and networks flatly rejected it.
2. The Flawed Compromise: UTF-16
In the early 1990s, Unicode believed all living languages would fit into 16 bits (65,536 characters). Windows NT, Java, and JavaScript bet heavily on UTF-16, allocating a fixed 2 bytes per character.
Then Unicode expanded to over 100,000 characters. UTF-16 was trapped. It had to invent clumsy Surrogate Pairs—two 16-bit words glued together to represent characters above U+FFFF—inheriting the worst of both worlds: it was neither fixed-width nor compact.
3. The Miracle: UTF-8
In September 1992, Unix creators Ken Thompson and Rob Pike were sitting in a diner in New Jersey with a placemat. They needed an encoding for their new operating system (Plan 9) that fulfilled three impossible constraints:
- It had to be 100% backward-compatible with 7-bit ASCII. Any existing ASCII file had to be a completely valid Unicode file without changing a single byte.
- It had to encode any of the 1.1 million Unicode code points.
- It had to be self-synchronizing: if a byte was corrupted or lost over a noisy network, the decoder had to recover on the very next character, never cascading corruption across the rest of the document.
That night on the diner placemat, they sketched UTF-8.
THE UTF-8 VARIABLE-LENGTH ENCODING MATRIX
Code Point Range Byte 1 Byte 2 Byte 3 Byte 4
──────────────────────────────────────────────────────────────────────────
U+0000 .. U+007F 0xxxxxxx (ASCII: 1 byte)
U+0080 .. U+07FF 110xxxxx 10xxxxxx (2 bytes)
U+0800 .. U+FFFF 1110xxxx 10xxxxxx 10xxxxxx (3 bytes)
U+10000 .. U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (4 bytes)
The diagram below traces the full pipeline from spoken language script down to physical UTF-8 byte transmission:
Look at the Genius of the Prefix Bits
Look at the leading bits of each byte in the UTF-8 table above:
-
1-Byte Characters (ASCII): The first bit is always
0.0xxxxxxxleaves 7 bits for data. That is the exact binary range of original 7-bit ASCII! If a file contains pure English text, UTF-8 uses exactly 1 byte per character, identical to ASCII. Every ASCII file in existence from 1963 is automatically a valid UTF-8 file. -
Multi-Byte Characters (2, 3, or 4 Bytes): How does a computer reading a stream of bytes know how long a character is? Count the number of leading
1s in the very first byte:- Starts with
110$\to$ This character is 2 bytes long. - Starts with
1110$\to$ This character is 3 bytes long. - Starts with
11110$\to$ This character is 4 bytes long.
- Starts with
-
Continuation Bytes: Every subsequent byte in a multi-byte sequence starts with the exact same prefix:
10(10xxxxxx).
Why Self-Synchronization Matters
In older encodings, if a transmission glitch dropped a single byte midway through a file, every subsequent character was thrown out of phase: the rest of the book became gibberish.
In UTF-8, that is mathematically impossible.
If you jump into the exact middle of a random UTF-8 data stream:
- If you see a byte starting with
10xxxxxx, you know instantly: "I am in the middle of a character; skip forward." - If you see
0xxxxxxx, you are at an ASCII character. - If you see
110xxxxx,1110xxxx, or11110xxx, you are at the clean start of a new character.
The decoder synchronizes itself within a maximum of 3 bytes.
The Modern Complexity: Grapheme Clusters and Emojis
With UTF-8, human text achieved universal storage. But human language is messy, and modern typography introduced a new layer of complexity: The Grapheme Cluster.
A user thinks of a "character" as a single visual unit on screen. But in Unicode, a single on-screen character can be composed of multiple distinct code points glued together.
Example: Combining Diacritics
The letter é can be represented in two completely different ways:
- Precomposed: Code Point
U+00E9(LATIN SMALL LETTER E WITH ACUTE). - Decomposed: Two code points:
U+0065(e) followed byU+0301(COMBINING ACUTE ACCENT).
Both render identically on screen as é. To prevent search engines and databases from treating them as different words, Unicode defines Normalization Forms (such as NFC and NFD) to mathematically canonicalize strings before hashing or comparing them.
The Emoji Explosion: Zero-Width Joiners (ZWJ)
The most extreme example of grapheme clustering is modern emojis.
Consider the emoji for an astronaut:
$$\text{🧑🚀} \quad (\text{Astronaut})$$
In Unicode, there is no single "astronaut" code point. It is composed of three code points joined by a special invisible character called the Zero-Width Joiner (ZWJ, U+200D):
[ U+1F9D1 (Adult) ] + [ U+200D (ZWJ) ] + [ U+1F680 (Rocket) ]
When a font engine renders this sequence, it sees the ZWJ ligature request and collapses both symbols into a single on-screen glyph: an astronaut!
A family emoji with a mother, father, daughter, and son:
$$\text{👨👩👧👦}$$
is actually seven distinct Unicode code points joined by three ZWJ characters:
[Man] + [ZWJ] + [Woman] + [ZWJ] + [Girl] + [ZWJ] + [Boy]
In UTF-8, this single visual "character" consumes 25 bytes of memory!
This is why modern programmers know:
- The length of a string in bytes $\neq$
- The length of a string in code points $\neq$
- The length of a string in visible grapheme clusters.
The Universal Archive of Human Thought
In 2008, UTF-8 overtook ASCII and legacy code pages to become the dominant encoding on the World Wide Web. Today, over 98% of all websites on Earth are encoded in UTF-8.
The journey of human text:
$$\text{Clay Tokens} ;\longrightarrow; \text{Cuneiform Wedges} ;\longrightarrow; \text{Sinai Alphabet} ;\longrightarrow; \text{Telegraph Tape} ;\longrightarrow; \text{ASCII} ;\longrightarrow; \text{UTF-8}$$
Across five thousand years, the goal of writing has never changed: to preserve human memory, contracts, laws, literature, and knowledge across space and time.
Intriguingly, humanity's invention of an abstract digital character set mapped onto physical bytes mirrors a much older encoding scheme discovered by nature: the 64-codon genetic code, where 3-letter nucleic acid triplet "code points" map deterministically onto 20 universal amino acid building blocks (explored in How Genes Build Proteins).
Through the mathematics of UTF-8 and the universal scope of Unicode, every script ever spoken or carved by humanity—from Sumerian grain receipts to Egyptian tomb tags, Sanskrit hymns, modern source code, and dancing emojis—now shares a single, immortal digital home in the silicon architectures of our world.
Where to Go From Here
Explore companion architectures or dive deeper into downstream mechanisms.
Verified Specifications & Architectural References
This explainer is grounded in primary-source engineering specifications, regulatory circulars, and standard documentation.
The Unicode Standard, Version 15.0
The official architectural specification defining the universal character code space, planes, canonical normalization algorithms, and bidirectional text rendering.
RFC 3629: UTF-8, a transformation format of ISO 10646
The definitive technical networking standard specifying UTF-8 bit distribution, prefix headers, sequence bounds, and security-critical overlong-form rejection rules.
The Secret History of Unicode
Historical narrative detailing the Xerox PARC and Apple origins of Unicode and the collaborative struggle to harmonize CJK ideographs and European alphabets.