roomnumber103's picture
Serve prebuilt files instead of building on the Hub
af957ce verified
|
Raw
History Blame Contribute Delete
10.3 kB
---
title: LLM Text Watermark Microscope
emoji: "\U0001F52C"
colorFrom: blue
colorTo: indigo
sdk: static
app_file: index.html
pinned: false
license: apache-2.0
short_description: Inspect three generations of LLM text watermarks in-browser
---
# LLM Text Watermark Microscope
An interactive research/education tool that shows **how LLM text watermarking evolved**
not just *that* watermarks work, but *what happens inside the decoding loop* when they do.
You supply the secrets and walk the decoding forward one stage at a time.
The screen is split: an unwatermarked **baseline** on the left, held fixed for the prompt, and the
watermark you are studying on the right. The baseline mirrors whatever stage you are on, so every
claim has a control beside it. Each method is a four-stage wizard that unlocks as you go:
| stage | Kirchenbauer | k-SemStamp | TextSeal |
|---|---|---|---|
| 1 | Seed & Split — type a seed, watch the vocabulary partition, click a token for the arithmetic | Seed & Clusters — which semantic clusters your secret allows next | Seed & R-values — the PRF numbers behind each candidate |
| 2 | Bias & Sampling — move δ and see any real decoding step re-weighted | Rejection Sampling — candidate sentences accepted/rejected live | Gumbel Sampling — dual-key routing vs. the baseline draw |
| 3 | Result — every generated token coloured by the list it came from | Result — every sentence by its cluster verdict | Evidence & Regions — per-token evidence heatmap, localized spans |
| 4 | Edit & Detect — all three detectors re-score as you type | | |
The four runs share sampling parameters:
| run | year | carrier | detection |
|---|---|---|---|
| Baseline | — | none | (reference) |
| [Kirchenbauer et al.](https://arxiv.org/abs/2301.10226) | 2023 | token identities (green/red vocabulary partition, +δ logit bias) | one-proportion z-test on green counts |
| [k-SemStamp](https://arxiv.org/abs/2402.11399) (Hou et al.) | 2024 | sentence semantics (k-means clusters over embeddings, rejection sampling) | z-test on valid-region sentence counts |
| [TextSeal](https://arxiv.org/abs/2605.12456) (Sander et al., Meta FAIR) | 2026 | distortion-free Gumbel-max sampling (dual-key PRF) | entropy-weighted Gamma p-value + **localized multi-region detection** |
Every stage exposes its internals rather than summarizing them: PRF seeds and the U-vs-γ
comparison for a token you click, per-step logits before and after biasing, candidate sentences
with their cluster assignments and accept/reject reasons, per-token Gumbel evidence, and the
localized region scan. Stage 4 runs **all three detectors on whatever you type**, which is where
the generations visibly separate — token-level edits destroy Kirchenbauer evidence, meaning-preserving
rewrites leave k-SemStamp intact, and excerpting shrinks a global score while the localized scan
still finds the span. Each method exports its seed, parameters, trace and scores as JSON.
Measured on a run of 96 tokens with Qwen3-0.6B (your numbers will differ):
Kirchenbauer reached z = 5.48 against a baseline of −0.61; k-SemStamp reached z = 3.00 against
2.31; TextSeal's unweighted score sat just under threshold at −log₁₀p = 3.39 and crossed it at
4.51 once entropy weighting was enabled — a direct demonstration of what that part of the paper
buys you.
## Everything runs in your browser
- **No server GPU, no API keys.** Model inference runs on your GPU via **WebGPU**
([Transformers.js](https://huggingface.co/docs/transformers.js) v3 / ONNX Runtime Web).
Your prompt and generated text never leave the browser.
- Default model: [Qwen3-0.6B](https://huggingface.co/onnx-community/Qwen3-0.6B-ONNX) q4f16
(~570 MB download, cached by the browser; thinking mode disabled). Swappable to
Qwen2.5-0.5B-Instruct (~483 MB) or SmolLM2-360M-Instruct (~273 MB).
- Sentence embeddings for k-SemStamp:
[EmbeddingGemma-300m](https://huggingface.co/onnx-community/embeddinggemma-300m-ONNX)
(~167 MB, downloaded only when you open that method). It is multilingual, so the semantic
clusters mean something in every language the demo model can write.
- **Fallbacks:** GPUs without `shader-f16` get `q4`; browsers without WebGPU fall back to WASM
(CPU — slow; keep max tokens low). Chrome/Edge ≥ 113 recommended.
All four runs share one custom autoregressive loop (manual `forward()` + KV cache — the
framework's `generate()` is bypassed so each algorithm can hook logits or replace sampling).
Baseline and Kirchenbauer consume the **same base RNG stream** with the same seed, isolating the
watermark's effect from sampling randomness. TextSeal replaces sampling with a deterministic PRF
rule and k-SemStamp's rejection loop changes randomness consumption, so stream-sharing is
impossible there — the UI says so rather than hiding it.
## What this tool is not
This is a **mechanism verifier, not an AI-text detector**. It checks for a *specific* watermark
inserted with a *known secret key*. No detection ≠ human-written; detection ≠ proof of
authorship. The editor also demonstrates honestly that iterative editing against a visible score
degrades detection — detectability and unremovability are different properties.
## Faithfulness notes (educational deviations from the papers)
| component | reference | this implementation | consequence |
|---|---|---|---|
| Kirchenbauer PRNG | `torch.Generator` (MT19937) + `randperm`, seed = `15485863 × prev_token mod 2⁶⁴−1` | same seed formula, but splitmix64-based per-token judgment `U(seed, key, id) < γ`, with the user's seed mixed in at the judgment rather than the seed so the displayed derivation stays literal | green-list size is Binomial(\|V\|, γ) instead of exactly γ\|V\|; z-test unaffected. **Not interoperable** with the reference detector |
| Kirchenbauer scheme | `lefthash` and `selfhash` | `lefthash` only | selfhash (context 4 + self-salt) not implemented |
| k-SemStamp encoder | SBERT fine-tuned with paraphrase-contrastive loss per domain | general-purpose EmbeddingGemma-300m, called through `AutoModel` for its `sentence_embedding` output (the feature-extraction pipeline would mean-pool hidden states and skip the model's dense projection) | paraphrase robustness weaker than published results, but multilingual |
| k-SemStamp centroids | k-means on 8k in-domain paragraphs | k-means (K=8) on ~440 wikitext-2, narrative and Korean sentences, shipped as JSON (`scripts/compute-centroids.mjs`). The script uses the same encoder and the same `task: clustering \| query: ` prefix as the browser — a mismatch there would put centroids and runtime assignments in different spaces | clusters are generic; margin default relaxed (0.01 vs paper 0.035) |
| k-SemStamp trials | `MAX_TRIALS = 100` | default 16 (browser latency), adjustable; each retry raises the temperature slightly, capped at 1.5x, because an unbounded climb buys candidate diversity by turning the text into gibberish | occasional forced accepts, shown in the trace |
| k-SemStamp counting | every sentence scored | a sentence in the same cluster as the one before it is **not counted again**, and fewer than three independent sentences reports **inconclusive** | consecutive same-cluster sentences re-ask one question ("is q in valid(q)?"), so counting them separately turns one coin flip into a confident-looking z. This is the same reasoning as `ignore_repeated_ngrams` on the Kirchenbauer side |
| TextSeal PRF | cryptographic hash (paper appendix A.1 / [official repo](https://github.com/facebookresearch/textseal)) | splitmix64 mixing over (key, k=3 context tokens, candidate) | statistically equivalent construction, **not interoperable** with official TextSeal |
| TextSeal localization penalties | exact combinatorial multi-region penalty | per-region `log₁₀M + log₁₀Y_max` simplification, ensemble `+log₁₀3` | slightly conservative p-values |
| TextSeal entropy weights | teacher-forced model entropies | optional (one extra forward pass ≤512 tokens); default unweighted | unweighted variant is also calibrated, per the paper |
KV-cache rollback for k-SemStamp re-computes the prefix on rejection instead of snapshotting
GPU tensors — simpler and leak-free at demo scale.
## Reproducibility
`seed`, `secret key`, sampling parameters, model repo/dtype and per-algorithm parameters are all
visible in each result's metadata and adjustable under Advanced settings. Identical
(model, prompt, parameters, key, seed) reproduce identical Baseline/Kirchenbauer outputs;
TextSeal is deterministic given the key and routing seed. WebGPU floating-point differences
across GPU vendors may change low-probability tie-breaks; bit-level determinism across devices
is not guaranteed.
## Development
```bash
npm install
npm run dev # local dev server
npm test # 74 unit + integration tests (node; mock model, no GPU needed)
npm run build # production build (dist/)
node scripts/compute-centroids.mjs # regenerate k-SemStamp centroids (downloads the encoder)
```
Architecture: `src/lib/model` (adapter + custom generation loop) · `src/lib/watermark`
(algorithms) · `src/lib/detectors` · `src/lib/workers/inference-worker.ts` (dedicated worker
owning the model) · `src/lib/stores/app-state.ts` (per-method wizard state) ·
`src/components/stages/<method>` (the stage screens) · `src/components/BaselinePanel.svelte`
(the mirror view).
## Citations
```bibtex
@inproceedings{kirchenbauer2023watermark,
title={A Watermark for Large Language Models},
author={Kirchenbauer, John and Geiping, Jonas and Wen, Yuxin and Katz, Jonathan and Miers, Ian and Goldstein, Tom},
booktitle={ICML}, year={2023}
}
@inproceedings{hou2024ksemstamp,
title={k-SemStamp: A Clustering-Based Semantic Watermark for Detection of Machine-Generated Text},
author={Hou, Abe Bohan and Zhang, Jingyu and Wang, Yichen and Khashabi, Daniel and He, Tianxing},
booktitle={Findings of ACL}, year={2024}
}
@article{sander2026textseal,
title={TextSeal: A Localized LLM Watermark for Provenance and Distillation Protection},
author={Sander, Tom and Chang, Hongyan and Sou{\v{c}}ek, Tom{\'a}{\v{s}} and others},
journal={arXiv preprint arXiv:2605.12456}, year={2026}
}
```