html2canvas walks the DOM and re-draws what it understands onto a canvas. That design means perpetual gaps: CSS it doesn't support (filters, blend modes, some flexbox/grid edge cases), web fonts that render subtly wrong, cross-origin images that taint the canvas and vanish, and iframes that never appear. The GitHub issues are a museum of 'my screenshot doesn't match my page'.
Screenshotty captures what Chrome actually painted — every property the browser supports, because it is the browser. It also works server-side, so captures don't depend on each visitor's device, browser quirks, or patience.
Compare output on your page — free toolimport html2canvas from "html2canvas";// Re-draws the DOM — fonts, filters, and CORS images may differ or vanishconst canvas = await html2canvas(document.body, { useCORS: true });const dataUrl = canvas.toDataURL("image/png");
// From your backend — render the real URL in real Chromeconst response = await fetch("https://api.screenshotty.link/api/v1/screenshot", {method: "POST",headers: {"X-Api-Key": process.env.SCREENSHOTTY_API_KEY,"Content-Type": "application/json",},body: JSON.stringify({ url: "https://example.com/page-to-capture" }),});const png = Buffer.from(await response.arrayBuffer());
Real Chrome paint output — filters, shadows, blend modes, custom fonts, canvas, and SVG all render exactly as on screen.
Cross-origin images and iframes appear in the capture instead of tainting a canvas or rendering blank.
One rendering environment, identical output every time — not whatever browser and zoom level each user happens to have.
Full-page capture, element capture, PDFs, dark mode, and 7 output formats — things a client-side canvas can't reach.
Because html2canvas re-implements rendering rather than reading the browser's actual paint output. Unsupported CSS, web-font metrics, and cross-origin restrictions all cause drift. A real-browser capture has none of these gaps by design.
Often yes: pass the URL with the user's cookies/headers, or POST the rendered HTML itself to the API. For truly client-only state, capture the data, not the pixels, and re-render it server-side with the html parameter.
Screenshotty starts free (100 screenshots/month, no credit card) and paid plans start at $9/month for 2,500 screenshots with $0.004 pay-as-you-go overage. Self-hosting costs server capacity sized for Chrome's memory spikes plus the engineering time to keep browsers patched, warm, and stable — typically far more than the subscription for any non-trivial volume.
Usually under an hour. Replace your capture function with one HTTP request — the viewport, full-page, wait-condition, and selector options you rely on are all API parameters. Your language guide (Python, Node.js, PHP, Java, Ruby, Go, C#) has copy-paste examples.