Sign Up to Our Newsletter

Be the first to know the latest tech updates

Uncategorized

Text Watermarking in Python: Catch Whoever Copies Your Writing

Text Watermarking in Python: Catch Whoever Copies Your Writing


On August 2, 2026, Anthropic began watermarking every piece of text Claude produces. Since 2024, Gemini (Google) has used SynthID-Text, a method they published in Nature and later open-sourced. OpenAI built something similar. China has required embedded labels on AI-generated content since September 2025, and nearly 190 organizations have signed the EU’s transparency code.

The result: billions of words generated from these systems every day carrying an invisible mark—not metadata that vanishes on copy-paste, but a signal baked into the words themselves.

So if AI companies can do that, can you?

Most people assume no. Images have pixels to tweak; audio has a spectrum. Text is just characters. If someone scrapes your writing and claims it, it’s your word against theirs.

That assumption is wrong. A century ago, mapmakers, dictionary editors, and at least one very annoyed lyrics company figured this out long before language models existed.

You can watermark plain text three ways, each leaving a detectable signal:

  • Invisible characters: easiest to add, easiest to erase. Any sanitizer or chatbot pass wipes them out.

  • Keyed word choices: specific substitutions based on a hidden key. Survives light editing, but a full rewrite kills it.

  • Meaning-level marks (rigged sampling): hardest to remove. It holds up better under rewriting, but the signal weakens. Durability always costs strength.

A text watermark isn’t a visible stamp. It’s a pattern of choices only you know.

1. Who this is for, and what you’ll get

If you publish writing online and want more than a guess when it gets copied, this is for you.

You’ll get:

  • A simple coin-flip intuition for three types of text watermarking.

  • An 80-line, standard-library-only script that embeds a 32-bit ID in text and detects it later.

  • A keyed word-choice watermark, a model-free detector, and a meaning-aware upgrade—plus where they fall short in practice.

  • Real-world survival tests across 12 channels, several editing attacks, full paraphrasing, and Chinese translation.

  • A practical rule for choosing the right watermark for the threat.

Everything was tested on real models: Gemma-2-9b-it for watermarking and Qwen2.5-7B-Instruct for attacks, running on an NVIDIA GB10. The test set included 50 original paragraphs and 100 public-domain passages for false-positive checks.

The scripts, corpus, and calibration data are all in text-watermarking-toolkit, so you can reproduce the results yourself.

2. People have been watermarking text for a century

Long before watermarking became cryptographic, it was used in a much simpler way: hide a tiny, deliberate mistake or variation, then see who copies it.

Text Watermarking in Python: Catch Whoever Copies Your Writing
Figure 1 — A century of text watermarking, split by what does the recognising. Above the 2006 line, marks were planted by hand and spotted by a human who knew what to look for; below it, both halves become a key and a hypothesis test. Image by author.

2.1 Trap streets

One example is from mapmakers.

Mapmakers inserted fake streets, towns, or landmarks into maps. If the same fake feature appears on a competitor’s map, the source of the copying is obvious.

In 1925, the General Drafting Company added a fake New York town called Agloe, named from its founders’ initials. Years later, a store opened at the crossroads and adopted the name. The fictional town had effectively become real.

Figure 2 — The trap street. One invented street costs the map nothing and eventually becomes the copy of others. Image by author.

2.2 Mountweazels

Reference books use the same trick with fake entries.

The New Columbia Encyclopedia famously included Lillian Virginia Mountweazel, a fictional photographer with an elaborate biography. The New Oxford American Dictionary later planted esquivalience, supposedly meaning “the wilful avoidance of one’s official responsibilities.”

Neither was real. Both were bait: if another reference work reproduced them, that was evidence of copying.

Figure 3 — The mountweazel. Same idea in a reference book: a word with no referent cannot be independently researched, only copied. Image by author.

2.3 Canary traps

A canary trap takes the idea one step further. Instead of giving everyone the same fake detail, each recipient gets a slightly different version.

If the document leaks, the variation identifies whose copy it came from.

Elon Musk has said Tesla used this technique in 2008 by varying whether sentences were separated by one space or two. Those tiny differences formed a binary signature unique to each recipient.

Figure 4 — The canary trap. Every recipient gets the same words and a different invisible pattern, so a leaked copy names the leaker. Image by author.

2.4 Genius vs. Google

The most important example here is one where the watermark apparently worked — but the lawsuit still failed.

Lyrics site Genius suspected Google was reproducing its transcriptions in search results. Genius began alternating straight and curly apostrophes in a pattern that, when read as Morse code, spelled REDHANDED.

The pattern was seeded into 301 songs and reportedly appeared in Google’s results for 116 of them.

Genius later used a second watermark based on different types of spaces, encoding the word Genius.

Figure 5 — Genius’s apostrophes. Straight is a dot, curly is a dash, and the sequence spells REDHANDED in Morse: the last hand-planted watermark in the story, and the first carrying a real payload. Image by author.

It sued Google for $50 million in 2019. The case was dismissed in 2020, not because the watermark failed, but because Genius did not own the underlying lyrics; it licensed them.

That distinction matters:

A watermark can show that your version of a text was copied. It cannot, by itself, prove that you owned the text in the first place.

3. The coin-flip analogy

Imagine that while writing, you repeatedly make a hidden binary action: insert one of two invisible characters, choose between two synonyms, or pick between two equally plausible next words.

One matching choice proves nothing: it has a 50% chance of happening by accident. But if 30 choices match the key in a row, the odds of that happening randomly are roughly 1 in a billion.

That probability is what gives the watermark statistical evidence. In formal terms, it can be expressed as a p-value.

Figure 6 — The coin-flip analogy. The same sentence either way; only the pattern of choices differs, and only someone holding the key can see it. Sixteen coins at 14 hits clears a 1%-false-positive threshold, which is also why short text is the hard case. Image by author.

The three watermarking families mainly differ in what the “coin flip” represents:

Family

The coin

Where it lives

Invisible characters

a zero-width character — present or absent

between the letters

Keyed word choices

“big” vs. “large”; straight vs. curly apostrophe

in the words

Rigged sampling

which token the model emits next

inside the generator

The first two can be applied to text that already exists. The third must happen while a language model is generating the text.

3.1 Technique 1 — invisible characters

Unicode includes characters that occupy zero visible space. Two examples are:

  • U+200B — zero-width space

  • U+200C — zero-width non-joiner

Both are legitimate Unicode control characters, but in ordinary Latin text they are effectively invisible.

Assign one character to 0 and the other to 1, and you have a hidden binary channel inside normal-looking text.

Zach Aysan described this technique in 2017 and pointed out an important consequence: copied text can carry an invisible fingerprint with it. Someone pasting a leaked document elsewhere could unknowingly preserve the identifier embedded inside it.

Figure 7 — Technique 1 end-to-end. Two invisible characters carry a 32-bit ID plus a CRC, repeated after every sentence, and one line of code removes all of it. Image by author.

How the implementation stays reliable

Here’s the implementation that I ran the experiments with.

"""Invisible-ink watermarking for plain text, using zero-width Unicode characters."""import reZERO, ONE = "​", "‌"   # zero-width space, zero-width non-joinerID_BITS, CRC_BITS = 32, 8PAYLOAD_BITS = ID_BITS + CRC_BITS# Insert a payload after sentence-ending punctuation that is followed by a space.SENTENCE_END = re.compile(r"(?<=[.!?])(?=\s)")def crc8(value: int, bits: int = ID_BITS) -> int:    """CRC-8/ATM (polynomial 0x07) over `bits` bits of `value`, MSB first."""    crc = 0    for i in reversed(range(bits)):        crc ^= ((value >> i) & 1) << 7        crc = ((crc << 1) ^ 0x07) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF    return crcdef embed(text: str, owner_id: int) -> str:    """Return `text` with an invisible copy of `owner_id` after every sentence."""    bits = f"{owner_id:0{ID_BITS}b}{crc8(owner_id):0{CRC_BITS}b}"    mark = "".join(ONE if b == "1" else ZERO for b in bits)    marked = SENTENCE_END.sub(mark, text)    return marked if mark in marked else text + markdef strip(text: str) -> str:    """Remove every zero-width watermark character. This is the sanitizer."""    return text.replace(ZERO, "").replace(ONE, "")def extract(text: str) -> int | None:    """Recover the owner ID, or None if no payload passes its CRC check."""    votes: dict[int, int] = {}    for run in re.findall(f"[{ZERO}{ONE}]+", text):        bits = "".join("1" if ch == ONE else "0" for ch in run)        for start in range(0, len(bits) - PAYLOAD_BITS + 1):            window = bits[start : start + PAYLOAD_BITS]            owner_id = int(window[:ID_BITS], 2)            if crc8(owner_id) == int(window[ID_BITS:], 2):                votes[owner_id] = votes.get(owner_id, 0) + 1    return max(votes, key=votes.get) if votes else None

Three choices do most of the work.

1. A checksum prevents false matches.
Without validation, any random sequence of zero-width characters could decode into an apparent ID. Adding an 8-bit CRC reduces the chance of a random payload passing validation to about 1 in 256. Across 1,700 extraction attempts in these experiments, there were zero false IDs.

2. The payload is repeated.
A 40-bit identifier requires 40 invisible characters. Repeating it after each sentence means even a copied paragraph can still contain a complete, decodable mark.

3. The decoder uses sliding windows.
Copying can merge, truncate, or misalign invisible-character sequences. Instead of assuming perfect formatting, the decoder tests every possible 40-bit window and returns the valid ID that appears most often.

What it looks like

Before

The report is confidential. Please do not forward it to anyone outside the team.

After

Visually, it looks identical. Internally, the text contains repeated zero-width characters encoding the identifier.

extract()0xc0ffee01

In this example, 80 invisible characters were added with no visible change. When the text is copied, those characters can travel with it because the clipboard treats them as ordinary text.

To find out, I tested 150 marked paragraphs per channel across 11 mechanical transformations and one language model, checking whether the exact 32-bit ID survived.

Figure 8 — Zero-width watermark survival by channel. The mark is untouched by every format conversion and every normalizer tested, and destroyed by the two things that rewrite characters: an explicit sanitizer, and an LLM asked to tidy the text. Image by author.

What survives

Against systems that simply move or reformat text, the watermark was extremely robust: 150/150 recovered through every tested mechanical channel, including:

  • copying only the middle 50% of a paragraph

  • UTF-8 and UTF-16 conversions

  • JSON and HTML round trips

  • Unicode NFKC normalization

Two results are worth noting. Python’s \s does not remove U+200B, because it is a Unicode format character rather than a normal space. NFKC normalization leaves it intact too.

What kills it

  • Explicit sanitization: 0/150 survived.
    If someone knows which zero-width characters to remove, a simple replacement strips the watermark completely.

  • Language-model cleanup: 4/50 survived — just 8%.
    I only asked the model to fix typos and formatting while preserving the wording. But language models generally regenerate text rather than edit the original character stream, so the invisible characters disappear. With full paraphrasing, survival went down to 0/50.

That gives technique 1 a simple limit:

It survives text transport. It does not survive text rewriting.

3.2 Technique 2 — keyed word choices

The second technique hides information inside the wording itself rather than between characters. In other words, we write constantly equivalent choices:

  • big/large

  • begin/start

  • problem/issue

In normal practice, a writer chooses whichever sounds best. However, with a keyed watermark, a secret key determines which acceptable alternative to use.

The major advantage over zero-width characters is that there is nothing extra to strip. The watermark is part of the text itself.

Figure 9 — Technique 2. The key decides which member of each interchangeable pair belongs at each slot. Nothing is hidden in the text, so there is nothing for a sanitizer to strip. Image by author.

Sanitizers, Unicode normalization, scrapers, and file conversions cannot remove it. To destroy the signal, you generally have to rewrite the words themselves.

3.2.1 The mechanism

Start with a public table of interchangeable word pairs. At each eligible word, use a secret key to choose which member of the pair should appear.

def keyed_bit(key: bytes, context: str, pair: tuple[str, str]) -> int:    """Which member of a pair the key selects at a slot with this left context."""    message = f"{context}|{pair[0]}".encode("utf-8")    return hmac.new(key, message, hashlib.sha256).digest()[0] & 1

If the bit is 0, use the first word. If it is 1, use the second. Existing words that already match the key stay unchanged; mismatches are swapped only when the sentence still reads naturally.

def detect(text: str, key: bytes) -> dict:    """How many slots land on the key's side, and how surprising is that?"""    slots = scan(text, key)    n = len(slots)    hits = sum(slot["on_key"] for slot in slots)    z = (hits - n / 2) / math.sqrt(n / 4) if n else 0.0    p = 0.5 * math.erfc(z / math.sqrt(2)) if n else 1.0    return {"slots": n, "hits": hits, "z": z, "p_value": p}

With the wrong key—or ordinary unmarked text—each choice should match the key about 50% of the time. A marked document produces an unusually high number of matches.

Two details matter

Use stable context.
The key cannot depend on a nearby word that might itself be swapped, or where embedding one bit changes the next one. I instead use the nearest preceding word that is not in the synonym table.

Hash the pair identity too.
Hashing only the context creates correlations when the same context repeats. Including the word pair keeps slots independent enough for the binomial test to behave properly.

After that fix, 100 human-written passages tested under 20 keys produced null z-scores with mean 0.007 and standard deviation 1.020—almost exactly what the statistical model predicts.

3.2.2 How well does it work?

I tested 50 paragraphs under three keys, using Gemma-2-9B-IT to reject swaps that changed meaning or grammar.

It accepted 880 of 1,039 swaps (84.7%), changing about 2.72% of all words.

At a calibrated 1% false-positive rate, detection required roughly z ≥ 2.45.

Figure 10 — Detector z-scores for marked text against unmarked human text. At paragraph length the two distributions overlap and a quarter of marked paragraphs fall below the threshold; at article length they don’t overlap at all. Length is the whole game. Image by author.

And here’s the result with an interesting view:

Text length

Slots

Mean z

Detected at 1% FPR

One paragraph (~215 words)

12.9

+2.93

73.3% (per-seed 68–78%)

One article (~1,075 words)

64.3

+6.66

100.0% (per-seed 100%)

Unmarked human text

−0.27

0.0%

The main limitation is sample size. A single paragraph had only about 13 usable slots. Even a perfect 13-for-13 match reaches only about z = 3.6, leaving little room for noise.

Across five paragraphs, though, the average rose to 64 slots, with z around +6.66 and perfect detection in the test set.

The practical takeaway: word-choice watermarks work at document length, not tweet length.

3.2.3 Detection ratio across techniques

Figure 11 — Detection after four editing attacks and a full rewrite, at paragraph length. Moderate edits degrade the mark gracefully; a paraphrase removes it completely. Bars show the mean, vertical lines the spread across three keys. Image by author.

Attack

Detected

Mean z

Clean copy

73.3%

+2.93

Sentence deletion 30%

53.3%

+2.48

Word replacement 10%

42.7%

+2.22

Partial copy (middle 50%)

28.7%

+1.88

Word replacement 30%

10.0%

+1.13

Full paraphrase

0.0%

−0.29

Deleting 30% of sentences still left about half the paragraphs detectable because surviving slots kept their original bits.

Partial copying performs worse mostly because there are too few observations. Half a paragraph may contain only six slots, and six coin flips cannot produce enough statistical evidence to cross the detection threshold.

In contrast, a full paraphrase is different. Detection fell to 0%, with mean z = −0.29—essentially the null distribution because the paraphraser made fresh word choices, effectively re-flipping every coin.

3.2.4 The hidden cost: text quality

The bigger problem is that a technically valid synonym is not always a good replacement.

Examples from the experiment included:

“Two dollars of filtered water repaired a issue I had thrown multiple hundred dollars at.” (original: fixed a problem I had thrown several hundred dollars at)

and

“a water heater failed and price me a ceiling” (original: cost me a ceiling)

The substitutions are understandable, but obviously worse. cost → price, for example, works in some noun contexts and fails as a verb.

To measure that cost, I used Qwen2.5-7B-Instruct as an independent evaluator.

Keyed synonym

Semantic (k=4)

Mean log-prob change per token

−0.1324

+0.0013

Passages that got less fluent

100% (150/150)

55.3%

New a/an agreement errors

28, in 26/150 passages (17.3%)

1, in 1 passage

Length change

+0.0%

+15.8%

The result was clear:

  • Every marked paragraph became less probable under the language model.

  • The average paragraph contained 5.9 swaps.

  • About 1 in 6 paragraphs gained a grammatical error.

So the tradeoff is straightforward:

more forced word choices → stronger statistical signal → greater risk to the writing quality.

The safest version uses a small, hand-audited set of genuinely interchangeable pairs—and still requires a human read before publication.

3.3 Technique 3 — marking the meaning

The first two techniques fail under paraphrasing for the same reason: the watermark lives in the wording. Rewrite the wording, and the mark disappears.

So what if the watermark lived in the meaning instead?

That is the idea behind PostMark (Chang et al., EMNLP 2024) and a similar embedding-based approach described by Jina AI.

Here is the flow:

  1. Keep a secret list of uncommon words.

  2. Use an embedding model to find which words are most semantically related to the document.

  3. Ask a language model to weave a few of those words naturally into the text.

If the meaning stays similar, the same rare words remain relevant — and because those words now carry actual content, a rewriter may keep them.

Figure 12 — Technique 3. The document’s meaning selects which secret words it should carry, so a paraphrase that preserves the meaning tends to select the same words. Tends to, at about one rewrite in three. Image by author.

For my experiments, I used BAAI/bge-m3, a multilingual embedding model. I did not use jina-embeddings-v3 because of its non-commercial license.

3.3.1 Getting the detector right

My first detector looked strong — until I tested it on clean text.

It compared semantic keywords from the suspect text with the planted watermark words and flagged 62.7% of translated samples.

But it also flagged 100% of the unwatermarked originals.

Why? Both sets reflected the document’s meaning, so the detector was measuring semantic similarity, not the watermark.

“Is this semantically the same document?”

not

“Does this document contain a watermark?”

The valid black-box detector

PostMark’s actual detector needs only the suspect text. It:

  • Understands the text’s meaning. It converts the suspect text into an embedding—a numerical representation of its semantic meaning.

  • Uses that meaning to select secret words. From its secret table, it finds the k words most semantically related to the text. Here, k just means some predetermined number, such as 20.

  • Counts matches. It checks how many of those selected secret words actually occur, literally, in the suspect text.

Unmarked text rarely contains these uncommon words by coincidence. However, watermarked text does. Using a threshold calibrated to about a 1% false-positive rate, this became the detector I report.

3.3.2 Results: robustness comes at a cost

With k = 4 words per paragraph, Gemma-2-9b-it successfully inserted 93.5% of the requested words, while increasing text length by 15.8%.

Condition

Black box (no original)

Presence (needs original)

Clean copy

99.3%

100.0%

Word replacement 30%

60.0%

99.3%

Partial copy 50%

40.7%

67.3%

Translated to English and back

23.3%

68.0%

Full paraphrase

9.3%

32.7%

Translated to Chinese

0.0%

0.7%

Unmarked original

0.7%

4.0%

Clean detection was nearly perfect, even on a single paragraph.

Paraphrasing was much harder:

  • 9.3% black-box detection after rewrite

  • 32.7% with a detector allowed to reference the original

  • 0% after direct Chinese translation

The translation result is unsurprising: the planted English words disappear. A multilingual embedding model can still recognize the meaning, but semantic similarity alone is not proof of a watermark.

The weak paraphrase result at k=4 also turned out to be partly a configuration issue. With only four planted words, losing one word changes the score dramatically.

So I repeated the same experiment with k = 12.

k=4

k=12

Words planted (of requested)

93.5%

80.6%

Clean copy

99.3%

100.0%

Full paraphrase

9.3%

36.7% (per-seed 34–40%)

Unmarked original

0.7%

0.7%

Paraphrase survival improved by roughly , while the measured false-positive rate stayed around 0.7%.

That leads to a more useful conclusion:

Semantic watermarking becomes more resistant to paraphrasing as you insert more watermark-bearing content.

But that robustness has a cost. At k=12, the model inserted only 80.6% of the requested words, down from 93.5% at k=4. Working a dozen unusual words into roughly 215 words of prose starts to affect naturalness.

Figure 13 — The three families before and after a full paraphrase by a different model, all scored by detectors that don’t get to see the original. Marking meaning is the only thing that outlasts a rewrite at all: 9% at k=4, rising to 36.7% at the heavier k=12 insertion rate. Image by author.

In other words, the trade-off is:

more watermark → better robustness → more rewriting and worse readability

At a practical insertion rate, the method detected roughly a third of paraphrased passages in my tests. That was substantially better than the other watermark families I tested, but still far from reliable enough for something like a legal attribution claim.

Reproduction caveat

This was a partial reproduction, not a direct replication of PostMark.

PostMark uses GPT-4o for word insertion. I used a 9B open model, ~215-word passages, and an intentionally aggressive paraphraser.

3.4 Technique 4 — rigged sampling

The earlier techniques can be added to text after it is written. This one is the opposite. It has to happen while the language model is generating each token.

A language model writes by repeatedly choosing the next token from a probability distribution. Each choice is effectively a coin flip, and a page of text contains thousands of them. The watermark works by rigging those random choices in a way only the detector can recognize.

Scott Aaronson described the core idea in 2022, based on work with OpenAI engineer Hendrik Kirchner: keep the model’s normal probabilities, but replace ordinary randomness with keyed pseudorandomness based on a secret key and the preceding tokens.

To a reader, the output looks normal. To someone with the key, the sequence contains a detectable statistical pattern.

Figure 14 — Technique 4. The key splits the vocabulary and nudges one half up at every position. Quality holds because there was rarely one correct next word, which is also why the mark vanishes when there was. Image by author.

There are two major approaches: green-list watermarking and tournament sampling.

3.4.1 Kirchenbauer et al. — green-list watermarking

At every step, the vocabulary is split pseudorandomly into a green list and a red list. Green tokens receive a small probability boost.

The model still has many reasonable words to choose from, but over hundreds of tokens it selects green words more often than chance predicts.

Detection is simple: count the green tokens and calculate a z-score.

3.4.2 SynthID-Text — tournament sampling

DeepMind’s SynthID-Text uses a different method. It samples several candidate tokens and runs them through a pseudorandom tournament to decide which one wins.

Its key advantage is a non-distortionary mode, designed so the overall token distribution remains unchanged. SynthID-Text is used with Gemini and is available in open-source tooling.

3.4.3 Other watermarking techniques

Meta’s watermarking research added stronger statistical guarantees and support for multi-bit payloads.

A more surprising result came from Watermarking Makes Language Models Radioactive: researchers found that a watermark could remain statistically detectable in a new model trained partly on watermarked text. In their experiments, the effect was measurable with only 5% contaminated training data.

OpenAI also developed a text watermark that reportedly performed very well internally on long passages, but did not release it. Its stated concerns included translation, paraphrasing, and simple character-level edits — the same attacks that weaken most text watermarks.

3.4.4 Does it hold up?

I tested Kirchenbauer and SynthID using Gemma-2-9b-it on the same 30 prompts:

  • 20 open-ended prompts

  • 10 factual prompts

  • 3 random seeds

  • 400 tokens per response

  • Detection tested at 50, 100, 200, and 400 tokens

  • Thresholds calibrated to a 1% false-positive rate

The biggest finding was not which watermark won. It was how much freedom the model had while writing.

Figure 15 — Detection rate against the number of tokens the detector sees, for both schemes, split by how much freedom the model had. On creative text both schemes get there; on factual text the watermark has almost nothing to hold onto. ✕ marks detection after a paraphrase at 400 tokens. Image by author.

On open-ended prompts, both schemes worked well.

Tokens seen

Kirchenbauer

SynthID-Text

50

28.3%

60.0%

100

53.3%

86.7%

200

78.3%

96.7%

400

95.0%

100.0%

SynthID was stronger at every tested length. It reached at roughly 200 tokens the detection performance Kirchenbauer needed about 400 tokens to achieve.

At 400 tokens, SynthID detected 100% of the watermarked open-ended generations in this experiment.

Tokens seen

Kirchenbauer

SynthID-Text

50

0.0%

30.0%

100

0.0%

33.3%

200

3.3%

76.7%

400

50.0%

76.7%

The lesson is simple:

More tokens + more possible word choices = more signal.

On factual prompts, both methods performed poorly.

Consider:

List the eight planets in order.

There is very little freedom in what comes next. The model cannot favor a watermarked token if doing so would make the answer wrong.

No choice means no useful coin flip. No coin flips means no watermark signal.

This is one of the biggest limitations of generative watermarking: it is naturally weak on short text, factual answers, code, and other highly constrained outputs.

Repetition also causes problems. In my factual tests, unmarked outputs produced unusually unstable detection scores because forcing simple factual answers to 400 tokens led the model to repeat itself, breaking some of the statistical assumptions behind detection.

Paraphrasing removes most of the signal. The biggest practical weakness was rewriting. After passing the 400-token watermarked outputs through Qwen2.5-7B-Instruct for paraphrasing, detection on open-ended text fell from:

  • Kirchenbauer: 95.0% → 23.3%

  • SynthID: 100% → 11.7%

On factual text, both fell to 0%.

That matches the broader weakness of this entire technique: the watermark belongs to the model’s original token choices. Once another model rewrites those choices, much of the statistical pattern disappears.

Arm

Throughput

Overhead

plain

110.5 tok/s

SynthID-Text

82.2 tok/s

−25.6%

Kirchenbauer

54.6 tok/s

−50.6%

Generation cost

In naive implementations, watermarking also adds computation during every generation step.

The reference implementations in transformers perform extra processing repeatedly in Python, so benchmark numbers should be treated as an upper bound, not as the cost of an optimized production system.

The useful comparison is relative:

Tournament sampling was cheaper than green-list watermarking in my tests, but neither was free.

Large providers can integrate the same logic much deeper into their serving infrastructure, reducing that overhead substantially.

Practical takeaway

Generative watermarking works best when text is long, open-ended, and left mostly unchanged. It becomes much weaker when the output is short, factual, repetitive, translated, or paraphrased.

So if an LLM is producing creative text and you want the original output to be traceable, these schemes can work remarkably well.

But if someone rewrites the text — or the model had little freedom to begin with — there may simply not be enough statistical signal left to detect.

3.4.5 The detection toolkit

If you find a page that looks suspiciously like yours, run:

python detect-watermark.py suspect.txt --key "my-secret-key"

Real output, on a marked article:

Checking 1,127 words.  [ -- ] No invisible characters. Nothing hidden between the letters.  [HIT ] KEYED WORD PATTERN FOUND - 80 of 80 word choices match your key         (z = 8.94, p = 1.9e-19). Chance alone would do this in less than         one unmarked text in a trillion.VERDICT: this text carries a watermark.Remember what that means: it is strong evidence of copying, not proof ofownership. Genius caught Google with a watermark and still lost in court.

And on a clean passage nobody ever marked:

  [ -- ] No invisible characters. Nothing hidden between the letters.  [ -- ] Word choices look ordinary - 17 of 36 match your key (z = -0.33).         That is what unmarked text looks like.VERDICT: no watermark found.

What it checks

The detector can run three checks:

  • Zero-width watermark: decodes any hidden payload and reports the owner ID.

  • Keyed watermark: with --key, recomputes each keyed slot and applies a z-test, flagging results at z ≥ 2.45.

  • Semantic watermark: with --semantic words.json, runs the embedder. If the dependency is missing, it fails gracefully with an install hint.

4. Which watermark for which threat

These techniques don’t really compete. They fail under different attacks. So the useful question isn’t which watermark is best? It’s what are you defending against?

Figure 16 — The decision rule, with the measurement behind each branch. Every rate shown is from this article’s runs, not from the source papers. Image by author.

Your threat

Use

Why

Scrapers and content farms republishing verbatim

Zero-width

100% through HTML, JSON, Markdown, docx; decodes from half a paragraph

Finding which recipient leaked a document

Zero-width, unique ID per copy

32 bits = 4 billion possible recipients; the CRC means no false accusations

Someone republishing with light edits

Keyed word choices

Nothing to strip; survives 30% sentence deletion at 53%

Someone running your text through an LLM

Semantic, at a heavy insertion rate

36.7% after paraphrase at k=12 (9.3% at k=4) — the best on offer, not a guarantee

Proving a specific unusual claim was yours

Canary trap

Free, no code, survives rewriting if the detail is load-bearing

Marking text your own LLM generates

SynthID or Kirchenbauer

Built into transformers; needs generation-time control

Proving you own the text in court

None of the above

This is a legal problem, not a technical one. Ask Genius.

Four practical rules

  • Mark at article length, not paragraph length.
    The keyed word mark rises from 73.3% detection at 215 words to 100% at 1,075 words. More text is the cheapest robustness you can buy.

  • Layer independent marks.
    Zero-width characters and keyed word choices don’t interfere with each other. A sanitizer that removes one can leave the other completely intact.

  • Calibrate on unmarked text first.
    Set thresholds using human-written text you never watermarked. I tested against 100 unmarked passages; that check exposed a detector that otherwise looked reliable.

  • Save the key when you publish.
    Record the key, ID, or word list used for each watermark. You’ll need it later to verify the mark.

If you can’t reproduce the detection, you don’t really have a watermark.

5. References and resources

Core techniques

  • A Watermark for Large Language Models — Kirchenbauer, Geiping, Wen, Katz, Miers, Goldstein, 2023 · arXiv:2301.10226. The green/red list scheme; implemented in transformers as WatermarkingConfig.

  • Scalable watermarking for identifying large language model outputs (SynthID-Text) — Dathathri et al., Nature 634, 818–823, October 2024 · doi:10.1038/s41586-024-08025-4. Tournament sampling with a non-distortionary mode; google-deepmind/synthid-text, License Apache-2.0.

  • PostMark: A Robust Blackbox Watermark for Large Language Models — Chang, Krishna, Houmansadr, Wieting, Iyyer, EMNLP 2024 · arXiv:2406.14517. The semantic watermark this article reimplements at small scale.

  • Three Bricks to Consolidate Watermarks for Large Language Models — Fernandez, Chaffin, Tit, Chappelier, Furon, WIFS 2023 (Best Student Paper) · arXiv:2308.00113. Grounded statistical tests with guaranteed FPR, and multi-bit payloads.

  • Watermarking Makes Language Models Radioactive — Sander, Fernandez, Durmus, Douze, Furon, NeurIPS 2024 · arXiv:2402.14904. Watermark traces survive into models trained on the marked text.

  • The hiding virtues of ambiguity: quantifiably resilient watermarking of natural language text through synonym substitutions — U. Topkara, M. Topkara, Atallah, MM&Sec ’06 · doi:10.1145/1161366.1161397. The ancestor of technique 2.

  • Watermarking GPT outputs — Scott Aaronson (with Hendrik Kirchner), 2022 · talk slides. The keyed-pseudorandomness proposal.

  • A Survey of Text Watermarking in the Era of Large Language Models — Liu, Pan, Lu, Li, Hu, Zhang, Wen, King, Xiong, Yu · ACM Computing Surveys 57(2), Article 47, 2024 · doi:10.1145/3691626 · arXiv:2312.07913.

Models used

  • google/gemma-2-9b-it — 9B instruction-tuned model; planted the keyed and semantic marks and generated Exp 3. Gated weights under the Gemma Terms of Use.

  • Qwen/Qwen2.5-7B-Instruct — 7B instruction-tuned model; ran every paraphrase, translation and readability score, so the attacker is never the model that planted the mark. License Apache-2.0.

  • BAAI/bge-m3 — multilingual embedding model, License MIT. Chosen over jina-embeddings-v3, which is CC-BY-NC and is cited here as prior art only.

Data

  • Corpus (50 blog paragraphs, 203–235 words). Written by the author for this article; no third-party licence applies.

  • Negatives (100 passages, 150–299 words). Project Gutenberg, public domain — 10 passages each from Pride and PrejudiceAlice’s Adventures in WonderlandMoby DickFrankensteinThe Adventures of Sherlock HolmesA Tale of Two CitiesThe Picture of Dorian GrayDraculaHeart of Darkness, and A Doll’s House. Gutenberg header and footer stripped before use.

Industry and regulation

  • Anthropic, “Claude’s text watermark”, August 2, 2026 · anthropic.com/news/claude-text-watermark

  • OpenAI’s unreleased text watermark, reported by the Wall Street Journal, August 2024 (secondary coverage: Tom’s Hardware, Engadget)

  • EU AI Act Article 50 and the Code of Practice on Transparency of AI-generated Content, July 2026 (~190 signatories) · European Commission

  • China CAC “Measures for Labeling AI-Generated Content” and mandatory standard GB 45438-2025, effective September 1, 2025 · Covington Inside Privacy

The stories

  • Genius vs Google: CNBC on the suit; TechCrunch on the August 2020 dismissal.

  • Tesla’s 2008 canary-trap emails, per Musk’s own account · The Intercept. Other accounts of the incident differ.

  • Mountweazels, esquivalience, trap streets and Agloe · Wikipedia, “Fictitious entry”

  • Zach Aysan, “Fingerprinting with Zero-Width Characters”, December 2017 · zachaysan.com

  • Thinkst Canarytokens — free honeytokens including Word-document beacons, DNS tokens and fake AWS keys · canarytokens.org

Tooling

  • MarkLLM — open toolkit implementing many watermarking schemes with a visualization layer; THU-BPM, EMNLP 2024 demo · arXiv:2405.10051 · THU-BPM/MarkLLM. Worth a look if you want to compare schemes beyond the two built into transformers.

  • Hugging Face, “AI Watermarking 101” · huggingface.co/blog/watermarking



Source link

Team TeachToday

Team TeachToday

About Author

TechToday Logo

Your go-to destination for the latest in tech, AI breakthroughs, industry trends, and expert insights.

Get Latest Updates and big deals

Our expertise, as well as our passion for web design, sets us apart from other agencies.

Digitally Interactive  Copyright 2022-25 All Rights Reserved.