The text inspector · eight detectors over one string

What is hidden
in the text.

Paste something and this desk reads it character by character. It reports the zero-width and format characters it finds and where they are; it decodes three of the channels people smuggle data through; it names the letters that are drawn like Latin and are not; it walks the bidirectional controls that can make what is displayed differ from what is stored; and it counts the typography, the whitespace and the normalisation, which are facts about an editor and nothing more.

Every figure on this page is counted from the text in front of it, every finding carries its offsets, and the diff shows the original with each removed character marked where it stood. Safe mode is on by default and keeps the invisible characters that are doing an ordinary job — emoji sequences, Arabic and Indic shaping, CJK variation selectors, bidirectional text — because each of those is deliberate and correct.

This desk finds characters, not authors. It does not say who or what wrote anything, it produces no score and no probability, and it never will.

8 detectors · 10 constructed specimens · offsets on every finding · JSON and Markdown reports · no key, no account, no upload · nothing typed here leaves the browser

The text

Paste it, drop it, or deal a specimen.

Plain text, Markdown, HTML source, JSON, CSV. Inspection runs as you type on anything short, and on a press for anything long. Nothing is uploaded: there is no request in this page that carries what you paste, and there never has been.

The text

waiting for text

Offsets everywhere on this page are code point indexes into the text above, counting from zero — the whitespace panel’s line:column pairs are the one exception, and they count from one · press /Ctrl+Enter to inspect

Files are read with FileReader, in this browser, up to 2 MB · accepted: .txt .md .html .json .csv · a file is never fetched, uploaded or stored

Nothing inspected yet. Nothing typed here leaves this browser.

The specimens

Ten short constructed specimens, built in this page’s own script from explicit code points, so that a reader can check exactly what each one carries. They are constructions, not captured samples of anybody’s writing.

The findings

Eight detectors, each one switchable.

Every detector reports what it found, how many, and at which offsets. A panel with an orange edge found something; that is a statement about characters, not a verdict about a document. Switching a detector off removes it from the page, from the counts and from both reports.

1 · Invisible and format characters

not run

This means these code points are present, this many times, at these offsets. It does not mean anybody put them there deliberately: word processors, chat clients, PDF exports and ordinary copy-and-paste all leave them behind.

2 · Hidden payloads

not run

Three channels: tag characters, the variation-selector byte channel, and runs of zero-width characters read as bits. All three are gated on printability, so a decoded string really is carried by the text; it does not follow that the text was written to carry it, and there is no standard for the bit channel, so the mapping assumed is named beside every decode. Tag characters are grouped into runs and each run is read on its own, and a run standing behind a U+1F3F4 black flag is a subdivision flag rather than a payload — it is shown, and it is not counted as a finding. The bit-channel scan reads the text itself and never what safe mode decided to keep, so no boundary in a decode is ever set by the cleaner rather than by the text.

3 · Homoglyphs and mixed script

not run

A word is reported only when it MIXES two alphabets and carries a known Latin lookalike anywhere in it — a spelling with no ordinary typographic reason, which is a statement about the spelling and not about the writer. A word written entirely in another script is never reported, because that is simply what writing in Russian, Greek or Armenian looks like. Neither are the Greek letters that stand for scientific quantities: a micrometre is an innocent reading, so of the lowercase Greek alphabet only omicron is kept — the one letter identical to its Latin twin, and never used as a symbol for exactly that reason. Rewriting the lookalikes is a separate, opt-in action in THE CLEANED TEXT, and it touches only the words listed here.

4 · Bidi control abuse

not run

Embeddings, overrides and isolates change the order characters are drawn in, not the order they are stored in, and that gap is the mechanism behind the attack named Trojan Source: source code can read one way to a reviewer and compile another. Their presence is a fact about the text, not a finding of intent — these same controls are how Arabic and Hebrew are set alongside Latin. Unbalanced ones are the part worth looking at, and so is crossed nesting: a closer that reaches past something still open, which is the malformed case UAX #9 exists to describe and which different renderers resolve differently. The panel lights for those two things only — a correctly closed pair is counted and named, not flagged. Every bidi control this page prints anywhere is drawn as a marker rather than as itself, because rendered as itself it would reorder the panel reporting it.

5 · Typographic fingerprint

not run

Counts and nothing else. Thirty tracked forms — eleven quotation, eight dashes, ten spaces and the ellipsis: curly against straight quotes, the eight dash forms, the ellipsis character against runs of three or more periods, and the fixed-width and non-breaking spaces. This is a fingerprint of a TEXT EDITOR, not of an author: it tells you which tool applied which substitutions, and no combination of these figures says anything about who or what wrote the words. This panel never reports a finding, because there is nothing here to find.

6 · Whitespace

not run

Trailing spaces, runs of two or more spaces, tabs against spaces, CRLF against LF, and whether the text ends with a newline. It means the file passed through tools that disagree about whitespace, which is most files. It means nothing about where the text came from, and a tidy file is not a suspicious one.

7 · Normalisation

not run

Whether the text is already NFC, and which characters change under NFC and under NFKC — fullwidth forms, ligatures, styled alphabets. It means the text is or is not in a canonical form. It does not mean anything is wrong: a great deal of perfectly ordinary text is not normalised and does not need to be. Normalising to NFC is a separate, opt-in action in THE CLEANED TEXT.

8 · Hidden in the HTML

not run

Runs only when the input is being read as HTML. It reports text a reader cannot see — hidden by an inline style or by the hidden or aria-hidden attribute — along with HTML comments and long data- attributes, and it prints what the hidden text says, because that is the whole point of looking. Hidden text has entirely ordinary uses: screen-reader labels, print-only blocks, templates and analytics markers. Finding some is a fact about the markup.

The diff

Where the text was carrying something.

Each marker stands exactly where its character stood, and its title carries the code point, the character’s name and its offset. Long inputs are capped rather than turned into a hundred thousand elements, and the panel says when it has capped them.

The diff

The cleaned text

What comes out, and the report that goes with it.

The cleaned text is the input with the characters detector one reports as removable taken out — safe mode’s keeps left in place. In HTML mode only text nodes are rewritten, and anything inside a script, a style, a noscript or a textarea is left alone entirely, however deeply inside one it sits. When nothing is removed, the output is your input returned unchanged rather than a reserialised copy of it.

Output

Two rewrites that are never done by default

Both are off unless you turn them on, both are recorded in the report, and the lookalike rewrite touches only the mixed-script words that detector three listed — never every Cyrillic letter in a document.

The cleaned file takes the extension its input deserves — .txt, .md, .html, .json or .csv. A file saved as .html is still the document you pasted: the cleaner rewrites text nodes and nothing else, so its scripts, stylesheets and remote references are exactly as they were, and opening it will run them. Both reports carry the detector list, the counts, the offsets, the decoded payloads, the options you set and a timestamp.

The bookmarklet

Strip the page you are already reading.

Drag the button to your bookmarks bar. On any page, pressing it walks the text nodes, text input values, titles, open shadow roots, template contents and same-origin frames, and removes the invisible carriers in place. It keeps ideographic variation selectors after Han characters. It writes a field through the value property’s own setter and fires input and change afterwards, so a framework that keeps its own copy of the value sees the edit. Three things it will not touch: file inputs, password fields, and rich-text editors — a contenteditable backed by a model cannot have its DOM rewritten underneath it without corrupting the model and destroying the undo history, so those are counted and left alone. It says how much it changed and nothing else: it is the stripper without the inspector.

Strip Unicode watermarks

On a phone, or in a browser that will not accept a drag: copy the URL, bookmark any page, then edit that bookmark and paste the javascript: URL over its address. Some browsers block bookmarklets outright; a userscript is the durable fallback. The bookmarklet runs entirely in the page you press it on and sends nothing anywhere.

About

An inspector, not an accusation.

This desk finds characters, not authors. It reports exactly what is in the text in front of it — these code points, at these offsets, this many times — and it stops there. It does not say that a document was written by a machine, it produces no score and no probability, and it will not be extended to do so. There is no reliable path from a non-breaking space to a conclusion about a person, and a tool that pretends otherwise is a tool for accusing people wrongly.

Invisible characters have ordinary, legitimate uses. A zero-width joiner is what makes one emoji out of three, and what spells out a subdivision flag behind a black one; a zero-width non-joiner is what keeps Persian and Hindi words correctly shaped, and in every Indic script it does that work after the virama, which is a combining mark rather than a letter; an ideographic variation selector is how a specific written form of a Han character is named; the bidirectional controls are how Arabic and Hebrew are set alongside Latin without one of them coming out backwards. Every one of those is invisible, deliberate and correct. Safe mode exists because of them, it is on by default, and it names each character it kept and why.

A document with none of these is not proven to be anything. An empty finding is an empty finding. Plain ASCII with straight quotes and LF line endings is what a plain text editor produces, and what a great many people and programs produce. Absence of evidence is the ordinary state of most text, and nothing on this page treats it as a result.

Nothing typed here leaves the browser. There is no fetch, no upload, no beacon and no analytics call carrying content anywhere in this page’s scripts. Files are read with FileReader, locally; the cleaned text and the two reports are built in memory and handed to you through a Blob and an object URL that is revoked afterwards. The site’s ordinary page-view tag is present, as it is on every page here, and it reports that this page was opened — never a character of what you put in it.

What the detectors cannot do is stated in each panel rather than hidden here. The zero-width bit channel has no standard, so the mapping assumed is printed beside every decode. The HTML detector reads inline styles and attributes only, so text hidden by a stylesheet rule is not found. Statistical watermarks in generated text, image metadata and C2PA provenance are all outside what this reads. And inspection runs on up to 500,000 characters in one pass, because every detector walks the text character by character; past that the page says so and offers the first slice rather than locking up.