I built this after reading through guillaumemeyer/watermarks-remover, a Python project that does something I had not seen anyone else do: it separates the parts of watermark removal it can prove from the parts it cannot, and tells you which is which. That honesty is the actual feature. The rest of this is a browser port of it.
What is actually hiding in text
Unicode has around 150 codepoints that render as absolutely nothing. Zero-width spaces. Word joiners. Two hundred and fifty-six variation selectors. An entire block of "tag characters" at U+E0000 that mirrors ASCII — meaning any message at all can be written in characters with no visible form, and carried inside an ordinary-looking sentence.
Here is a line of text. It looks like one sentence. It is carrying four separate marks:
о in
"report" is a homoglyph — a different letter that looks identical. The tag characters spell out a
document identifier. The variation selectors spell out initials. All of it survives copy-paste, and
none of it survives this page.
The decoding is the part I care about most. Plenty of tools will strip invisible characters. Very few will tell you what was written in them, and that is the difference between "your text has been cleaned" and "someone tagged this paragraph with a document ID before sending it to you."
Three layers, two of which belong in a browser
The upstream project splits the problem in a way that survives contact with reality:
| Layer | What it is | In the browser? |
|---|---|---|
| A — deterministic | Invisible Unicode, tag characters, variation selectors, bidi controls, homoglyphs, exotic spaces. A character is present or it is not. | yes |
| B — statistical | Watermarks built into which words a model picked. Removing one means rewriting the passage. | no |
| File cleaners | EXIF, XMP, IPTC, C2PA content credentials, PNG text chunks, PDF properties, Office and EPUB properties. | yes |
Layer B is missing on purpose, and the tool says so on its own front page. A statistical watermark is a property of word choice across a whole passage — the only way to remove it is to rewrite a substantial fraction of the text, sentence by sentence, which costs you your voice and requires a language model. In a browser that means either a multi-gigabyte download or an API call. The API call would break the one promise the tool makes, which is that nothing you paste in ever leaves your device. So it does the two layers it can do honestly, and is explicit about the third rather than implying coverage it does not have.
Two decisions I would defend
Images get surgery, not a re-encode
The common way to strip metadata from an image in a browser is to draw it onto a canvas and export it again. It works, and it is quietly destructive: your image is recompressed, transparency can be flattened, the colour profile is gone, and the file you get back has nothing in common byte-wise with the file you put in.
This tool parses the container instead. A PNG is a sequence of length-tagged chunks; a JPEG is a sequence of marker segments; a WebP is a RIFF. It drops the ones carrying metadata — including the PNG text chunk where image generators write your entire prompt back into the file — and copies every remaining byte through untouched. The pixels come out bit-identical. I test this by injecting an EXIF block and a trailing tracker string into a real photograph, running it through, and asserting the result is byte-for-byte the original file again.
PDF edits never change the file's length
A PDF ends with a cross-reference table recording the byte offset of every object in the document. Delete four bytes near the front and every offset after it is wrong — which is why so many "remove PDF metadata" scripts produce files that a reader refuses to open.
So nothing here deletes. Every edit overwrites in place with the same number of bytes: string contents
become spaces, hex digits become zeroes, XMP packet bodies become whitespace. The xref stays valid and the
document still opens. Running it over my own CV — a 279 KB PDF exported from a browser — it found the
document title, the creation and modification timestamps, and, in the /Creator field, the full
user-agent string of the headless Chrome that generated it, build number included. All 607 cross-reference
entries still resolve afterwards.
That user-agent leak is a good illustration of why metadata is worth taking seriously. Nobody chose to put their browser build number in their CV. The export tool did it, silently, and it travels with every copy of the file.
The part most tools leave out
A watermark remover that reports success on everything is lying, so here is the list of things this one cannot do — the same list is on the tool's own page, one tab away from the button:
- Pixel-domain watermarks. SynthID and its relatives live in the image content, not the metadata. Stripping EXIF does nothing to them, and re-encoding does not reliably help — they are built to survive exactly that.
- Statistical text watermarks. Layer B, above.
- Metadata inside compressed PDF object streams. A byte-level pass cannot read them. The report says so rather than reporting a clean file.
- Encrypted PDFs. Detected and refused rather than corrupted.
- TIFF. The format addresses its own image data through the same tables that hold EXIF, so there is no chunk that can safely be dropped.
Findings are classified rather than just listed: confirmed (it was there, it is gone, check it yourself), probable, informational, and likely false positive — things it found, understood, and deliberately kept. A zero-width joiner inside a family emoji is structural, not a mark. So is an ideographic space in Japanese text, and a Cyrillic letter inside a Russian word. Getting those cases right is most of the work; a tool that strips every invisible character it sees will break emoji and mangle every language that is not English.
Checking my work
There is a test page that runs thirty assertions in your
browser against the same modules the tool uses — the emoji case, the Russian case, the CJK case, PNG and
JPEG and WebP and GIF round-trips, PDF length preservation, DOCX rebuild. The same file runs headlessly with
node tests.node.js. If the tool's claims and the tests disagree, the tests are the ones I trust.
And you should not trust either of them over a real checker. exiftool -a -G1 will list
everything still in an image, c2patool checks for content credentials, and
qpdf --qdf --object-streams=disable makes a PDF greppable. If one of those disagrees with the
report, the checker is right.
What it is for
Content you own or are authorised to process. Taking the GPS coordinates out of a photograph before you post it. Taking your name and your company off a document before it circulates. Finding out whether the text someone sent you has a tracking string sewn into it. Passing off machine-written work as your own or defeating attribution you agreed to is not what it is for, and removing a marker does not change what the content is.
No dependencies, no build step, no server, no account. Four small JavaScript modules and a page. It works offline after the first load, which felt like the right closing argument for a tool whose entire premise is that your files stay yours.
Have a look inside your own files
Paste a paragraph, or drop a photo. Nothing is uploaded — check your network tab.