Use html2canvas for a browser-side download, or Playwright when you need a faithful, repeatable browser render. html2canvas reconstructs an element from the DOM and exports a canvas as PNG; Playwright opens the page in a real browser and captures the rendered pixels. The first is easy to add to a web page, while the second is the safer choice for full pages, complex CSS, web fonts, CI jobs, and server-side APIs.
Choose the right conversion method
| Requirement | Best fit | Reason |
|---|---|---|
| A Download PNG button inside your web app | html2canvas | Runs in the visitor’s browser and returns a canvas directly. |
| Pixel-faithful rendering of modern CSS | Playwright | Uses a real Chromium, Firefox, or WebKit page rather than rebuilding the DOM. |
| Whole scrollable page | Playwright | Supports fullPage: true and controlled viewport dimensions. |
| One component such as an invoice | Either | html2canvas accepts an element; Playwright can capture a locator. |
| Server, CI, visual regression, or an API | Playwright | Runs in Node.js and can return PNG bytes without writing a file. |
| Cross-origin images or iframes | Usually Playwright | Canvas security can prevent html2canvas from reading foreign resources. |
PNG preserves lossless color and can include transparency. The pixels still depend on the page’s computed background: set an explicit background when a transparent or unexpected result would be confusing.
Convert an HTML element in the browser with html2canvas
html2canvas traverses the selected DOM subtree and paints a canvas representation. It is not a literal screenshot, so unsupported CSS, inaccessible iframe content, or cross-origin images can differ from what the browser visibly shows.
Complete download-button example
<div id="capture" style="padding:16px;background:#f5da55;color:#111">
<h2>Color PNG export</h2>
<p>This element will become a PNG.</p>
</div>
<button id="save">Download PNG</button>
<script type="module">
import html2canvas from '@html2canvas/html2canvas';
document.querySelector('#save').addEventListener('click', async () => {
const canvas = await html2canvas(document.querySelector('#capture'), {
scale: window.devicePixelRatio
});
const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
</script>
Install the package with your normal JavaScript package manager, then run this as a module in an evergreen browser. The scale setting uses the device pixel ratio so a high-density display produces a sharper image; very large values also increase memory use and PNG size.
Recommended Free Tools
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Capture a specific region and omit controls
Pass x, y, width, and height to crop the render. Set data-html2canvas-ignore on a button, toolbar, or other element that should not appear:
<button data-html2canvas-ignore>Edit</button>
<script type="module">
const canvas = await html2canvas(document.querySelector('#capture'), {
x: 0,
y: 0,
width: 800,
height: 500,
scale: 2,
useCORS: true
});
const pngUrl = canvas.toDataURL('image/png');
</script>
useCORS: true only helps when the image server sends an appropriate CORS header and the resource is loaded in a CORS-compatible way. It cannot bypass another site’s security policy. Wait until the target has its final dimensions, fonts, and images before calling html2canvas; otherwise the canvas records the intermediate layout.
Why the result may not match the page
- The library bases its output on DOM information rather than taking an actual browser screenshot, so CSS it does not support can render differently.
- Cross-origin images can taint the canvas, causing
toDataURL()to fail or preventing readable pixel output. - Cross-origin iframe documents are inaccessible to the page and cannot be rendered into the canvas.
- The library runs in the browser; it is not, by itself, a Node.js screenshot engine.
Capture a color PNG with Playwright
Playwright launches a real browser, waits for navigation, and captures the rendered page. This is the dependable route when layout fidelity matters.
Install and capture a full page
npm install playwright
npx playwright install chromium
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'page.png', fullPage: true, type: 'png' });
await browser.close();
fullPage: true extends the capture over the entire scrollable document. Omit it for the visible viewport. PNG is lossless; Playwright also supports JPEG and WebP when a smaller or lossy file is preferable.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Rank #2
Capture one element
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.locator('#invoice').screenshot({ path: 'invoice.png', type: 'png' });
await browser.close();
A locator screenshot clips to the element’s bounding box. You can similarly select a header, chart, or card with a CSS selector.
Return PNG bytes instead of a file
const pngBuffer = await page.screenshot({ type: 'png' });
// Send pngBuffer from an API response, store it, or pass it to image processing.
This buffer form is useful for HTTP endpoints and pixel-diff pipelines. Set the viewport explicitly for reproducible output; CSS pixels and device pixels differ when you change device scale factor.
Make dynamic pages deterministic
- Navigate with a condition suited to the site, such as
domcontentloadedornetworkidle; network idle is not a guarantee that application data is complete. - Wait for a meaningful selector, for example
await page.locator('#invoice').waitFor()or an assertion that the content is visible. - Wait for fonts and images when they affect layout. In page code,
await page.evaluate(() => document.fonts.ready)can ensure web fonts have finished loading. - Use a fixed viewport, locale, timezone, color scheme, and device scale factor when screenshots are compared over time.
- Set a background deliberately with page CSS or a capture option so transparent areas do not become accidental black or white regions.
Color, size, and output controls
Background and transparency
PNG supports alpha, but the page’s computed background determines what is actually painted. Add a solid background to the capture element for invoices, cards, and social graphics. Keep transparency only when a downstream compositor needs it.
Resolution and scaling
For html2canvas, scale controls output density. For Playwright, the viewport controls layout width; device scale factor controls physical pixel density. Increasing either can multiply memory use. A very tall full-page capture may exceed browser or image-decoder limits, so split long documents into sections when necessary.
Rank #3
Fonts, lazy images, and animations
Capture after web fonts resolve and lazy-loaded images enter the viewport. Disable or freeze animations for repeatable output, and provide stable data rather than capturing while a chart or carousel is changing.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, while options cover full-page and element capture, viewport and device presets, retina scale, dark mode, custom CSS or JavaScript, clicks, selector or network-idle waits, lazy images, request blocking, headers, cookies, user agents, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous webhooks, bulk capture, and usage reporting. Its API accepts the parameter names used by other screenshot services, which can simplify a switch.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for the available parameters and response headers. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers.
It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try the API.
Equivalent API calls in Python and Node.js
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting
html2canvas throws a security or tainted-canvas error
Find images loaded from another origin, including CSS background images and iframe content. Host them with CORS headers, proxy them through your own origin, or use Playwright, which captures the browser surface rather than reading a canvas.
Rank #4
- 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
Fonts or images are missing
Trigger the conversion only after the target is visible and assets have loaded. In Playwright, wait for a selector and the font promise; for lazy images, scroll or use a capture service option that loads them before capture.
Full-page output is clipped
Use Playwright’s fullPage: true, ensure the document has finished expanding, and check for fixed-position overlays. For extremely long pages, capture logical sections and join them in an image-processing step.
Colors look wrong or the background is unexpected
Inspect the computed background and color scheme. Set an explicit background, select the intended light or dark mode, and avoid capturing while a theme transition is running.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Playwright times out
Check DNS, authentication, robots or bot challenges, and the selected wait condition. Increase the navigation timeout only after confirming the page eventually loads; waiting for permanent analytics requests can make networkidle unsuitable.
Best Value
The output is too large
Reduce viewport or scale, capture an element rather than the whole page, or choose WebP/JPEG where lossless PNG is unnecessary. Keep PNG for text, diagrams, and graphics that need exact color edges.
Which approach should you use?
- Choose html2canvas for a user-initiated, same-page export where approximate DOM fidelity and a simple download matter.
- Choose Playwright for server-side work, full pages, cross-origin resources, complex CSS, repeatable screenshots, and visual testing.
- Choose ScreenshotNeo when you want a hosted call instead of maintaining browsers, especially when consent banners, popups, bot failures, billing behavior, or AI-agent access matter.
Frequently Asked Questions
Does converting HTML to PNG preserve selectable text?
No. PNG is a raster image; text becomes pixels. Keep the original HTML or create a PDF as well when users need searchable or selectable content.
Can I capture an HTML string that is not hosted at a URL?
Yes with Playwright: create a page, call page.setContent(html), wait for fonts and images, then call page.screenshot({ type: 'png' }). html2canvas requires the markup to exist in a browser document.
Should I use PNG, JPEG, or WebP?
Use PNG for crisp text, UI, and diagrams; JPEG for photographic images where some loss is acceptable; WebP when you need smaller files and your consumers support it.
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.

