remove-gemini-watermark
v0.1.2
Published
Remove Gemini / Nano Banana watermarks from images. Pure local processing, no upload. Works in browser and Node.js.
Downloads
319
Maintainers
Readme
remove-gemini-watermark
Remove Gemini / Nano Banana watermarks from images — locally, with no uploads.
Works in Node.js (CLI / server) and browser (Vite / webpack / any bundler).
| Entry | Environment | Exports |
|-------|-------------|--------|
| remove-gemini-watermark | Node.js | processFile + removeWatermark |
| remove-gemini-watermark/browser | Browser | removeWatermark + readImageBuffer + pixelsToBlob |
How It Works
Uses Reverse Alpha Blending to mathematically recover the original pixels hidden beneath the semi-transparent watermark overlay:
original = (blended - watermark × α) / (1 - α)The watermark shape and opacity are encoded in bundled mask templates. No AI models, no network requests — pure pixel math.
CLI Usage
npx (no install required)
npx remove-gemini-watermark photo.pngGlobal install
npm install -g remove-gemini-watermark
remove-gemini-watermark photo.pngExamples
# Single file — modified in-place
remove-gemini-watermark photo.png
# Multiple files
remove-gemini-watermark a.png b.jpg c.webp
# Entire directory (recursive)
remove-gemini-watermark ./photos
# Mix of files and directories
remove-gemini-watermark ./screenshots export.pngSupported formats: PNG · JPG / JPEG · WebP
Files are modified in-place. Back up originals if needed.
Programmatic API
Node.js — processFile()
import { processFile } from 'remove-gemini-watermark';
const result = await processFile('photo.png');
console.log(result.detected); // true if watermark was found
console.log(result.debug.processedPixels); // number of pixels modified
// Custom output path (original is preserved)
await processFile('photo.png', { output: 'photo_clean.png' });
// Adjust alpha strength (default: 1)
await processFile('photo.png', { alphaStrength: 1.2 });Browser — remove-gemini-watermark/browser
Browser-only entry. Zero dependencies — sharp is never bundled.
With <input type="file">
import { removeWatermark, readImageBuffer, pixelsToBlob } from 'remove-gemini-watermark/browser';
async function handleFile(file: File) {
// 1. Decode file → PixelData
const pixels = await readImageBuffer(file);
// 2. Remove watermark (in-place)
const result = await removeWatermark(pixels);
console.log(result.detected, result.debug);
// 3. Encode back to Blob → Object URL
const blob = await pixelsToBlob(pixels, 'image/png');
const url = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
}
document.querySelector('input[type=file]')
.addEventListener('change', e => handleFile(e.target.files[0]));With Canvas ImageData
import { removeWatermark } from 'remove-gemini-watermark/browser';
const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = {
data: imageData.data,
width: canvas.width,
height: canvas.height,
};
const result = await removeWatermark(pixels);
// pixels.data is modified in-place
ctx.putImageData(imageData, 0, 0);React / Vite example
import { removeWatermark, readImageBuffer, pixelsToBlob } from 'remove-gemini-watermark/browser';
export function WatermarkRemover() {
const [resultUrl, setResultUrl] = useState<string>();
async function handleDrop(file: File) {
const pixels = await readImageBuffer(file);
const result = await removeWatermark(pixels);
if (result.detected) {
const blob = await pixelsToBlob(pixels);
setResultUrl(URL.createObjectURL(blob));
}
}
return (
<div onDrop={e => handleDrop(e.dataTransfer.files[0])}>
{resultUrl && <img src={resultUrl} />}
</div>
);
}API Reference
removeWatermark(pixels, options?) — universal
Modifies pixels.data in-place and returns a result.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| alphaStrength | number | 1 | Multiply mask alpha — increase if watermark is not fully removed |
| force | boolean | false | Process even when watermark is not detected |
processFile(inputPath, options?) — Node.js only
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| output | string | same as input | Output file path |
| alphaStrength | number | 1 | See above |
| force | boolean | false | See above |
RemoveResult
{
detected: boolean;
debug: {
maskSize: number; // 48 or 96 (px)
watermarkBrightness: number;
surroundBrightness: number;
brightnessDiff: number; // > 10 means watermark detected
processedPixels: number;
};
}Requirements
- CLI / Node.js: Node.js >= 18,
sharp(installed automatically as an optional dependency) - Browser: any modern browser supporting
createImageBitmapandOffscreenCanvas(Chrome 69+, Firefox 105+, Safari 16.4+)
Development
Setup
git clone https://github.com/YOUR_USERNAME/remove-gemini-watermark.git
cd remove-gemini-watermark
pnpm installBuild
pnpm buildOutput goes to dist/. The CLI entry is dist/cli.js.
Watch mode
pnpm devRebuilds automatically on source changes.
Local testing
Link the package globally so the remove-gemini-watermark command is available system-wide:
npm linkThen test with any image:
remove-gemini-watermark ~/Downloads/photo.pngAfter making changes, just rebuild — no need to re-link:
pnpm build
# the command is updated immediatelyTo unlink when done:
npm unlink -g remove-gemini-watermarkProject structure
src/
├── algorithm.ts # Pure algorithm — mask data, detection, reverse blending (zero deps)
├── io.ts # I/O — runtime branch: Canvas API (browser) / sharp (Node.js)
├── remove-watermark.ts # Orchestrator — combines algorithm + io, exports shared API
├── browser.ts # Browser entry — removeWatermark + readImageBuffer + pixelsToBlob
├── cli.ts # CLI entry — argument parsing, file/directory walking
└── index.ts # Node.js public API exportsPublishing
npm version patch # or minor / major
npm publishprepublishOnly runs build automatically before publishing.
License
MIT
