Unicode character inspector
Breaks text into its code points with names, categories, blocks and encodings, and flags invisible, bidi and lookalike characters.
What this tool does
Paste any text and it comes back broken into the individual characters it is really made of. Each one gets its Unicode name, its U+ number, what kind of character it is, which writing system and block it belongs to, the bytes it takes in UTF-8 and UTF-16, and the escape sequence it needs in six different formats.
Most people arrive here because something does not add up. A string is longer than it looks, two values that display identically refuse to compare equal, a username was rejected for characters that appear to be plain letters, or a line of source code reads differently from how it behaves. All four have the same shape: a character doing something the screen does not show.
Characters, code points and graphemes
The word "character" means at least three different things, and mixing them up is where most Unicode confusion starts. A code point is one value in the Unicode standard, written U+0041 or U+1F600. A grapheme is what a reader counts as one character. They are frequently not the same number.
- A plain "a" is 1 grapheme, 1 code point, 1 UTF-16 unit — everything agrees.
- U+00E9, the precomposed e-acute, is 1 grapheme, 1 code point, 1 UTF-16 unit.
- U+0065 U+0301, the same letter written as e plus a combining accent, is 1 grapheme but 2 code points and 2 UTF-16 units.
- U+1F600, the grinning face, is 1 grapheme and 1 code point but 2 UTF-16 units — it lives above the BMP, so JavaScript stores it as a surrogate pair.
- The family emoji is 1 grapheme, 5 code points and 8 UTF-16 units: three people joined by two invisible joiners.
The family emoji is the case that catches people out. It looks like one thing, JavaScript reports its length as 8, and a database column of 5 characters will not hold it. This tool groups code points under the grapheme they build, so both readings are visible at once instead of having to be reconciled in your head.
The characters you cannot see
A surprising number of Unicode characters draw nothing at all. They exist to control how the text around them behaves, and because they leave no mark they survive copying, pasting and review without anyone noticing. These are the ones the tool points at rather than merely listing:
- Zero-width space, joiner and non-joiner (U+200B, U+200D, U+200C) — no width, no mark, and they break every string comparison they touch.
- Soft hyphen (U+00AD) — a suggestion about where a word may break, invisible until it does.
- No-break space (U+00A0) — looks exactly like a space and is a different character, which is why it survives so long in configuration files.
- Byte order mark (U+FEFF) — often left at the start of a file, where it becomes an invisible first character of the first value.
- Bidi controls and overrides (U+202A to U+202E, U+2066 to U+2069) — they reorder the text around them.
The bidi controls deserve their own mention. They were added so that Hebrew and Arabic can be mixed with Latin text correctly, and they do that job well. But an override placed inside a comment or a string literal can make a line of source code display in an order completely different from the order the compiler reads — which means a reviewer can approve code that does something else entirely. That class of trick is known as Trojan Source, and it is why this tool flags every bidi control separately from the other invisible characters.
Lookalike characters and mixed scripts
Unicode contains many characters that are visually identical to ASCII letters and are not them. Cyrillic а, Greek ο and Latin a occupy different code points and render the same in most fonts. A domain or a username built out of them looks correct and points somewhere else.
Flagging every non-ASCII lookalike would be useless: it would paint every Russian, Greek or Hebrew word red, and a warning that fires on ordinary text is a warning people learn to skip. So the rule here is the one the Unicode security standard uses — a word is suspicious when the letters inside it come from more than one writing system:
- "paypal.com" written entirely in Latin letters: nothing to say about it.
- The same word with a Cyrillic er and a in front of Latin "ypal": one word, two writing systems, flagged.
- A whole Russian word in Cyrillic: one writing system, ordinary text, not flagged.
- A Japanese sentence using Han, Hiragana and Katakana together: three scripts, one writing system, not flagged.
That last case is the reason the rule needs care. Japanese is written with three scripts at once and Korean mixes two, so a naive "more than one script is suspicious" test would call every Japanese sentence an attack. The combinations that are genuinely one writing system are treated as one, which keeps the warning meaningful.
Normalisation, or why two identical strings differ
Some characters can be written more than one way. The letter é is either the single code point U+00E9 or the pair U+0065 U+0301 — a plain e followed by a combining acute accent. Both display identically. Neither is wrong. They are not equal.
Normalisation is the process of picking one spelling, and there are four forms of it. NFC composes wherever it can and is what the web assumes by default; NFD decomposes instead. The two K forms go further and also fold compatibility characters — the fullwidth A becomes a plain A, and the ligature fi becomes fi — which is useful for search and matching and lossy for storage.
The panel here shows all four forms of whatever you paste and marks which of them differ from the input. If none differ, the text is already normalised and the mismatch you are chasing is elsewhere. If some do, you have found it.
Names, categories, scripts and blocks
Every assigned character carries four pieces of identity, and they answer different questions. The name is the character itself. The general category says what kind of thing it is — a lowercase letter, a combining mark, a currency symbol, a format control. The script is the writing system it belongs to. The block is the range of code points it was assigned in, which is an organisational fact rather than a linguistic one.
Block and script are easy to confuse and often disagree. Latin letters live in at least half a dozen blocks, and a block can hold characters from several scripts. When you want to know "what alphabet is this", the script is the answer; when you want to know "where in the standard does this live", the block is.
Names, scripts and blocks are shown in English here in every language, deliberately. They are identifiers defined by the standard, not prose — U+200D is named ZERO WIDTH JOINER everywhere in the world, and translating it would make it impossible to search for.
Encodings and escapes
A code point is a number; it becomes bytes only once an encoding is chosen. UTF-8 uses between one and four bytes and keeps ASCII unchanged, which is why it won. UTF-16 uses one or two 16-bit units, and it is what JavaScript strings are made of — which is why .length reports 2 for a single emoji.
U+0041 41 # UTF-8: one byte, same as ASCII U+00E9 C3 A9 # UTF-8: two bytes U+20AC E2 82 AC # UTF-8: three bytes U+1F600 F0 9F 98 80 # UTF-8: four bytes
Each row here also gives the character written as an escape in JavaScript, JSON, HTML, CSS, URLs and Python, ready to copy. Those are not all the same, and the difference matters above the BMP: JavaScript and Python have a code-point escape and use it, while JSON has none and must spell an emoji as its two surrogate halves.
Hidden characters, and text that came out of an AI model
Much of the text people paste here was never typed by the person pasting it. It was copied out of a document, a web page, a ticket or an assistant’s answer, and the question they arrive with is whether it is carrying characters they cannot see. That is the question this tool answers, and it is worth saying exactly how far the answer goes: the text is broken into its code points, each with its name where the standard gives it one, and the ones worth a second look are counted in a line above the list. If that line is not there, nothing was found.
What a hidden character proves is narrower than most writing on the subject suggests. Nothing inside a code point records where it came from: one that arrived by copying out of a content management system and one that arrived with a generated answer are the same value. Invisible characters really have been reported in the output of large language models — in the case that drew the most attention, a narrow no-break space turning up where an ordinary space belonged, the model’s maker answered that it was a by-product of training and not anything put there on purpose, and the reports stopped within days. So a finding here tells you what to delete. It does not tell you what wrote the sentence.
- Spaces that are not the ordinary space. The no-break space (U+00A0) and the narrow no-break space (U+202F) are the two named most often; each is about the width of a space, each is a different character, and this tool flags both.
- Characters with no width at all. The zero-width space (U+200B), the word joiner (U+2060) and the byte order mark (U+FEFF) draw nothing, survive review, and break the first comparison they touch. They are flagged as invisible.
- The Tags block (U+E0000 to U+E007F). It holds one invisible code point for every printable ASCII character — U+E0041 is named TAG LATIN CAPITAL LETTER A — so a whole sentence can be spelled inside text that displays nothing at all. Each one is named here and flagged as invisible.
- The punctuation that gets blamed and is not hidden. The em dash (U+2014) and the curly apostrophe (U+2019) draw a visible mark, so nothing here flags them; they are ordinary typography whatever produced them.
Two reasons to run the check are worth keeping apart. The everyday one is that invisible characters break things quietly — a comparison that ought to match, a lookup key, a field that rejects your input without saying why. The other is that text is read by programs as well as by people: the characters in the Tags block were meant for language tags, they render as nothing in most editors and diffs, and something that reads text rather than looking at it reads them anyway. For a long answer pasted whole, note that the list of rows is trimmed and the count is not — it is taken over everything you pasted.
Frequently asked questions
- Why is my string longer than the number of characters I can see?
- Either it contains invisible characters, or it contains graphemes built from several code points. Both are shown here: invisible characters are flagged, and multi-code-point graphemes are grouped so you can see the one character and the several values it is made of.
- Two strings look identical but are not equal. Why?
- Usually normalisation. An accented letter can be one code point or a letter plus a combining mark, and both render the same. Paste each string here and compare the code point lists, or look at which normalisation forms differ from the input.
- What is a zero-width space and how did it get into my text?
- It is a character with no width and no mark, used to suggest a line-break opportunity. It usually arrives by copying text out of a web page, a document editor or a chat client, and because nothing displays, it survives every review until something compares strings and fails.
- What is the difference between a code point and a character?
- A code point is one Unicode value. A character in the everyday sense is a grapheme — what a reader counts as one — and a grapheme can be several code points. A family emoji is five code points and one grapheme.
- Why does the tool say some characters have no name?
- Four different reasons, and it says which. Surrogates and private-use code points have no name in the standard, unassigned ones have nothing to name, and a character in a rarely-inspected historic block has a name that this build does not carry — the name table keeps the blocks people actually paste from rather than shipping all of them.
- Is a mixed-script warning proof of an attack?
- No. It means a single word draws its letters from more than one writing system, which is the shape a spoofed name has and is also something ordinary text does occasionally. It is a reason to look, not a verdict.
- Is anything I paste sent to a server?
- No. The character database is downloaded with the page and everything runs in your browser; nothing you type is uploaded or logged, and it works with no network connection.
- Does text from an AI model contain invisible characters?
- Sometimes, and never in a way that identifies anything. The case that drew the most attention was a narrow no-break space (U+202F) appearing in long answers where an ordinary space belonged; the model’s maker answered that it was a by-product of training rather than anything put there on purpose, and it stopped being seen within days. Paste your text here and you will see what is in it. What you will not see is where it came from, because a code point does not record that.
- How do I find and remove hidden characters in my text?
- Paste it here to find them: each code point gets a row, with its name where it has one, and the ones worth a second look are counted above the list. Removing them is your editor’s job — nothing here rewrites your text — but once you know which code point you are chasing, each row gives it as an escape in six formats, which is what a search box that understands escapes wants.
- Is an em dash or a curly quote a hidden character?
- No. The em dash (U+2014) and the curly apostrophe (U+2019) both draw a mark on the screen, so nothing here flags them. They are frequently pointed at as a sign of generated writing, which is a question about style that a character inspector cannot answer. They are still worth knowing about for a different reason: neither is ASCII, so both can upset something that expects it, and a string written with them does not compare equal to the same string typed with a hyphen and an apostrophe.
Related tools
- URL slug generator
Turn any title into a clean URL slug.
- Case converter
Convert between camelCase, snake_case, Title Case and more.
- String length
Count characters, words and lines — Unicode aware.
- Regex tester
Test regular expressions — match, replace, split.