1 The layer confusion
One glyph is not one codepoint is not one byte
The question "how long is this string?" has four different, all-correct answers, depending on which layer you count. Here is the same family emoji, counted four ways.
len() · Rust chars()
.length · Java/C# length
The seven codepoints are four faces — 👨 👩 👧 👦 — glued by three invisible ZWJ joiners. Strip the joiners and the one glyph falls apart into four. The flag 🇮🇳 tells the same story: 1 glyph, 2 codepoints, 4 UTF-16 units.
2 The characters you can't see
Invisible characters, revealed
Some codepoints have no visible glyph at all. They copy-paste silently, break exact matching, and — in one nasty case — reorder text on screen without changing a single byte. unicodex's motif is the reveal box: a dashed mint frame that makes the invisible visible, its abbreviation set in monospace inside.
ZERO WIDTH SPACE U+200B
Zero pixels wide. "Hithere" looks like one word but is eight codepoints, and never equals "Hithere". It hides in copied IDs, URLs and code.
RIGHT-TO-LEFT OVERRIDE U+202E
Flips display order. The bytes read invoice_gnp.exe, but the screen shows invoice_exe.png — the Trojan Source class of trick, in filenames and source code alike.
NO-BREAK SPACE U+00A0
Looks exactly like a space, isn't one. Word-splitting and exact string matching both fail on it, and a leading BOM (U+FEFF) can make a parser reject valid JSON.
Paste any of these into unicodex and every flagged character is revealed inline in its own box — with its Unicode name, a one-line risk note and a citation to the document that defines it.
3 The same text, four ways
Normalization — NFC · NFD · NFKC · NFKD
Unicode lets the "same" text be encoded more than one way. Normalization collapses those to a single canonical form so comparisons work. There are two axes: canonical (NFC/NFD — same appearance) versus compatibility (NFKC/NFKD — same meaning, may change appearance).
These two look pixel-identical. One is é (a single codepoint); the other is e + a combining acute accent. They fail === — but normalize both to NFC and they're equal.
The single ligature codepoint fi (U+FB01) survives NFC untouched — but compatibility normalization decomposes it to plain f + i. Great for search; lossy for round-tripping.
unicodex shows all four forms of your input at once and marks which ones changed — so you can see, for any string, whether a normalization mismatch is what's breaking your comparison.
4 Letters that lie
Look-alikes — when a letter isn't the letter
Different alphabets contain characters drawn the same. Swap a couple in and you get a word that reads perfectly but is, byte-for-byte, something else entirely — the basis of homograph phishing domains.
The first two letters are Cyrillic er (U+0440) and a (U+0430) — genuinely different letters, not a normalization difference. No amount of normalizing will ever make them match the Latin word, which is exactly why the flag matters. CONFUSABLE
5 The straight answer
So, why is my string length wrong?
It isn't wrong. Every layer is telling the truth about a different thing — and the tool you're using picked one for you. Here's who counts what, on that one family emoji.
Four answers, all correct, none of them a bug. The fix is never to argue with the number — it's to know which number you have. unicodex lays all four in front of you for any text you paste, then shows the codepoints and bytes that produced them.
✓ Private by construction
The text you inspect never leaves your browser
You paste sensitive things into a character inspector — tokens, keys, private messages. Both this page and unicodex ship a policy that makes the browser itself refuse to send them anywhere.
default-src 'self'; connect-src 'none'
- No network calls are possible —
fetch, XHR and WebSockets are refused by the browser, by policy, not by promise. - No server, no account, no analytics — nothing to log in to, nothing watching.
- No external fonts, scripts or images — everything is same-origin or a
data:URI. - Local only — this explainer stores just your theme choice; unicodex keeps optional autosaved input in
localStorage, with one-tap clear.
! What this honestly is not
Honest limits
- Counts are the host engine's. Grapheme segmentation follows this browser's
Intl.Segmenterand its Unicode version — older engines can split Indic conjuncts differently. unicodex reports the engine's answer, not a universal truth. - Not a security verdict. The confusable and invisible-character catalogs are curated subsets — the absence of a flag is never proof a string is safe, and unicodex never says "safe".
- Rendering ≠ identity. A glyph shown here may draw as tofu or differently on your device fonts. This is about what text is, not how any screen draws it.
- Approachable, not exhaustive. This page teaches the mechanism with a handful of examples; Unicode is far larger. Use the live app to inspect your own real strings.
Paste your own string. Watch it come apart.
Free, offline, MIT-licensed. Every codepoint, byte and invisible character — revealed in your own browser, sent nowhere.
Open unicodex →