Reference FIELD NOTES ยท 4 MIN READ
Unicode and Code Points: Beyond ASCII
How Unicode numbers every character from U+0000 to U+10FFFF: planes and blocks, UTF-8 byte counts, combining marks, and why one emoji can count as two characters.
ASCII gave a number to 128 characters โ enough for English, not for the world. Unicode finishes the job: every character in every living and historic writing system, every symbol, and thousands of emoji each get their own number, called a code point. Once you can read code points, a lot of browser and programming oddities stop being mysterious and start being arithmetic.
From 128 characters to 1,114,112
ASCII assigns codes 0โ127: control characters at the bottom, then punctuation, digits, uppercase, lowercase (see How Text Encoding Works for the full story). It fits in 7 bits and covers exactly one script. Unicode's ambition is bigger: assign a unique code point to every character ever used. The address space runs from U+0000 to U+10FFFF โ that is 17 planes of 65,536 code points each, 1,114,112 slots in total. Not all are filled, and some are reserved, but the ceiling is fixed by the format itself.
The first plane, U+0000 to U+FFFF, is the Basic Multilingual Plane (BMP), and it holds most of what you read daily: Latin, Greek, Cyrillic, Arabic, Hebrew, the major Indian and Southeast Asian scripts, CJK ideographs, punctuation, currency symbols. Emoji and rare historic scripts live in the supplementary planes above U+FFFF โ the grinning face sits at U+1F600 in the Emoticons block.
Reading a code point
The U+ prefix marks a hexadecimal number, so U+1F600 is decimal 128512. A few landmarks show how the space is organized:
A = U+0041 (decimal 65) LATIN CAPITAL LETTER A Basic Latin
รฉ = U+00E9 (decimal 233) LATIN SMALL LETTER E WITH ACUTE Latin-1 Supp.
โฌ = U+20AC (decimal 8364) EURO SIGN Currency Symbols
๐ = U+1F600 (decimal 128512) GRINNING FACE Emoticons
Each code point also carries an official name and belongs to a named block โ a contiguous range reserved for a script or purpose. These metadata fields are what make a lookup tool useful: they turn "some weird box" in your terminal into a precise, searchable identity. (For how hexadecimal works, see Number Base Systems Explained.)
Code points are not bytes: UTF-8
A code point is a number, not a storage format; encodings turn numbers into bytes. UTF-8, the web's standard, uses 1 byte for code points up to U+007F (that is why ASCII text is valid UTF-8 unchanged), 2 bytes up to U+07FF, 3 bytes up to U+FFFF, and 4 bytes beyond. Count the difference on two words:
cafรฉ : 4 code points โ 5 UTF-8 bytes (รฉ costs 2 bytes)
๐ : 1 code point โ 4 UTF-8 bytes
This is why "length" has three honest answers: a string can be 4 characters, 4 code points, and 5 bytes at once. Databases measure bytes, editors measure glyphs, and specs often mean code points โ which answer a tool gives you matters.
Code points are not what you see
Unicode also splits one visible character into multiple code points when it is useful. Accented letters exist as single points โ รฉ = U+00E9 โ but also as a base letter plus a combining mark: e (U+0065) followed by U+0301 (COMBINING ACUTE ACCENT). Both render as รฉ; the first is 1 code point and 2 UTF-8 bytes, the second is 2 code points and 3 UTF-8 bytes. Same glyph, different bytes โ the two forms are canonically equivalent, and Unicode defines normalization forms (NFC composes, NFD decomposes) so comparisons can agree on one. The same logic extends to flag emoji and family emoji, which are sequences of several code points rendered as one image. A code point is what Unicode stores; a grapheme is what your eye sees, and they agree only most of the time.
Why one emoji can count as two characters
Here is the quirk that trips up real code. JavaScript strings predate Unicode's expansion, so they are sequences of UTF-16 code units โ 16-bit numbers. Code points above U+FFFF do not fit, so UTF-16 stores them as a surrogate pair: two units, one after the other. JavaScript's .length counts units, not points, so:
"๐".length โ 2 (one surrogate pair, ONE code point)
[..."๐"].length โ 1 (spread iterates code points)
"cafรฉ".length โ 4 (all below U+FFFF โ no surprise)
You can watch both counting models in one tool. The Character Counter reports ๐ as total 2, unique 1: the total follows .length (2 UTF-16 units) while the frequency table iterates code points and sees a single character. Neither number is wrong โ they are counting different layers, and now you know which is which. The same split explains why slice() can cut an emoji in half and why regexes need the u flag to match one whole emoji.
Try it yourself
Type รฉ, โฌ and ๐ into the Unicode Lookup and read back each code point, decimal value, official name, block and plane โ then paste ė (letter plus combining accent) and see it decompose into two code points. Browse the first 128 rows in the ASCII Table and notice they are exactly Unicode's U+0000โU+007F block: ASCII did not go away, it became the foundation floor. Finally, count cafรฉ and a lone emoji in the Character Counter and check the numbers against the tables in this article.