Invisible Character Detector
Reveal and strip zero-width, bidi and other hidden characters — code point, name and position for every hit, entirely on this device.
Paste or type text to scan it for hidden characters.
The per-character report appears here after you paste text.
The JSON report appears here after you paste text.
Press ? for shortcuts
Do it offline:$ rg -nP '[\x{061C}\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{2069}\x{FEFF}\x{00AD}\x{180E}]' file.txt
How to use it
- Paste or type your text, or open a file with the picker; it is read inside this tab and never sent to a server.
- Read the report — a per-class tally, then one row per hidden character with its code point, name, class and line:column position.
- Switch to Clean and choose the classes to strip; the defaults take bidi, zero-width, soft hyphens and controls, while joiner, exotic spaces and other formats wait until you enable them.
- Copy the cleaned text or download the JSON report; a credential-shaped input stays out of the shareable link unless you confirm it.
Some characters take up no space yet change everything: a zero-width space can split an identifier, and a bidirectional override can reorder a line of source without altering a single byte. This page scans text for hidden, zero-width, control and bidirectional code points, reports each one with its code point, Unicode name, class and line-and-column position, then strips the classes you choose. Nothing you paste is uploaded.
How it works
The scanner walks your text one code point at a time, tracking a UTF-16 offset with a one-based line and column, so every hit is placed where it actually sits; a character above the basic plane spans two code units, and a CRLF pair counts as one line break.
Bidirectional controls get extra scrutiny. Under the Unicode Bidirectional Algorithm (UAX #9), embeddings, overrides and isolates open a writing direction that a matching pop should close; when the opening and closing counts do not balance, the page raises the unbalanced advisory — the shape behind Trojan Source (CVE-2021-42574), where source is made to render in an order the compiler never sees.
These advisories are signals, not verdicts: a flag means look closer, and a clean result is not proof the text is safe, only that these code points are absent. Names come from a curated table; anything outside it is still placed and classified, with its full name a click away in the Unicode Inspector.
Examples
if access != "user // admin " { grant() }4 hidden characters · bidi ×4
1:19 U+202E bidi RIGHT-TO-LEFT OVERRIDE
1:21 U+2066 bidi LEFT-TO-RIGHT ISOLATE
1:30 U+2069 bidi POP DIRECTIONAL ISOLATE
1:32 U+2066 bidi LEFT-TO-RIGHT ISOLATE
flag Bidirectional control characters are present — they can reorder how text displays without changing the underlying bytes (Trojan Source, CVE-2021-42574).
flag Bidirectional embedding/isolate markers are unbalanced — opening and closing counts differ, the pattern the Trojan Source advisory flags. Review the marked positions.The right-to-left override U+202E and the isolates around it make admin render as if commented out, though the bytes are unchanged; the markers do not balance, so the unbalanced advisory fires.
admin=true1 hidden character · zero-width ×1
1:6 U+200B zero-width ZERO WIDTH SPACEA zero-width space U+200B sits at column 6, between admin and the equals sign, so the token no longer matches a plain admin though the two look identical.
👩💻1 hidden character · joiner ×1
1:3 U+200D joiner ZERO WIDTH JOINER
flag Zero-width joiner (U+200D) is present — it is often legitimate inside emoji sequences and is not removed unless the joiner class is stripped.
cleaned · 0 removed
👩💻The default set — bidi, zero-width, soft hyphen and control — leaves the joiner in place, so the emoji stays whole and nothing is removed. Turn the joiner class on only to split a sequence.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
A joiner can be part of an emoji
The zero-width joiner welds separate glyphs into one, as in the woman-technologist emoji. Detection lists it, but its class stays off in Clean by default, so a strip never quietly breaks a sequence.
A byte-order mark and a stray ZWNBSP are one code point
U+FEFF leading a file is a byte-order mark; the same code point mid-word is a zero-width no-break space. Both carry the name ZERO WIDTH NO-BREAK SPACE, and position alone tells a leading mark from a buried one.
Exotic spaces are found, not fixed
A non-breaking or ideographic space is flagged as exotic-space with its code point, but collapsing whitespace runs is a separate job for the whitespace cleaner.
Astral characters are measured in code units
A Unicode tag character lives above U+FFFF and takes two UTF-16 code units, so its offset and the next column advance by two — the count editors and string slicing use, not glyphs.
Combining accents are visible, so they pass
An e followed by a combining acute is a mark you can see, not a hidden control, so it is never flagged; folding accents is the accent remover's job.
Common errors
Input is larger than the 1 MB limit for this tool.- This is the only refusal, and it declines the whole input rather than scanning part of it, so no hit ever points at the wrong line. There is no parse-failure case — every other string, however strange, scans successfully. For anything larger, the ripgrep line below finds the common offenders in a file of any size.
FAQ
How is this different from an xn-- or punycode check?
A punycode or homograph check deals with visible letters that only look alike — a Cyrillic а posing as a Latin a. This page instead finds code points that render as nothing at all: zero-width spaces, joiners and bidi controls. The two are complementary — one hides in plain sight, the other completely.
Is a zero-width joiner always bad?
No. The joiner holds emoji sequences together and is required by several scripts, so it is legitimate far more often than not. It is reported but left in place unless you strip the joiner class on purpose, which would break any emoji that needs it.
Does my text leave the browser?
No. The scan, the counts and the cleaning all run in this tab with no upload and no account. Because pasted text is so often a password or private source, the safest inspector is one that never transmits it.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Scan the current text again, in Detect or Clean.
- ⌘/Ctrl + S
- Download the report as .json, or in Clean the cleaned text as .txt.
- ⌘/Ctrl + C
- With nothing selected, copy the report; a selection copies itself instead.
- ⌘/Ctrl + Shift + C
- Force-copy the report whether or not something is selected.
- t
- Cycle to the next theme.
- ? or ⌘/Ctrl + /
- Open the keyboard-shortcut help.
- Esc
- Dismiss the top overlay; focus returns to its trigger.