SynthID covers four separate watermarking systems: one for images, one for text, one for audio, one for video. They share the name and a detector umbrella, but the image and text watermarks are completely opposites, so there is no single "SynthID bypass" to speak of.
TLDR: The image watermark cannot be bypassed, but there are ways to work around it. The text watermark can simply be rewritten.
There is no unified "bypass"
The image, audio and video marks are signal-domain watermarks, where the watermark is in the pixels or the samples. It is physically present in the file, and unlike metadata you can strip or modify, removing it means damaging the actual file.
The text mark is just tokens and the mark is applied while the text is being generated, by selecting which words get chosen, and it is recovered later by a statistical test.
The image watermark
SynthID-Image is a post-hoc, deep-learning watermark with two neural networks:
- Encoder that takes a finished image and embeds the signal
- Decoder that reads that signal back out
Google published the technical details in October 2025 in the SynthID-Image paper. There is also an external partnership variant called SynthID-O used outside Google's own products.
The mark is spread across the whole image, so cropping a section does not remove it. During training the watermarked image is repeatedly ran through JPEG compression, added noise, blur, filters, rotation and resizing, and the encoder is penalised every time the decoder fails to recover the mark afterwards.
In other words, the encoder is explicitly optimised to survive the most used image manipulation techniques. Independent reverse-engineering concluded that single-transform attacks fail because they are what the model learned to defend against.
So if you want to see this for yourself, run these transformations, then check the result in the SynthID Detector:
# recompress
magick input.png -quality 70 attacked_jpeg.jpg
# blur
magick input.png -gaussian-blur 0x1.2 attacked_blur.png
# noise
magick input.png -attenuate 0.4 +noise Gaussian attacked_noise.png
# crop 15% off each edge
magick input.png -gravity center -crop 70x70%+0+0 +repage attacked_crop.png
# resize down and back up
magick input.png -resize 60% -resize 166% attacked_resize.png
All of these degrades the image before it removes the watermark, because the watermark was trained against these.
So how does this invisible watermark actually look like? If you ask Nano Banana to generate a completely white image, you can actually extract the image-wide watermark pattern from it. See the pattern on the denoised version:

The only category that reliably may work is stacking several destructive operations on top of each other, and by the time you have done enough to break the mark you have pretty much destroyed the picture. Obviously, you cannot self-host the encoder to test the decode side properly, since Google won't release it.
The text watermark
Large language models generate one token at a time, and each candidate token has a probability. SynthID-Text intervenes at this step: at each position, every candidate token is assigned several pseudorandom g-values derived from a secret key set, and the token with the highest total g-value tends to win. If you repeat this across a few hundred tokens, the output shows measurable statistical skew toward high-g-value tokens. A detector that knows the keys can recompute the g-values and test whether the skew is present.
This is shipped in Hugging Face Transformers from 4.46 onward. To install it:
pip install "transformers>=4.46" torch
Transformers has two relevant watermarks:
- The red-green scheme (
WatermarkingConfig+WatermarkDetector) has a detector that needs no training and returns a z-score, so it is the easiest way to see statistical text watermark appear and then degrade. - SynthID-Text proper (
SynthIDTextWatermarkingConfig) uses tournament sampling and needs a trained Bayesian detector. This one is more difficult to to set up.
Generate watermarked and plain text from the same model and score both:
import torch
from transformers import (
AutoTokenizer, AutoModelForCausalLM,
WatermarkingConfig, WatermarkDetector,
)
model_id = "openai-community/gpt2" # any causal LM works
tok = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)
tok.pad_token_id = tok.eos_token_id
tok.padding_side = "left"
wm = WatermarkingConfig(bias=2.5, seeding_scheme="selfhash", greenlist_ratio=0.25)
prompt = tok(["The history of the lighthouse is"], return_tensors="pt")
plen = prompt["input_ids"].shape[-1]
gen = dict(do_sample=True, max_new_tokens=200, top_k=50)
out_wm = model.generate(**prompt, watermarking_config=wm, **gen)
out_plain = model.generate(**prompt, **gen)
det = WatermarkDetector(model_config=model.config, device="cpu", watermarking_config=wm)
z_wm = det(out_wm[:, plen:], return_dict=True).z_score[0]
z_plain = det(out_plain[:, plen:], return_dict=True).z_score[0]
print(f"watermarked z = {z_wm:.2f}")
print(f"unwatermarked z = {z_plain:.2f}")
The detector flags anything with a z-score above 3.0 by default. Watermarked is above that, plain text is much lower, closer to zero. The mark is invisible in the reading but obvious in the maths.
The weakness is structural since text is mutable in ways that preserve meaning but move the tokens. Synonyms, reordering / a full paraphrase all change the token sequence the detector is scoring. You can run the watermarked text through a second model with "rewrite this, keep the meaning" prompt and re-score:
# paraphrase however you like: a second local model, an API call, or by hand.
# then re-tokenise the rewritten text and score it with the SAME detector:
rewritten = tok([paraphrased_text], return_tensors="pt")
z_rewritten = det(rewritten["input_ids"], return_dict=True).z_score[0]
print(f"paraphrased z = {z_rewritten:.2f}")
For SynthID-Text proper keys define the mark and len(keys) sets the number of layers:
from transformers import (
AutoModelForCausalLM, AutoTokenizer, SynthIDTextWatermarkingConfig,
)
tok = AutoTokenizer.from_pretrained("repo/id")
model = AutoModelForCausalLM.from_pretrained("repo/id")
wm = SynthIDTextWatermarkingConfig(
keys=[654, 400, 836, 123, 340, 443, 597, 160, 57, 29],
ngram_len=5,
)
inputs = tok(["your prompt here"], return_tensors="pt")
out = model.generate(**inputs, watermarking_config=wm, do_sample=True, max_new_tokens=200)
print(tok.batch_decode(out, skip_special_tokens=True))
This needs a Bayesian detector trained for that exact key set and tokenizer in order to detect it (not free), using BayesianDetectorModel, SynthIDTextWatermarkLogitsProcessor and SynthIDTextWatermarkDetector.
The training script is in the transformers research projects under synthid_text, and there is a pre-trained detector on the Hub (joaogante/dummy_synthid_detector) for an end-to-end run. The paraphrase result is the same as the red-green case; rewriting the text changes the g-value.
Audio & Video
Audio watermarking covers Lyria and NotebookLM's generated speech. The mark is inaudible and is designed to survive MP3 compression, added noise and speed changes, so the audio is very similar to the image detection mechanism.
Video is Veo, and it is the image watermark applied frame by frame. A video is a sequence of stills so it inherits the image watermark on every frame. Re-encoding, frame-rate changes and filters were in scope during training.
How to break it?
Images, audio and video: You can technically bypass SynthID on an image, but bypass not the correct word here. First of all, the watermark is a useful thing to have, especially with the current models being capable of producing near-perfect images (r/isthisAI).
What you actually do is take the watermarked image and regenerate a very close approximation of it with a model that does not use SynthID. It will not be pixel-for-pixel, but it is close enough to pass. The new image carries no mark because those pixels never had one to begin with, so nothing was removed. The original file still has its watermark of course. There are plenty projects on Github that do this already. This works against any pixel-domain watermark, not just SynthID.
Text: Rewrite works, routed through a second model, but this is just a consequence of the medium being editable. That is also why it is the only one of the four you can reliably fool.