Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To make html2canvas output repeatable, make every rendering input explicit and wait for every asynchronous resource before capturing. Fix the viewport, canvas scale, element bounds, scroll offsets, fonts, images, background, and dynamic data; freeze or remove intentionally changing elements in onclone; and export only after the capture promise resolves. This controls the inputs html2canvas can reconstruct, although it cannot promise pixel identity with a browser’s native compositor.
What “consistent” means with html2canvas
html2canvas does not copy the browser’s final composited framebuffer. It reads the DOM, computed styles, and available resources, then paints an approximation into a canvas. The project documentation cautions that “The screenshot is based on the DOM and as such may not be 100% accurate to the real representation, but builds the screenshot based on the information available on the page.” (html2canvas documentation)
That distinction matters for visual regression tests. You can make repeated runs deterministic for the inputs html2canvas sees, but browser-only effects, cross-origin frames, unsupported CSS, and differences between browser or operating-system environments can still produce different pixels.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A deterministic capture you can use as a baseline
The following example waits for fonts and images, fixes geometry and scale, freezes volatile content in the cloned document, ignores known noise, and exports after the promise fulfills.
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
async function waitForImages() {
const images = [...document.images];
await Promise.all(images.map(img => {
if (img.complete) {
return img.decode ? img.decode().catch(() => {}) : Promise.resolve();
}
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
}
async function captureDeterministically() {
await document.fonts.ready;
await waitForImages();
const element = document.querySelector('#capture');
if (!element) throw new Error('Missing #capture element');
const canvas = await html2canvas(element, {
scale: 1,
windowWidth: 1280,
windowHeight: 720,
width: 1280,
height: 720,
x: 0,
y: 0,
scrollX: 0,
scrollY: 0,
backgroundColor: '#ffffff',
useCORS: true,
imageTimeout: 15000,
logging: false,
onclone: clonedDocument => {
clonedDocument.querySelectorAll('[data-volatile]').forEach(el => {
el.textContent = '[frozen]';
});
clonedDocument.querySelectorAll('video').forEach(video => video.pause());
},
ignoreElements: el => el.matches('.clock, .ad, .cursor')
});
const blob = await new Promise((resolve, reject) => {
canvas.toBlob(result => result ? resolve(result) : reject(new Error('toBlob returned null')), 'image/png');
});
return blob;
}
captureDeterministically().then(blob => {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'capture.png';
link.click();
URL.revokeObjectURL(link.href);
});
Set width and height only when you want a fixed capture rectangle. For a naturally sized element, omit them but keep the viewport and scroll values fixed. The option names and defaults are listed in the official configuration reference.
Freeze the geometry that controls layout
Viewport and responsive breakpoints
Media queries can change columns, line wrapping, and hidden elements. Pass the same numeric windowWidth and windowHeight on every run, and run your test in a browser context with the same outer viewport. If a fixed header or sticky control moves when the page scrolls, set both scrollX: 0 and scrollY: 0, or use the exact offsets your test specifies.
Element bounds
Capture the same selector and, when the test requires a known rectangle, provide x, y, width, and height. Avoid calculating these values from a moving page after an animation starts. Record the canvas’s width and height in diagnostics; a dimension change usually indicates a viewport, scale, or bounding-box difference rather than a color mismatch.
Scale and device-pixel ratio
The documented default for scale is window.devicePixelRatio. Two machines with different DPR values therefore produce different canvas dimensions even when CSS pixels match. Use scale: 1 for one CSS pixel per output pixel, or choose another fixed value and use it in every environment. Do not rely on the host machine’s DPR for a regression baseline.
Rank #2
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Wait for fonts and images before cloning
Fonts
Await document.fonts.ready before calling html2canvas, and make sure the intended font files have actually loaded. A fallback font changes glyph widths, line breaks, and element heights. If your application injects a stylesheet or uses a font loader, wait for that loader as well; document.fonts.ready only helps once the relevant font faces are known to the document.
Images
An image can be “complete” while it is still decoding. The example calls decode() when available and resolves both load and error events. Set imageTimeout deliberately; the documented default is 15,000 milliseconds. A late or missing image can alter layout as well as the pixels inside the image box.
External images and CORS
useCORS defaults to false. Set it to true only when the image server sends a suitable Access-Control-Allow-Origin header. If the server cannot provide that header, route the asset through a same-origin proxy you control. Otherwise the image may be skipped or taint the canvas, preventing toDataURL or toBlob from succeeding.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Rank #3
Remove nondeterministic DOM state with onclone
html2canvas clones the document before rendering. Use onclone to alter only that clone, leaving the live page untouched. Replace timestamps, random IDs, live counters, rotating carousel panels, network placeholders, caret styles, and animation classes with fixed values. Freeze videos or replace animated media with a poster image. This is safer than mutating the production DOM just for a test.
onclone: clonedDocument => {
clonedDocument.querySelectorAll('[data-now], [data-random]').forEach(el => {
el.textContent = '2026-01-01T00:00:00Z';
});
clonedDocument.querySelectorAll('.carousel').forEach(el => {
el.classList.add('is-test-slide-1');
});
}
For content that should never appear in a baseline, add data-html2canvas-ignore in markup or use an ignoreElements predicate. Typical exclusions are clocks, ads, cursor indicators, chat launchers, and video overlays.
<span class="clock" data-html2canvas-ignore>12:34:56</span>
const options = {
ignoreElements: element => element.matches('.clock, .ad, .cursor')
};
Make output and diagnostics explicit
Background and export format
The configuration reference documents backgroundColor: '#ffffff' as the default. Set the color explicitly so a future default or stylesheet change cannot alter the baseline. Use backgroundColor: null when transparency is intentional. Call toBlob or toDataURL only after the html2canvas promise resolves; exporting earlier races the renderer.
Rank #4
Logging and error reporting
Keep logging: true while diagnosing missing resources, then disable it in normal runs. Supply the maintained onError hook to record resource failures; html2canvas reports the error and continues rendering, so an apparently successful promise can still contain a missing image or font.
const options = {
logging: true,
onclone: doc => { /* freeze state */ },
onError: error => console.error('html2canvas resource error', error)
};
Compare runs in an order that finds the cause
- Canvas dimensions: compare pixel width and height first. A mismatch points to
scale, viewport, or bounds. - Viewport and scroll: verify
windowWidth,windowHeight,scrollX, andscrollY, plus the actual browser viewport. - Fonts: inspect computed
font-family, loaded font-face files, and text wrapping. - Images: check request status, decode completion, and CORS response headers.
- DOM state: diff timestamps, random values, animation classes, carousel indexes, and network-populated fields in the cloned document.
- Environment: record browser version, operating system, device-pixel ratio, and color settings. Keep these fixed for pixel-level tests.
Common failures and precise fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Different dimensions on each machine | Implicit device-pixel ratio or responsive breakpoint | Set a fixed scale, windowWidth, and windowHeight; verify the real viewport. |
| Text wraps differently | Fallback or late-loading web font | Await document.fonts.ready, verify font requests, and use the same browser/font files. |
| Images are blank or missing | Late decode, timeout, or cross-origin restrictions | Await load/decode, set imageTimeout, enable useCORS only with valid headers, or use a same-origin proxy. |
toDataURL or toBlob throws a security error |
Tainted canvas from an external image | Serve the image with the required CORS header or proxy it through your origin. |
| A clock, ad, or cursor changes pixels | Intentionally volatile content is included | Mark it data-html2canvas-ignore, filter it with ignoreElements, or replace it in onclone. |
| Capture differs after scrolling | Sticky/fixed elements and nonzero scroll offsets | Set stable scrollX/scrollY and capture from a fixed viewport state. |
| Cross-origin iframe is empty | Browser same-origin policy blocks its contentDocument |
Render the frame from its own origin or use a native browser screenshot service; html2canvas cannot bypass this boundary. |
Performance and reliability trade-offs
- Waiting costs time: font readiness, image decoding, and a deliberate image timeout reduce race conditions but increase capture latency. Apply the waits once per page state rather than before every small element.
- Fixed scale costs memory: higher scales multiply canvas pixels. Use the smallest fixed scale that meets your comparison requirement.
- Full-page captures are heavier: long pages require more layout and canvas memory. Capture a stable component when the test does not need the entire document.
- Deterministic data beats pixel masking: replacing a timestamp in
onclonepreserves layout better than blurring it after export. - Native screenshots have a different boundary: when exact compositor output, cross-origin frames, or browser-only effects are mandatory, use a native browser screenshot API instead of treating html2canvas as a guarantee.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A single request returns PNG, JPEG, WebP, or PDF, while its capture pipeline accepts consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. You can turn each cleanup step off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing result.
Use the same URL with cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the complete option list and parameter details in the ScreenshotNeo documentation. It supports fixed viewports and 12 device presets, retina scale, full-page lazy-image loading, CSS-selector element capture, dark mode, custom CSS and JavaScript, click and wait conditions, blocked requests or resource types, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, PDF controls, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Existing screenshot-API parameter names also work, easing migration.
Plans include 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Best Value
- JavaScript Jquery
- Introduces core programming concepts in JavaScript and jQuery
- Uses clear descriptions, inspiring examples, and easy-to-follow diagrams
FAQ
Can html2canvas guarantee identical pixels?
No. It can stabilize the DOM inputs it reconstructs, but browser engines, fonts, unsupported CSS, cross-origin frames, and compositor effects can still differ. Use a native browser screenshot for compositor-level identity.
Should I leave useCORS enabled?
Enable it only when the image origin sends a compatible Access-Control-Allow-Origin header. Otherwise use a same-origin proxy; the option cannot override server or browser security policy.
What should a regression test store besides the image?
Store canvas dimensions, viewport and scroll values, browser and DPR, font-load status, image request results, and the frozen-state inputs. Those records identify environmental drift faster than a pixel diff alone.
Frequently Asked Questions
Can html2canvas guarantee identical pixels?
No. It can stabilize the DOM inputs it reconstructs, but browser engines, fonts, unsupported CSS, cross-origin frames, and compositor effects can still differ. Use a native browser screenshot for compositor-level identity.
Should I leave useCORS enabled?
Enable it only when the image origin sends a compatible Access-Control-Allow-Origin header. Otherwise use a same-origin proxy; the option cannot override server or browser security policy.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →What should a regression test store besides the image?
Store canvas dimensions, viewport and scroll values, browser and DPR, font-load status, image request results, and the frozen-state inputs. Those records identify environmental drift faster than a pixel diff alone.
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.

