To watermark a screenshot with JavaScript, draw the screenshot onto a canvas, draw the watermark text over it, then export the canvas as an image. If you first need to capture part of a web page, render the DOM element to a canvas with html2canvas and pass that canvas into the same watermarking function. The watermark is baked into the exported pixels; a separate CSS overlay is not.
Choose the right way to get your screenshot
The watermarking step is the same whether your starting image is already available or you create a capture first: draw the source onto a destination canvas, overlay text, and export. The part that changes is how you obtain the source.
| Starting point | Practical approach | Trade-off |
|---|---|---|
| Existing image or screenshot | Load or receive the image, draw it to a canvas, then add text and export. | Simple and controllable, but cross-origin images can prevent reading or exporting the canvas. |
| DOM element in a web page | Use html2canvas to render the element to a canvas, then watermark it. | Browser-side and convenient, but it reconstructs the DOM rather than capturing browser pixels; CSS and embedded content may differ. |
| Browser extension | Use the browser’s native tab screenshot API. | More faithful to what the browser displays, but requires extension permissions and browser-specific API handling. |
| Server-side screenshot | Use browser automation such as Puppeteer or Playwright. | Requires a server-side browser workflow; html2canvas is not a native screenshot solution for Node.js. |
The html2canvas project explains its DOM-based rendering approach, its limits, and when to use native capture or browser automation in its documentation.
Watermark an existing image or canvas
This browser-side function accepts an image, canvas, or other supported drawable source, places it on a fresh canvas, overlays a text watermark at the bottom-right, then triggers a PNG download. It uses toBlob() and an object URL rather than converting the entire image to a large data URL.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
- This app applies a standard watermark that you design with custom color and text on multiple images at once: 500+.
- No ads, no in-app purchases, no Wi-Fi needed
- No subscriptions you own the software
- GDPR/UKDPR/CADPR compliant
- Made in the USA
async function watermarkImage(source, text) {
if (!source || !source.width || !source.height) {
throw new Error("Source must have a drawable width and height");
}
const canvas = document.createElement("canvas");
canvas.width = source.width;
canvas.height = source.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("2D canvas context is unavailable");
ctx.drawImage(source, 0, 0);
ctx.save();
ctx.globalAlpha = 0.55;
ctx.fillStyle = "white";
ctx.font = "bold 24px sans-serif";
ctx.textAlign = "right";
ctx.textBaseline = "bottom";
ctx.fillText(text, canvas.width - 16, canvas.height - 16);
ctx.restore();
const blob = await new Promise((resolve, reject) => {
canvas.toBlob(result => {
if (result) resolve(result);
else reject(new Error("Canvas export failed"));
}, "image/png");
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "watermarked-screenshot.png";
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
For example, if you have an image element, wait until it has loaded before calling the function:
const image = document.querySelector("#source-image");
if (!image.complete) {
await new Promise((resolve, reject) => {
image.addEventListener("load", resolve, { once: true });
image.addEventListener("error", reject, { once: true });
});
}
await watermarkImage(image, "Example watermark");
drawImage() can draw image, canvas, and video sources, including scaling them onto the destination canvas; see Apple WebKit’s drawImage reference. The new canvas is important: the watermark is only embedded in the output once it is drawn into the same canvas that you export.
Adjust placement and appearance
Change the drawing settings to suit the image. globalAlpha controls opacity, fillStyle the color, font the weight and size, and the alignment and baseline determine how the text sits relative to the coordinates. In the example, the 16-pixel inset keeps the watermark away from the bottom and right edges. For a top-left watermark, use textAlign = "left", textBaseline = "top", and coordinates such as 16, 16.
Rank #2
- Image editing program compatible with Windows 11, 10 (x64)
- Create slideshows and photo collages
- Adjust size, crop, rotate or straighten images
- Optimize and enhance your photos
- Burn photos to CD, DVD or Blu-ray
If you need consistent placement across differently sized images, calculate the font size and inset from canvas.width and canvas.height instead of using fixed pixel values. For long text, measure it with ctx.measureText(text) and wrap it yourself; fillText() draws a single line.
Recommended Free Tools
Capture a web page element with html2canvas
Install html2canvas using the package manager and bundler used by your project, or include its browser build as appropriate for your setup. The project API accepts an element and options and resolves to a canvas. Then hand that canvas to watermarkImage() from above.
const element = document.querySelector("#capture");
if (!element) throw new Error("Capture element #capture was not found");
const source = await html2canvas(element);
await watermarkImage(source, "Example watermark");
In a module-based project, import the library before this code using the import path provided by your package setup. The html2canvas documentation describes installation, usage, configuration, and rendering limits at html2canvas documentation.
Cross-origin images and CORS
A page may display an image from another origin while the browser still prevents that image from being read back through canvas. If a cross-origin resource is drawn without the required server permission, the canvas can become tainted and export or pixel access will fail. html2canvas has a useCORS option that can attempt CORS loading, but it cannot grant permission the image server does not provide. Configure the remote server to send appropriate CORS headers, use a same-origin asset, or omit that resource.
What html2canvas does and does not capture
html2canvas recreates a rendering from the DOM and styles; it is not a capture of the browser’s final pixels. Some CSS, cross-origin images, embedded media, or other content may be missing or rendered differently. For a browser extension, use the browser’s native screenshot API where available. For server-side captures, use Puppeteer or Playwright rather than trying to run html2canvas as a native screenshot engine in Node.js.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsExport formats, image size, and cleanup
Why use toBlob()
canvas.toBlob() creates a Blob containing the canvas image. PNG export is required by the browser API; support for other formats depends on the user agent. MDN documents the method and its behavior at HTMLCanvasElement.toBlob(). The example exports PNG. To request another supported format, pass its MIME type, such as image/jpeg, and use a matching file extension.
Rank #4
- Non-destructive photo editing: Manage and edit your RAW photos with this powerful and ultra-fast photo editor
- Simple photo management: With no importing required, conveniently view, store and organize all your photos in one place
- Powerful batch processing: Load, select, edit and output your photos quickly with controls and presets that allow you to edit one or thousands of photos at once
- Time-saving tools: Watermark, correct, remove blemishes and more with integrated tools; image presets automate tedious and repetitive processing work
- Get complete control with a personalized workflow: Customizable workspace, easy integration with photo editors, built-in support, and shareable web galleries
MDN cautions that toDataURL() can have performance costs for large images and can run into URL-length limits. A Blob and object URL are generally a better fit for a downloadable image. The example revokes the object URL after initiating the download; if you observe that a browser cancels the download before it reads the URL, defer revocation briefly or revoke it after your application no longer needs it.
Canvas dimensions and memory
A full-page capture may be very large. Canvas dimension limits depend on the browser and platform, so there is no universal maximum to rely on. An oversized render may be blank, incomplete, or fail to export. If that happens, capture a smaller element, reduce the output scale or dimensions, or split the page into sections. Large canvas images also require substantial memory during rendering and encoding; avoid making multiple full-resolution copies unless the workflow needs them.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If you need a screenshot from a URL rather than a DOM element already on screen, ScreenshotNeo can return an image or PDF from one GET request. It offers PNG, JPEG, or WebP output, and supports JavaScript-capable page capture and watermarking can still be done in your own canvas workflow after you receive the image. See the ScreenshotNeo API documentation for request options.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchconst q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const imageBlob = await res.blob();
const image = await createImageBitmap(imageBlob);
await watermarkImage(image, "Example watermark");
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before the capture by default, with each step configurable. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report page verdict and billing status. Its MCP server lets AI agents use screenshot tools, and the free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month—no card required.
Troubleshooting
- The downloaded file is blank or incomplete: The source may not have finished loading, or the requested canvas dimensions may exceed the browser or platform’s limits. Wait for image load, check the source dimensions, and try a smaller capture or scale.
- Canvas export throws a security error: A cross-origin image may have tainted the canvas. Use an asset served with suitable CORS permission, a same-origin image, or remove the offending resource. Setting
useCORScannot replace server permission. - The screenshot looks different from the page: html2canvas reconstructs DOM and styles rather than copying browser pixels. Try simplifying unsupported content or choose a native browser screenshot API for extension capture.
- No download appears: Call the function in response to a user action where practical, verify the browser allows downloads, and ensure
toBlob()returned a Blob. Keep the object URL alive until the browser has begun handling the link. - The watermark is missing or clipped: Confirm the function receives the resolved source canvas or loaded image, verify the text is nonempty, and check its coordinates, alignment, font size, alpha, and color against the image dimensions.
- The canvas context is unavailable: The function checks for this explicitly. Retry in a browser environment with canvas support and avoid invoking DOM-dependent code in a server-only JavaScript runtime.
Frequently Asked Questions
Can I watermark an image without changing the original file?
Yes. The function draws the source onto a separate destination canvas and exports that canvas, leaving the original source unchanged.
Does the watermark protect an image from being copied?
No. It visibly marks the exported pixels, but it is not encryption or access control.
Can I use a logo instead of text?
Yes. Load the logo as an image and draw it onto the destination canvas with `drawImage()` before export.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




