Add Watermarks to Images Without Uploading: Client-Side Canvas Method (2026)
You need to watermark 50 ID scans before emailing them to a landlord. Every online watermark tool wants you to upload them—your passport and bank statements—to an unknown server. That's exactly the kind of document you should never upload.
The good news: your browser can watermark images entirely on your own device, no upload needed. Using the Canvas API, you add text or logo watermarks, strip location metadata, and export—all locally.
This guide shows the client-side watermarking method, how to make watermarks crop-proof, and how to batch-process many images while keeping everything private.
Why Watermark Client-Side?
The privacy problem with upload-based tools
Watermarking is most needed for sensitive images: ID scans, contracts, proofs of address, unreleased designs. These are precisely the files you shouldn't send to a random server.
The client-side advantage
| Aspect | Upload tools | Client-side (Canvas) |
|---|---|---|
| Data leaves device | Yes | No |
| Works offline | No | Yes |
| Batch limit | Often capped | Unlimited |
| Speed | Upload + download | Instant |
| Strips EXIF/GPS | Maybe | Yes (automatic) |
The browser's Canvas API is powerful enough to do all of this locally.
How Canvas Watermarking Works
The core flow, entirely in-browser:
async function watermarkImage(file, text) {
// 1. Load the image into memory
const bitmap = await createImageBitmap(file);
// 2. Draw it onto a canvas
const canvas = document.createElement('canvas');
canvas.width = bitmap.width;
canvas.height = bitmap.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0);
// 3. Draw the watermark on top
ctx.font = \`\${Math.floor(bitmap.width / 20)}px sans-serif\`;
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.fillText(text, 40, bitmap.height - 40);
// 4. Export (re-encoding strips EXIF automatically)
return new Promise(res => canvas.toBlob(res, 'image/jpeg', 0.9));
}
Your image never touches a network. The toBlob re-encode also drops EXIF/GPS metadata as a free side effect.
Making Watermarks Crop-Proof (Diagonal Tiling)
A single corner watermark is easily cropped out. For documents, tile the watermark diagonally across the whole image:
function drawDiagonalWatermark(ctx, width, height, text) {
ctx.save();
ctx.globalAlpha = 0.18;
ctx.fillStyle = '#000';
ctx.font = \`\${Math.floor(width / 22)}px sans-serif\`;
// Rotate the canvas -30 degrees
ctx.translate(width / 2, height / 2);
ctx.rotate(-30 * Math.PI / 180);
const step = Math.floor(width / 4);
const diag = Math.hypot(width, height);
for (let y = -diag; y < diag; y += step) {
for (let x = -diag; x < diag; x += step * 2) {
ctx.fillText(text, x, y);
}
}
ctx.restore();
}
This covers the entire image, so cropping can't remove the mark without destroying the content.
Adding a Logo Watermark
Instead of text, overlay a logo image:
async function logoWatermark(photoFile, logoFile) {
const [photo, logo] = await Promise.all([
createImageBitmap(photoFile),
createImageBitmap(logoFile)
]);
const canvas = document.createElement('canvas');
canvas.width = photo.width;
canvas.height = photo.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(photo, 0, 0);
// Scale logo to 20% of photo width, bottom-right, 70% opacity
const w = photo.width * 0.2;
const h = (logo.height / logo.width) * w;
ctx.globalAlpha = 0.7;
ctx.drawImage(logo, photo.width - w - 20, photo.height - h - 20, w, h);
return new Promise(res => canvas.toBlob(res, 'image/png'));
}
Batch Processing Many Images
Process a whole folder without uploading any of them:
async function batchWatermark(files, text) {
const results = [];
for (let i = 0; i < files.length; i++) {
const blob = await watermarkImage(files[i], text);
results.push({ name: \`wm_\${files[i].name}\`, blob });
// Optional: report progress
console.log(\`\${i + 1}/\${files.length} done\`);
}
return results;
}
Then trigger downloads (or zip them client-side with a library like JSZip). All 50 ID scans watermarked locally, nothing uploaded.
Opacity, Color, and Readability Tips
| Goal | Setting |
|---|---|
| Subtle mark on photos | globalAlpha = 0.2–0.3 |
| Strong deterrent on documents | globalAlpha = 0.35–0.5, diagonal tiling |
| Readable on any background | Add a text shadow (see below) |
| Not covering the subject | Bottom-right corner, small font |
Text shadow for contrast
ctx.shadowColor = 'rgba(0,0,0,0.6)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillText(text, x, y);
White text with a dark shadow stays legible over both light and dark areas.
The EXIF/Privacy Bonus
When you draw an image to a canvas and re-export it, the browser writes a fresh file from raw pixels. This means:
- GPS coordinates (where the photo was taken) → removed
- Device info (camera/phone model, serial) → removed
- Timestamps → removed
So client-side watermarking doubles as metadata scrubbing—important when sharing photos that shouldn't reveal your location.
Common Mistakes
Mistake 1: Watermark too small to deter. A tiny corner mark is cropped in seconds. Use diagonal tiling for documents.
Mistake 2: Full opacity covering the image. globalAlpha = 1 makes an ugly, content-blocking mark. Use 0.2–0.5.
Mistake 3: Exporting at low quality. toBlob(res, 'image/jpeg', 0.5) degrades the image. Use 0.9 for good quality.
Mistake 4: Forgetting logos need transparency. Export logo watermarks as PNG to preserve transparency; JPEG will add a background.
FAQ
Q: Can I watermark images without uploading them?
A: Yes. The browser's Canvas API processes images entirely on your device—no server, no upload. Ideal for sensitive documents.
Q: Does client-side watermarking remove GPS/EXIF data?
A: Yes, as a side effect. Re-encoding through canvas writes a fresh file without the original metadata.
Q: How do I stop someone cropping out the watermark?
A: Use diagonal tiling that repeats the mark across the whole image, so cropping can't remove it without destroying the content.
Q: Can I batch-watermark many images?
A: Yes—loop over the files, watermark each via canvas, and download (or zip) the results. No per-file upload, no batch cap.
Q: What opacity should I use?
A: 0.2–0.3 for subtle photo marks; 0.35–0.5 with diagonal tiling for documents you want to protect strongly.
Conclusion
Watermarking sensitive images doesn't require handing them to a stranger's server. In the browser, you can:
- Add text or logo watermarks via the Canvas API—fully local
- Make them crop-proof with diagonal tiling
- Batch-process unlimited images offline
- Strip EXIF/GPS automatically on export
- Keep quality with proper opacity and export settings
For ID scans, contracts, and personal photos, client-side watermarking is faster, unlimited, and—most importantly—private.