MarkCleaner / Field notes

ChatGPT Watermark Remover: Compare the Text Before You Strip Anything

A character scan tells you what is present. A before-and-after comparison tells you what changed during your own copy-and-paste step. Neither result identifies who wrote the text or proves a watermark.

Answer: Keep a copy of your text before copying it into the destination, then compare the Unicode code points in both versions locally. Remove only a change you understand and actually want to remove. A detected zero-width character by itself is not evidence that ChatGPT inserted a watermark.

Why compare two versions?

A scan of one text can report that a code point exists, but it cannot tell whether the character was already in your source, arrived from an editor, or appeared during paste. The practical question for a publishing workflow is often narrower: what changed between my saved draft and the text I pasted?

In the competitor pages reviewed for this guide, the common workflows scan one text, highlight characters, or remove a predefined set. This page adds a paired-input check: compare the saved source with the pasted copy and inspect only the net code-point changes. It is a diagnostic step, not a watermark detector.

A small reproducible example

These are synthetic strings, not collected ChatGPT outputs. In the first pair, one zero-width space is inserted between A and the emoji. The report should show one added U+200B; the emoji itself is one Unicode code point but two UTF-16 code units.

BeforeAfterCode-point changeWhat the result means
A😀B
3 code points; 4 UTF-16 code units
A​😀B
4 code points; 5 UTF-16 code units
U+200B: +1One code point differs between these constructed inputs. This does not establish who inserted it or why.
A BA BU+0020: −1; U+00A0: +1A normal space was replaced by a no-break space; the visible result may look nearly identical.
👩💻👩‍💻U+200D: +1The inserted joiner participates in an emoji sequence. A blanket “delete every invisible character” rule may alter intended text.

The counts above are derived directly from the displayed Unicode strings. To reproduce them, run the code below with the same before/after pairs. Use your own original and pasted text for a real comparison; synthetic examples cannot say what happened in your editor.

Compare code-point counts in your browser

This short script reads two strings that you paste into the variables. It performs no network request. It reports count differences, not character positions, and intentionally does not remove or rewrite either string.

const before = `PASTE YOUR SAVED SOURCE HERE`;
const after  = `PASTE THE COPIED VERSION HERE`;

function counts(text) {
  const result = new Map();
  for (const character of text) {
    const cp = character.codePointAt(0);
    const label = `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`;
    result.set(label, (result.get(label) || 0) + 1);
  }
  return result;
}

const a = counts(before);
const b = counts(after);
const labels = [...new Set([...a.keys(), ...b.keys()])].sort();
console.table(labels
  .map(codePoint => ({
    codePoint,
    before: a.get(codePoint) || 0,
    after: b.get(codePoint) || 0,
    delta: (b.get(codePoint) || 0) - (a.get(codePoint) || 0)
  }))
  .filter(row => row.delta !== 0));

For the first synthetic pair, the only row is U+200B | before 0 | after 1 | delta +1. For the second, the changed rows are U+0020 | delta −1 and U+00A0 | delta +1. A zero-row table means the two strings have the same per-code-point counts; it does not prove their order is identical, so also compare the text itself if order matters.

Use the result without damaging the text

  1. Save the source. Keep an untouched copy before pasting into a CMS, word processor, or web form.
  2. Compare locally. Run the snippet on the saved source and copied result. Avoid pasting private drafts into a third-party inspector if you do not want that service to process them.
  3. Interpret the delta. A changed code point is a difference, not an authorship marker. Check whether the character has a formatting, language, or emoji function before removing it.
  4. Make a copy, then clean selectively. Apply the smallest change that fixes the issue you observed. Re-run the comparison and inspect the rendered result in the destination.
  5. Keep evidence separate from claims. A character-level difference does not predict an AI detector score and does not establish that a text is human-written or AI-written.
Important limitation: OpenAI has described text watermarking as a research direction it had developed and was considering; that is not evidence that a particular zero-width character in a pasted passage is a ChatGPT watermark. Unicode also assigns legitimate layout and joining behavior to characters people sometimes call “invisible.” Do not label a scan result a watermark without independent evidence.

What this check can and cannot answer

It can answerIt cannot answer
Which code-point counts differ between the exact two strings you supplied.Which application or person introduced the difference.
Whether a copy/paste step changed a space-like or format character in this example.Whether text contains a reliable ChatGPT watermark or hidden author ID.
Whether selective cleanup changes the measured counts.Whether an AI detector will accept the text or whether its prose is original.

Sources and scope

This guide reports a reproducible comparison method and synthetic examples. It does not claim to remove a proven watermark.