Use Playwright or Puppeteer when you need a faithful screenshot of a rendered page; use html2canvas when JavaScript runs inside the page and you want to export a DOM element. Playwright and Puppeteer control a real browser, support full-page and element captures, and can return image bytes. html2canvas runs in the user’s browser and reconstructs an image from DOM information and styles, so it is convenient but not identical to a native browser screenshot.
Choose the right JavaScript approach
| Approach | Runs where | Best for | Important limitation |
|---|---|---|---|
| Playwright | Node.js or another server-side runtime | High-fidelity automation, visual tests, full-page or element files | Requires a browser installation and automation setup |
| Puppeteer | Node.js or another server-side runtime | Chrome-oriented automation, scripted captures and buffers | Requires a browser installation and automation setup |
| html2canvas | Inside the web page | An “Export this card/report” button for same-origin content | Rebuilds pixels from DOM and styles; cross-origin assets and iframes are restricted |
For a production screenshot service, a real browser is usually the safer default because it executes layout, fonts, JavaScript, lazy loading and responsive CSS as a visitor would see them. For a browser-only export feature, html2canvas avoids a server and can turn a selected element into a downloadable PNG.
Take a screenshot with Playwright
Install and create a full-page PNG
In a new Node.js project, install Playwright:
npm install playwright
npx playwright install chromium
Save this as screenshot.mjs and run it with node screenshot.mjs:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1
});
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 beyond the viewport to the page’s scrollable height. Without it, the output is only the visible viewport. If you omit path, page.screenshot() returns image data instead of writing a file:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute#1 Best Overall
const imageBytes = await page.screenshot({ fullPage: true, type: 'webp', quality: 85 });
// Store imageBytes in object storage, return it from an HTTP response, or process it further.
Capture one HTML element
Use a locator when you need only a card, invoice or chart:
const invoice = page.locator('.invoice');
await invoice.waitFor({ state: 'visible' });
await invoice.screenshot({ path: 'invoice.png', type: 'png' });
The locator screenshot automatically uses the element’s bounding box. Waiting for visibility avoids an empty or partially rendered result.
Control rendering before capture
- Wait for application data: wait for a meaningful selector, such as
await page.locator('.dashboard-loaded').waitFor();, rather than relying only on a fixed sleep. - Wait for fonts:
await page.evaluate(() => document.fonts.ready);helps prevent fallback-font screenshots. - Set a stable viewport: use the same width, height and device scale factor for repeatable output.
- Hide transient UI: inject CSS or hide selectors for cookie prompts, animations and timestamps that should not appear in a test image.
- Use a dark theme or locale: set the page context and browser preferences before navigation when the page changes with color scheme or language.
await page.emulateMedia({ colorScheme: 'dark' });
await page.addStyleTag({ content: `*, *::before, *::after { animation: none !important; transition: none !important; }` });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('main').waitFor({ state: 'visible' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'dark-page.png', fullPage: true });
JPEG, WebP and quality
PNG preserves sharp text and transparency. JPEG is smaller for photographic pages but does not support transparency. WebP often reduces size while retaining good quality. Playwright accepts type: 'png', type: 'jpeg' or type: 'webp'; the quality option applies to lossy formats.
Take a screenshot with Puppeteer
Install and capture the rendered page
npm install puppeteer
Save as puppeteer-shot.mjs:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'page.png', fullPage: true, type: 'png' });
await browser.close();
networkidle2 waits until there are no more than two active network connections, which is useful for pages that fetch data after navigation. It is not a guarantee that every application has finished rendering, so also wait for an application-specific selector when possible.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #2
Capture an element or return bytes
const element = await page.$('.invoice');
if (!element) throw new Error('Invoice element was not found');
await element.screenshot({ path: 'invoice.png' });
const buffer = await page.screenshot({ fullPage: true, encoding: 'binary' });
Puppeteer can return screenshot data for storage or an HTTP response instead of writing a local file. Close the browser in a finally block in long-running services so failed jobs do not leak browser processes.
Use html2canvas in the browser
Export an element from a page
html2canvas reads the selected element’s DOM and computed styles, paints a canvas, and lets the user download the result. This example loads the module from a public CDN:
<script type="module">
import html2canvas from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
const element = document.querySelector('#capture');
if (!element) throw new Error('Capture target was not found');
const canvas = await html2canvas(element, {
backgroundColor: '#fff',
scale: window.devicePixelRatio
});
const blob = await new Promise(resolve =>
canvas.toBlob(resolve, 'image/png')
);
if (!blob) throw new Error('The browser could not create an image blob');
const link = document.createElement('a');
link.download = 'capture.png';
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);
</script>
Give the target a fixed background when transparency is not wanted. A higher scale produces a sharper image but consumes more memory; very large pages can exceed canvas or device-memory limits.
What html2canvas can and cannot reproduce
- It is not a browser compositor screenshot. The library infers appearance from DOM nodes and styles, so unusual CSS, browser UI, plug-ins and some visual effects may differ.
- Images, fonts and other resources from another origin must permit cross-origin use or be served through a same-origin proxy. Otherwise the canvas can become tainted and export operations fail.
- Cross-origin iframes cannot be read because browser security prevents access to their
contentDocument. Capture the framed page separately or move the content to a permitted origin. - It captures the selected DOM subtree. To include content below the viewport, ensure the element has the required dimensions and use the library’s sizing options rather than assuming it will behave like a browser full-page capture.
Full-page versus element screenshots
Choose full-page capture when
- You are archiving an article, landing page or invoice that extends below the fold.
- You are running visual regression tests across the complete document.
- You need the browser’s actual layout, including lazy-loaded images and responsive breakpoints.
Choose element capture when
- A user needs to export a chart, receipt, profile card or report section.
- The surrounding navigation, cookie notice or unrelated content should not be included.
- You want a smaller file and a predictable canvas area.
Reliable capture workflow
- Define the output: decide PNG, JPEG, WebP or PDF; viewport dimensions; full page or a selector; and whether transparency is required.
- Load the page: navigate with Playwright or Puppeteer and set a deterministic viewport, locale, color scheme and user agent when those affect layout.
- Wait for readiness: combine navigation waiting with a visible application selector, completed fonts and any required API-driven state.
- Stabilize visuals: disable animations, freeze rotating content and hide elements that should not be part of the image.
- Capture and validate: check that the output exists, has nonzero dimensions and contains the expected selector or page region.
- Release resources: close pages and browsers, and enforce a timeout so a stalled site cannot occupy a worker indefinitely.
Troubleshooting common failures
The screenshot is blank or too short
The page may still be loading data, the selector may be hidden, or the browser may have navigated to an error page. Wait for a visible, content-specific selector, log the final URL and response status, and verify the selector before calling screenshot(). Use fullPage: true for below-the-fold content.
Images or fonts are missing
Check that requests completed before capture and that remote assets allow access from the rendering origin. In browser automation, wait for document.fonts.ready and for key images to report completion. With html2canvas, configure CORS or use a same-origin proxy; otherwise the canvas may be tainted.
A cross-origin iframe is absent
html2canvas cannot inspect a cross-origin iframe. Render the iframe’s page in its own permitted context, request a server-side image from that service, or redesign the component so the content is same-origin.
The output differs between runs
Animations, rotating ads, timestamps, random data, responsive breakpoints and late API responses cause drift. Fix the viewport and scale, disable animation, provide deterministic test data, wait for a stable selector and capture at a consistent time.
The browser process hangs or jobs run out of memory
Set navigation and overall job timeouts, close every page and browser in cleanup code, limit concurrency, and avoid creating enormous canvases. Reuse a controlled browser process for batches while isolating each page context.
Rank #4
Playwright or Puppeteer cannot launch
The required browser binary may not be installed, or a restricted server may lack sandbox permissions and shared libraries. Run the package’s browser installation command during deployment, use the documented container dependencies, and inspect the launch error before changing flags. Do not disable browser security broadly unless the deployment environment requires it and is isolated.
Performance, reliability and cost considerations
- Browser startup: launching Chromium for every URL is slower than keeping a bounded browser pool. Reuse the process, but create a fresh page or context for isolation.
- Page weight: full-page images and high device scale factors increase memory and transfer size. Use element capture or WebP when the workflow does not require lossless output.
- Dynamic content: a network-idle event can take a long time on pages with analytics or streaming connections. Prefer a meaningful readiness selector plus a maximum timeout.
- Security: treat target URLs and custom headers as untrusted input. Restrict outbound access in a screenshot service to reduce server-side request forgery risk, and never log authentication cookies or authorization headers.
- Repeatability: record URL, viewport, browser version, wait condition and capture options with each artifact so visual differences can be diagnosed.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One request returns a PNG, JPEG, WebP or PDF, without maintaining Playwright or Puppeteer workers. Its cleanup step accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before the capture; each step can be disabled.
Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers report the page verdict and whether the request was billed. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
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,
)
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 body = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', body));
See the ScreenshotNeo documentation for request options. Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Every plan includes the available features, including full-page and element capture, device presets, custom CSS and JavaScript, waiting rules, request blocking, headers and cookies, signed links, asynchronous jobs, bulk capture and PDF controls. Create a free ScreenshotNeo account to get started.
Recommended Free Tools
FAQ
Can JavaScript capture a screenshot without a server?
Yes. html2canvas can export same-origin DOM content directly in the browser. It is appropriate for user-triggered component exports, not for guaranteed pixel-for-pixel browser rendering.
Best Value
Which library is better for visual regression tests?
Playwright or Puppeteer is the better fit because each drives a real browser and supports controlled viewport, navigation, full-page and element captures.
Does fullPage: true include content loaded after scrolling?
It captures the document’s full scrollable layout, but lazy content still needs to be triggered or awaited. Make sure images and application data are ready before capture.
Can I screenshot a page that requires authentication?
Browser automation can use an authenticated context, cookies or headers when you are authorized to access the page. Protect those credentials and avoid exposing them in logs or client-side code.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteQuick 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.




