PNG alpha channel — the hidden gotcha when watermarks blend with transparency
If you only have time to read three lines: when a watermark’s RGB matches the background it is invisible to any RGB-based detector. But if the PNG preserves the alpha channel, the watermark geometry is leaked as a binary mask — 2,400 pixels in our test — that any agent can read by opening the file in Photoshop, GIMP, ImageMagick or a 5-line script. The fix is straightforward: bake the alpha into RGB before saving, or save as JPEG, or accept that the watermark is recoverable by anyone who has the file.
toDataURL('image/png'), and most AI image generators. The gotcha is not that the watermark is removable — it is that the watermark is detectable by anyone with the file, in three lines of code.
What the alpha channel actually stores
PNG supports a fourth channel called alpha that says “how opaque is this pixel”. A pixel with RGBA = (180, 30, 30, 255) is fully opaque red; (180, 30, 30, 128) is the same red blended 50% with whatever is behind it; (180, 30, 30, 0) is fully transparent. Most image viewers render the alpha against a checkerboard background or whatever sits beneath the image. Most export tools preserve whatever alpha the document has when saving.
The gotcha is the interaction between two facts:
- When a watermark is composited over a photo with reduced opacity, the composited RGB may end up looking the same as the un-watermarked region. (This is by design — that is what low alpha does.)
- The PNG file format stores the alpha channel separately from RGB. If the editor exports the document “as-is”, the alpha channel still records the watermark’s footprint even when the RGB channel does not.
The two facts together mean: a watermark can be invisible to a human eye or to any RGB-based detector, but recoverable to anyone who reads the alpha channel.
The experiment
Five 600x400 fixtures, each with the same 200x90 watermark composited in the bottom-right corner. The watermark has three bars with different alpha values (255 full, 200 semi, 100 faint). For each fixture we save three variants:
- Baked PNG — the watermark alpha is composited into RGB; the PNG has no alpha channel. This is what most social platforms re-encode to.
- Preserved PNG — the watermark alpha is kept in the file’s alpha channel; the RGB is the composited result. This is what Photoshop and Figma export by default.
- JPEG — JPEG has no alpha channel; the watermark is baked in by definition.
For each variant we measure: file size, how many pixels in the watermark rect have alpha < 255 (the alpha leak), and the maximum per-pixel RGB delta against the un-watermarked ground truth in the watermark rect (the RGB visibility).
The numbers
| Fixture | Watermark RGB | Baked bytes | Preserved bytes | JPEG bytes | Alpha leak (px) | Baked max RGB delta |
|---|---|---|---|---|---|---|
| flat white bg | white on near-white | 2,081 | 6,550 | 2,741 | 2,400 | 10 |
| flat red bg | white on red | 4,556 | 6,570 | 5,224 | 2,400 | 225 |
| flat red match | red on red, same RGB | 3,993 | 6,126 | 1,695 | 2,400 | 0 |
| gradient bg | white on gradient | 5,967 | 7,779 | 10,923 | 2,400 | 151 |
| checker bg | white on checker | 5,916 | 7,726 | 23,173 | 2,400 | 225 |
Look at the bottom-right cell of the third row. The watermark is the same red as the background (RGB = 180, 30, 30 in both). The baked PNG stores zero difference per pixel in the watermark region. RGB invisibility is perfect. Yet the alpha-preserved PNG leaks 2,400 pixels of watermark geometry, exactly the same as every other fixture, because alpha is independent of RGB.
How an attacker reads the alpha channel
Three lines of code:
from PIL import Image
img = Image.open('watermarked.png').convert('RGBA')
mask = img.split()[-1].point(lambda a: 255 if a < 255 else 0)
mask.save('recovered_watermark_mask.png')
The mask is a clean binary image of the watermark shape, even when the RGB channel looks identical to the un-watermarked background. From here, the attacker has the watermark region as a ready-to-feed mask for any inpainter.
Tools that do the same thing without code:
- Photoshop: open the PNG, click the channel tab, select Alpha. The watermark shape is right there.
- GIMP: Layer → Transparency → Remove Alpha Channel or Component → View Alpha Channel.
- ImageMagick:
identify -verbose file.png | grep -i alphagives you alpha statistics;convert file.png -alpha extract mask.pngextracts the alpha channel as a grayscale image. - ffmpeg:
ffmpeg -i file.png -vf alphaextract mask.png.
Why this happens by default
Most export pipelines preserve alpha because the alternative (baking it in) loses information. If a designer has a transparent logo on top of a transparent background and saves it as PNG, the right thing to do is preserve the alpha so the logo can be re-used. The problem is that the same default applies when you have an opaque background with a watermark on top: the editor has no way to know whether the partial alpha in the watermark region is “intentional transparency that the user wants to keep” or “a watermark that the user wants to bake in”.
Photoshop’s default: preserve alpha. Figma’s default: preserve alpha. GIMP’s default: preserve alpha. Browser canvas.toDataURL('image/png'): preserve alpha. ImageMagick convert: preserve alpha. The watermark has to be explicitly flattened or pre-multiplied to bake the alpha into RGB.
How to actually protect a watermark against alpha leakage
If you are a watermarker (the person who put the watermark on the image, not the person who wants to remove it), the only ways to hide the watermark from alpha-channel recovery are:
- Bake the alpha into RGB before saving. In Photoshop: Layer → Flatten Image, then save as PNG. In Figma: select all layers, then Export with “Include alpha channel” unchecked. In code: composite the watermark over the background using straight alpha math, then save RGB only (drop the alpha channel).
- Save as JPEG. JPEG has no alpha channel. The watermark is baked in by definition. The cost is JPEG’s lossy compression, but if you have the source pixels and need to ship a watermark the user cannot detect from alpha, this is the simplest answer.
- Save as WebP. WebP also has no alpha unless you opt in. Same answer as JPEG.
- Embed the watermark at full opacity. If you can’t bake, make the watermark fully opaque so there is no alpha gradient to leak. You give up the soft-edge look but you get alpha-leak protection.
- Add intentional noise to the alpha channel. Add 1-2 units of random alpha to every pixel in the image so the watermark region is not cleanly distinguished by a single threshold. Defeats trivial recovery; a determined attacker with a noise model can still recover the watermark.
If you are the person trying to remove the watermark, you don’t need any of the above — just read the alpha channel as in the snippet above, then run the recovered mask through any inpainter. The 2,400 pixels in our test give you the full watermark geometry in a single PNG.
What about browser canvas?
When a web app draws a watermark on a <canvas> and exports it via canvas.toDataURL('image/png'), the resulting PNG has whatever alpha the canvas pixels had at export time. globalAlpha at draw time propagates to the canvas pixel alpha, which propagates to the PNG. The same gotcha applies. canvas.toDataURL('image/jpeg', 0.92) drops alpha and is the right choice for a watermark that should be alpha-leak-proof.
The site’s own Watermark Maker exports JPEG only by default precisely for this reason: the watermark you put on a stock photo is meant to survive being shared, and surviving being shared means surviving alpha-channel recovery. JPEG-baked watermarks are recoverable only by the inpainter, not by a 5-line alpha extractor.
What about AI-generated images?
Most image generation APIs (Stable Diffusion, DALL-E, Midjourney, Flux) return PNG with full alpha = 255, so the alpha channel is empty. If you watermark such an image with reduced opacity and save through an editor that preserves alpha, the alpha-channel gotcha applies the same way. If you watermark with full opacity and save as PNG, the alpha channel is uniformly 255 and the gotcha does not apply — but the watermark is then obvious in the RGB.
Adobe Firefly’s “Generate” output is PNG with alpha; Adobe’s in-editor watermark export preserves alpha. Same gotcha. The product teams know this; it is a known tradeoff and they have not (as of mid-2026) shipped a default that bakes alpha for export.
FAQ
Is the alpha leak the same as the watermark being removable?
No. The alpha leak is the watermark being detectable. Once detected, the watermark can be removed by any inpainter — including this site’s Telea — given the recovered mask. But the alpha leak is the easier of the two problems: a 5-line script recovers the mask, while removing the watermark requires a 200-line algorithm (Telea) or a 50 MB neural network (LaMa).
Does this work for video watermarks?
The principle generalises but the surface is different. Video containers with alpha (VP9 with alpha, ProRes 4444, WebM with alpha) leak per-frame watermark geometry. Video containers without alpha (H.264, H.265, VP9 without alpha) bake the watermark in. Same trade-off: alpha is recoverable, baked is recoverable only by inpainting.
What if the editor saves with pre-multiplied alpha?
Pre-multiplied alpha (RGB pixels are already multiplied by their alpha) is used in some game and motion-graphics pipelines. PNG does not specify whether alpha is pre-multiplied or straight; both exist. The alpha channel still leaks the watermark shape either way; pre-multiplication only affects how RGB values are stored, not the alpha itself.
Can I watermark an image and have it survive this attack?
Yes, but only by accepting that the watermark is recoverable. There is no “invisible-to-everyone” watermark in a format that supports alpha. The trade-off is fundamental: the watermark’s job is to be visible to humans and removable-by-design to robots, but the alpha channel is the bridge between the two.
Does the site’s tool recover watermarks via alpha?
Not directly. The site’s tool requires you to paint the mask manually. But the alpha-leak trick is the same as what any recovery tool would do: read the alpha channel, threshold, feed the mask to an inpainter. If you want to test it on your own images, the snippet in this article is enough to recover the mask.
Methodology and reproducibility
Five 600x400 RGB fixtures generated deterministically (flat colours, vertical gradient, black-and-white checkerboard). One 200x90 RGBA watermark overlay with three bars at alpha 255/200/100. For each fixture, three encode variants: baked PNG (RGB only, no alpha channel), preserved PNG (RGBA kept), JPEG (RGB only, lossy). All encodes run through sharp 0.35.4. Measurements: file size from fs.statSync, alpha-leak count by scanning img.split()[-1] in the watermark rect, RGB delta from per-pixel max-abs against the un-watermarked source. All numbers in this article are copy-pasted from work/day5-bench/results/alpha_compare.json.
← Back to the tool · Previous: 10 watermark tools compared · About