Text can contain characters that take up no space and display as nothing at all. Most are harmless leftovers from copying between programs. Some are not: bidirectional controls can make a line of code read differently to a person than to a compiler, and Unicode tag characters can carry a whole hidden message inside text that looks perfectly ordinary. Paste anything above to see what is actually in it.
Why text from a chatbot often contains them
A wave of interest in this began in 2025 when people noticed unusual characters in output from language models โ most often the narrow no-break space, U+202F, sitting where an ordinary space belonged. The popular theory was a deliberate watermark.
The likelier explanation is far duller: these are tokenisation artefacts. Models are trained on text scraped from the web, which is full of typographic spaces from publishing systems, and they reproduce what they were trained on. There is no evidence of a hidden signature, and any such scheme would be trivially defeated by exactly this page.
The practical problem is real regardless of cause. Paste that text into code and a string comparison fails. Paste it into a CSV and a column splits wrongly. Search for the word and the search misses it, because the thing you typed and the thing on screen are different sequences of bytes.
The two kinds that actually matter
Bidirectional controls (U+202AโU+202E, U+2066โU+2069) exist so Arabic and Hebrew can be mixed with Latin text. They also let text be displayed in an order different from how it is stored. In source code this is the basis of the Trojan Sourceclass of attacks: a comment that appears to end where it does not, so a reviewer reads one program and the compiler builds another.
Unicode tag characters (U+E0000โU+E007F) are the more interesting case. They render as nothing in every font, and an entire ASCII message can be encoded in them. Text that reads as one harmless sentence to a person can therefore carry a completely different set of instructions to a language model reading the same string โ the mechanism behind a good deal of prompt-injection work.
These are flagged separately here because they are the only categories where the question is not tidiness but where the text came from.
Not everything invisible is unwanted
Two characters are removed only if you ask. The zero-width joiner (U+200D) is what turns separate emoji into one: a family emoji is several people joined by it, and stripping it produces three separate figures instead. The zero-width non-joiner (U+200C) is genuinely part of correct spelling in Persian, and matters in several Indic scripts.
Tools that strip everything invisible corrupt this text silently. The joiner option above is on by default for that reason.
Why counting characters is harder than it looks
JavaScript strings are sequences of 16-bit units, and anything above U+FFFF โ including every tag character โ occupies two of them. Code that walks a string one unit at a time sees two meaningless halves rather than one character, and will both miscount and mis-detect. This page counts by code point, which is why the character count matches what you would count yourself.
Related tools
User agent and browser details for what your browser reveals, tracking status for what sites can see, and the connection report for everything at once.
Privacy note
The text is examined in your browser as you type. It is never uploaded, never logged and never stored โ there is no server involved in the checking at all, which matters given people paste drafts, code and private messages into a page like this. Close the tab and nothing remains.