# Show HN: Remove the Gemini watermark by solving the alpha blend, not inpainting

> Source: <https://github.com/long66666/gemini-watermark-remover>
> Published: 2026-07-21 09:12:15+00:00

Remove the **Gemini ★ sparkle watermark** from images you generated with Google
Gemini or **Nano Banana** — entirely on your own machine. No uploads, no
sign-up, no server. Works as a browser tool, a TypeScript library, and a CLI.

Look at the stars inside the sparkle's outline. They are *back*, not painted
over — see [how it works](#how-it-works).

What this does — and doesn't.This removes thevisiblecorner sparkle only. It doesnottouch[SynthID]or any other invisible provenance watermark, and it never will. Google itself already ships watermark-free images to AI Ultra subscribers; this gives you the same clean canvas for contentyou generated yourself.

**Try it:** [geminiwatermarkwiper.com](https://geminiwatermarkwiper.com) — same
engine, running in your browser tab.

The mark is composited as a plain alpha blend:

```
observed = α · overlay + (1 − α) · original
```

That equation is invertible. If you know the overlay — its exact colour and its exact alpha, per pixel — the original comes back:

```
original = (observed − α · overlay) / (1 − α)
```

So the whole problem reduces to *knowing the overlay*, and it can be solved for
exactly. Ask Gemini for a flat white image and a flat black one. Both come back
carrying the same mark, over backgrounds you already know, which is two equations
per pixel for two unknowns:

```
α = 1 − (obs_white − obs_black) / (bg_white − bg_black)
overlay = (obs_black − (1 − α) · bg_black) / α
```

The recovered mark peaks at **31% opacity** and is nowhere near opaque, so the
original pixels were never destroyed — they were only dimmed. Reversing the blend
restores them outright. Nothing is inpainted, nothing is guessed at, and the
stars that were under the sparkle come back exactly as they were.

The template lives in [ src/template-sparkle.ts](/long66666/gemini-watermark-remover/blob/main/src/template-sparkle.ts);

[regenerates it if Google ever changes the mark.](/long66666/gemini-watermark-remover/blob/main/scripts/extract-template.mjs)

`scripts/extract-template.mjs`

Google composites the mark at a fixed **48px**, inset a fixed **96px** from the
right and bottom — the same absolute pixels in a 1024×1024 image and a 1408×768
one, not scaled to the frame.

Confirming the mark is *there* is the harder half, and most of the obvious
approaches quietly fail. Matching on colour finds teal lakes. Correlating the
template's shape against brightness is buried by gravel and fooled by any smooth
gradient. And one unmarked photo has a grey rock sitting exactly where the
sparkle would be, matching an overlay's brightness to within 5%, which defeats
every test based on amplitude.

What works is to remove the overlay and ask whether it *explained* the pixels —
specifically, what happens to the gradient energy along the mark's own
silhouette, the ring where the template's alpha changes fastest and a composited
edge would live:

**The outline must vanish.** Subtracting a sparkle from something that isn't one manufactures edges instead of erasing them.**What's left must look like the rest of the photo**, judged against control patches beside it. The rock fails here: it isn't star-shaped, so cutting a star out of it leaves its edges standing.

Both gates are in [ src/detect.ts](/long66666/gemini-watermark-remover/blob/main/src/detect.ts), with the measurements that
set them.

**100% local**— images never leave your machine** Exact reconstruction**, not inpainting: the pixels under the mark are recovered, not approximated** Says no when it means no**— an image with no sparkle is returned untouched rather than scrubbed- Batch CLI for folders of images — PNG, JPG, WebP
- Zero runtime dependencies in the core;
`sharp`

only for the CLI

``` js
import { removeWatermark } from 'gemini-watermark-remover';

// imageData: ImageData from a canvas, or { width, height, data }
const result = removeWatermark(imageData);

if (result.mode === 'none') {
  // No sparkle found. The image is returned unchanged.
} else {
  ctx.putImageData(
    new ImageData(result.image.data, result.image.width, result.image.height),
    0,
    0,
  );
}
npx gemini-watermark-remover input.png              # writes input-clean.png
gwr *.png --suffix -nowm                            # batch
gwr shot.jpg -o clean.jpg --region 900,980,120,120  # override detection
```

Nano Banana is the community name for Gemini's image model — its images carry the same sparkle, so everything here applies unchanged.

On the roadmap: the same template applied per-frame via WebCodecs. Watch the repo.

- Images carrying the
**large "Gemini" wordmark**(a different, much bigger overlay) are not supported yet. They are detected as unsupported and left alone rather than half-erased. Extracting that template needs a flat white/black pair from the export path that produces it. - The mark is assumed to be at Google's standard placement. A cropped or resized
image may need
`--region`

.

Use this on **content you generated yourself**. Invisible provenance (SynthID) is
deliberately left intact — this project is about a clean visual canvas, not about
defeating AI-content detection.

MIT
