Unicode Character Inspector — Codepoints, Bytes, Invisible-Character Detector & Normalization Diff

Paste anything. unicodex breaks it into grapheme clusters, codepoints and UTF-8/UTF-16 bytes, diffs its four normalization forms, and reveals every ZWSP invisible or deceptive character — with the browser engine itself as ground truth, so "why is my string length wrong?" finally gets a straight answer.

Private by construction: this page ships a Content-Security-Policy of connect-src 'none' — the browser itself blocks any network request. Nothing you paste ever leaves this browser; that is enforced, not promised.

Inspect text

Samples:

0 grapheme clusters what you see · Intl.Segmenter · cursor steps
0 UTF-16 units JS .length · Java/C# length · DOM APIs
0 codepoints Python 3 len() · Rust chars().count()
0 UTF-8 bytes on the wire · Go len() · DB storage/limits

Cluster by cluster

Each card is one grapheme cluster — one "character" as you see it. Flagged characters appear as dashed reveal boxes. Select a card for bytes, class chips and copy-as escapes.

Flagged characters

Scanned against a hand-verified catalog of ~70 risky characters (zero-width, bidi/Trojan Source, look-alike spaces, line separators, confusables). Every entry cites the Unicode document that defines it. Absence of a warning is not a "safe" verdict — the confusable list is a curated subset of UTS #39.

Normalization diff — NFC · NFD · NFKC · NFKD

Computed by this browser's String.normalize (UAX #15). "changed" means the form differs from your input; unstable clusters are marked.

Compare two strings

For "these look identical but don't match" bugs: raw equality, NFC equality, the first divergent codepoint, and any flagged characters that explain a visual match that isn't a real match.

Clean copy

Mechanical, per-category fixes only — each toggle states exactly what it replaces, every pass is idempotent by test, and nothing here guesses intent or repairs encoding damage (mojibake).

Honest limits