Watermarking images used to mean opening Photoshop, setting up a text layer, adjusting opacity, flattening, and exporting. For a single image, that's five minutes. For fifty images, it's an afternoon.
Here's how to do it entirely in your browser using the Canvas API — no server uploads, no software, no account required.
Why Watermark Images?
A digital watermark serves several purposes depending on who's using it:
Copyright protection. Once an image is online, it can be copied and reused. A visible watermark doesn't prevent copying, but it deters casual theft and makes attribution obvious when images spread beyond their original context.
Branding. YouTubers, bloggers, and photographers add their channel name or website URL to every image. When content gets shared, the watermark travels with it.
Portfolio protection. Photographers and designers showing work to potential clients add prominent watermarks over their best work. Buyers can see the quality without getting a usable file.
Sample image protection. Stock photo sites overlay visible watermarks on previews. The watermark is integral to the business model.
The Privacy Problem with Upload-Based Tools
Most online watermarking tools work the same way: you upload the image, their server applies the watermark, you download the result.
This is fine for stock photos or public images. It becomes a problem when the image contains unreleased product shots, client work under NDA, private photos, or any content you wouldn't want stored on a third-party server, even temporarily.
Browser-based processing avoids this entirely. The file never leaves the device.
How Browser Watermarking Works
The Canvas API makes this straightforward. The core process:
- Draw the original image onto a canvas
- Draw the watermark (text or image) on top
- Export the canvas as a new image file
async function addTextWatermark(imageFile, text, options = {}) {
const {
position = 'bottom-right',
fontSize = 24,
color = 'rgba(255, 255, 255, 0.7)',
fontFamily = 'Arial',
padding = 20,
} = options;
// Load original image
const img = await loadImage(imageFile);
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(img, 0, 0);
// Configure text style
ctx.font = `${fontSize}px ${fontFamily}`;
ctx.fillStyle = color;
ctx.textBaseline = 'bottom';
// Add shadow for visibility on any background
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
// Calculate position
const textWidth = ctx.measureText(text).width;
let x, y;
switch (position) {
case 'top-left': x = padding; y = fontSize + padding; break;
case 'top-right': x = canvas.width - textWidth - padding; y = fontSize + padding; break;
case 'bottom-left': x = padding; y = canvas.height - padding; break;
case 'bottom-right': x = canvas.width - textWidth - padding; y = canvas.height - padding; break;
case 'center': x = (canvas.width - textWidth) / 2; y = canvas.height / 2; break;
}
ctx.fillText(text, x, y);
return canvas;
}
function loadImage(file) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = URL.createObjectURL(file);
});
}
Adding a Logo Watermark
Logo watermarks require PNG files with transparent backgrounds. The alpha channel in the PNG becomes the transparency when composited.
async function addLogoWatermark(imageFile, logoFile, options = {}) {
const {
position = 'bottom-right',
opacity = 0.7,
scale = 0.15, // Logo width as fraction of image width
padding = 20,
} = options;
const [img, logo] = await Promise.all([
loadImage(imageFile),
loadImage(logoFile),
]);
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(img, 0, 0);
// Calculate logo dimensions
const logoWidth = canvas.width * scale;
const logoHeight = (logo.naturalHeight / logo.naturalWidth) * logoWidth;
// Calculate position
let x, y;
switch (position) {
case 'bottom-right':
x = canvas.width - logoWidth - padding;
y = canvas.height - logoHeight - padding;
break;
case 'center':
x = (canvas.width - logoWidth) / 2;
y = (canvas.height - logoHeight) / 2;
break;
// ... other positions
}
// Apply opacity and draw logo
ctx.globalAlpha = opacity;
ctx.drawImage(logo, x, y, logoWidth, logoHeight);
ctx.globalAlpha = 1.0;
return canvas;
}
Tiled Watermarks
For maximum protection (the kind used on high-value previews), repeat the watermark across the entire image:
function addTiledWatermark(canvas, text, options = {}) {
const {
fontSize = 20,
color = 'rgba(255, 255, 255, 0.3)',
rotation = -30, // degrees
spacing = 150,
} = options;
const ctx = canvas.getContext('2d');
ctx.font = `${fontSize}px Arial`;
ctx.fillStyle = color;
const angleRad = (rotation * Math.PI) / 180;
const textWidth = ctx.measureText(text).width;
// Save context state
ctx.save();
// Rotate and tile
for (let y = -canvas.height; y < canvas.height * 2; y += spacing) {
for (let x = -canvas.width; x < canvas.width * 2; x += spacing + textWidth) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angleRad);
ctx.fillText(text, 0, 0);
ctx.restore();
}
}
ctx.restore();
}
Batch Processing Multiple Files
For applying the same watermark to many images:
async function batchWatermark(files, watermarkConfig) {
const results = [];
for (const file of files) {
const canvas = await addTextWatermark(file, watermarkConfig.text, watermarkConfig);
// Convert to blob
const blob = await new Promise(resolve => {
canvas.toBlob(resolve, 'image/jpeg', 0.92);
});
results.push({
name: `watermarked_${file.name}`,
blob,
url: URL.createObjectURL(blob),
});
}
return results;
}
Watermark Placement Considerations
Contrast matters: A white watermark is invisible on a white sky. Using a slight shadow (ctx.shadowColor) helps visibility on any background.
Size vs. intrusiveness: Watermarks too small to read serve no protective purpose. The sweet spot is typically 15–25% of the image width for logo watermarks.
Corner placement: Bottom-right is the convention most people expect. It's also easiest to crop out. Bottom-center is harder to remove without noticeably altering the image.
Central placement: Maximum protection, maximum intrusiveness. Reserve for sample images where the watermark is the whole point.
Try It
ToolZip's image watermark tool handles all of this in your browser — text watermarks with font, size, color, and opacity controls; logo watermarks via PNG upload; batch processing; nine position options.
toolzip.app/tools/image-watermark
ToolZip — 48 free browser-based tools. Everything runs client-side.
Top comments (0)