Free tools Windows power users keep installed
One-click scans. No signup required.
Use Playwright’s page.screenshot() to capture a webpage as a PNG in TypeScript. Set a deliberate viewport, navigate to the page, and either provide a file path to save the image or omit it to receive PNG bytes as a Node.js Buffer. Use fullPage: true for the entire scrollable document, or call screenshot on a locator to capture one element.
Capture a webpage as a PNG with Playwright
This Node.js example opens Chromium, navigates to a page, saves a PNG, and closes the browser even if navigation or capture fails. It uses Playwright’s documented networkidle navigation wait; for pages that keep network requests open, choose another readiness check as described below.
import { chromium } from 'playwright';
async function main() {
const browser = await chromium.launch();
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
});
await page.goto('https://example.com', {
waitUntil: 'networkidle',
});
await page.screenshot({
path: 'page.png',
type: 'png',
});
} finally {
await browser.close();
}
}
main();
Install Playwright in a Node.js project with npm install playwright, then install its Chromium browser with npx playwright install chromium. Save the example in a TypeScript file such as capture.ts. To execute TypeScript directly, use a TypeScript runner already configured in your project; alternatively, compile it with your TypeScript build setup and run the resulting JavaScript with Node.js. Playwright’s exact setup may vary with your project’s module configuration.
The output path is relative to the process’s current working directory. Change page.png to an absolute path or a path such as screenshots/page.png if you want a different destination; create the destination directory first if it does not exist.
#1 Best Overall
Get the PNG as a Buffer instead of writing a file
page.screenshot() returns a Promise<Buffer>. When you omit path, the resolved value contains the image bytes, ready for upload, storage, or image processing:
const pngBytes = await page.screenshot({ type: 'png' });
// pngBytes is a Node.js Buffer
For example, within the earlier try block you can replace the path-based capture with the following file write:
import { writeFile } from 'node:fs/promises';
const pngBytes = await page.screenshot({ type: 'png' });
await writeFile('page.png', pngBytes);
Choose one approach if you only need one output: passing path lets Playwright write the screenshot, while omitting it gives your code the bytes to handle. Avoid converting a Buffer to base64 unless the destination specifically requires a base64 string; the Buffer is already usable as binary data.
Choose the capture scope: full page or one element
A viewport screenshot captures what is visible in the current page viewport. For a long article or document, request a full-page image. For a component such as an invoice, card, or chart, take a locator screenshot instead.
Crashes, 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 minutePC 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 & 11Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Capture the entire scrollable document
await page.screenshot({
path: 'full-page.png',
fullPage: true,
type: 'png',
});
fullPage: true captures the full scrollable page rather than only the visible viewport. It can produce a much taller image, so use it when the complete document is the intended deliverable, not just to make a viewport capture “better.” Pages with content that loads only as the user scrolls may need additional preparation before capture; a full-page option alone is not a guarantee that every lazy-loaded asset has appeared.
Capture a selected element
const invoice = page.locator('.invoice');
await invoice.screenshot({
path: 'invoice.png',
type: 'png',
});
Replace .invoice with a CSS selector for the target. The locator must resolve to the intended element, and that element must be present and visible when the screenshot is taken. If the page has multiple matching elements, make the selector more specific or select the intended match before capturing. Element capture and fullPage solve different problems: one isolates a target element; the other covers the document.
Control dimensions, image type, and readiness
Set viewport and pixel scale intentionally
The viewport in browser.newPage() determines the browser’s CSS layout dimensions. Pick fixed dimensions when repeatability matters; otherwise, a page may wrap text or rearrange responsive components differently from the screenshot you expect.
Playwright’s screenshot scale option controls output pixel density. scale: 'css' produces one output pixel per CSS pixel. scale: 'device' uses device pixels and can create a larger, higher-DPI image. Use CSS scale when stable CSS-pixel dimensions are the priority, and device scale when extra pixel detail is more important.
await page.screenshot({
path: 'page.png',
type: 'png',
scale: 'css',
});
Wait for the page state you actually need
Navigation completion and application readiness are not always the same thing. waitUntil: 'networkidle' is a useful choice for pages whose important resources finish loading after the initial response, but some applications continuously make requests. In that case, waiting for the network to become idle can time out or delay the capture. Prefer a condition tied to the page’s own ready state, such as waiting for an element that indicates the content you need has rendered.
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
});
await page.locator('main').waitFor({ state: 'visible' });
await page.screenshot({ path: 'page.png', type: 'png' });
The locator in this example is illustrative: replace main with a reliable selector for the content in your target site. A fixed delay can be useful when a known animation or delayed widget must finish, but it is less robust than waiting for a meaningful condition because the right duration can vary between runs.
Other capture controls
Playwright also supports PNG, JPEG, and WebP output; PNG is the default when no other type is inferred. For PNG captures, set type: 'png' explicitly when you want the output format to be clear in code. The screenshot API also provides a style option for applying a stylesheet during capture and a timeout option for limiting how long the screenshot operation may wait. Consult the Playwright API documentation for the version installed in your project when adding options beyond these examples.
Make repeated captures more reliable
A successful capture is not necessarily a repeatable one. Dynamic content, animation, and the browser environment can change pixels between runs. For visual-test baselines, keep the capture conditions controlled rather than treating every difference as an application regression.
Recommended Free Tools
- Fix the viewport. Use the same width and height for baseline and comparison captures.
- Wait for meaningful readiness. Prefer a page-specific selector or state over a generic wait when the application has a known rendering milestone.
- Choose the scope deliberately. Use viewport, full-page, or element capture according to the comparison you want.
- Reduce transient visual changes. Disable or mask animations and dynamic content where appropriate. Playwright screenshot options and visual comparison tools can help manage these cases.
- Keep the environment stable. Playwright’s visual-comparison documentation warns that rendered screenshots can vary with operating system, browser version, settings, hardware, power source, and headless mode.
- Close the browser in cleanup code. A
finallyblock prevents a failed navigation or screenshot from leaving the browser process open.
For a project already using Playwright Test, expect(page).toHaveScreenshot() is the visual-regression route; PNG is its default snapshot format. Generate and compare baselines in a controlled environment, and investigate environment changes before treating pixel differences as application changes.
Or skip the browser setup
If you need a screenshot without installing and managing a local browser, ScreenshotNeo offers a one-request screenshot API and an MCP server. Its API accepts a URL and returns an image or PDF; the following Node.js example requests a WebP image using the provided API pattern:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
For API setup and options, see the ScreenshotNeo documentation. Its clean-shot options accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; you can turn each step off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether the request was billed. The MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
The free plan includes 1,000 screenshots per month with no card required. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free. Every listed feature is available on every plan. Sign up for ScreenshotNeo’s free plan to try it without a card.
When to use Playwright, Puppeteer, or a screenshot API
| Approach | Best fit | What to account for |
|---|---|---|
| Playwright | TypeScript browser automation, page and element captures, and projects using Playwright Test. | You manage browser installation and execution; consistent visual comparisons depend on a controlled environment. |
| Puppeteer | Projects already built around Puppeteer and its browser automation APIs. | It supports file and byte-returning screenshot use cases; choose waits and capture scope deliberately. |
| ScreenshotNeo API or MCP server | Captures through a hosted endpoint, or screenshot actions initiated by an MCP-capable AI client. | Use an API key for API requests and check the response headers to determine the page verdict and billing status. |
Both Playwright and Puppeteer can save PNG files or provide screenshot bytes, and both support full-page and element capture workflows. Choose the library that matches the automation stack already in your application. Playwright documents Chromium, Firefox, and WebKit in its examples; the examples here use Chromium. A hosted API or MCP server is an alternative when you want to avoid local browser setup, not a reason to change an existing test stack that already meets your needs.
Best Value
Puppeteer equivalent
If your TypeScript project uses Puppeteer, the core sequence is similar. This example waits with Puppeteer’s networkidle2 policy and saves a full-page PNG:
import puppeteer from 'puppeteer';
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', {
waitUntil: 'networkidle2',
});
await page.screenshot({
path: 'page.png',
type: 'png',
fullPage: true,
});
} finally {
await browser.close();
}
}
main();
Puppeteer’s screenshot API also has a byte-returning overload, and its encoding: 'base64' option provides a base64 string. Its guide also demonstrates capturing an element with ElementHandle.screenshot(). Use the API style appropriate to the Puppeteer version and TypeScript types in your project.
Troubleshoot common capture failures
The screenshot is empty, incomplete, or shows a loading state
- Likely cause: The capture ran before the application rendered the desired content, or the chosen navigation wait does not represent the page’s actual ready state.
- Fix: Wait for a page-specific visible element or application readiness condition before calling screenshot. If the site continuously sends requests, try
domcontentloadedfollowed by a meaningful selector wait rather than relying onnetworkidle.
The capture hangs or navigation times out
- Likely cause: A network-idle condition never occurs, a resource is slow, or a screenshot operation exceeds its configured timeout.
- Fix: Use a navigation policy appropriate to the page, then wait for the specific content required. Review the screenshot operation’s
timeoutsetting if the capture itself is the slow step; do not assume a longer timeout fixes an unending readiness condition.
The image has the wrong size or the page layout differs
- Likely cause: The viewport or pixel scale differs from the intended output, or responsive layout changed at the selected dimensions.
- Fix: Set the viewport explicitly and select
scale: 'css'for one output pixel per CSS pixel, orscale: 'device'for device-pixel output. Confirm whether you intended viewport or full-page capture.
The element screenshot fails or captures the wrong content
- Likely cause: The selector matches no visible element, is ambiguous, or identifies a different element than intended.
- Fix: Wait for the target locator to become visible and make its selector specific enough to identify the intended component before calling
locator.screenshot().
Visual tests change despite no deliberate UI change
- Likely cause: Operating system, browser version, settings, hardware, power source, or headless mode changed; animation or dynamic content may also be different.
- Fix: Generate and compare baselines under controlled conditions, and disable or mask transient content where suitable. Check environment changes alongside application changes.
The browser remains running after an error
- Likely cause: Browser cleanup only occurs after successful navigation and capture.
- Fix: Put browser work inside
tryand callbrowser.close()infinally, as in the examples.
Quick decision guide
- For a file on disk, pass
path: 'page.png'. - For bytes to upload or process, omit
pathand use the returned Buffer. - For everything in the scrollable document, add
fullPage: true. - For one component, call screenshot on a locator or element handle.
- For repeatable results, fix viewport, wait on meaningful page readiness, and control the test environment.
- For browser automation already centered on Playwright or Puppeteer, keep the capture in that stack; use a hosted API or MCP server when avoiding browser management is more important.
Frequently Asked Questions
Can I use the screenshot Buffer directly in an HTTP upload?
Yes. It is binary PNG data, so pass the Buffer through the receiving library’s binary-body or multipart-file interface rather than encoding it as text.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsDoes TypeScript change the screenshot API?
No. TypeScript calls the same Playwright or Puppeteer APIs as JavaScript; TypeScript adds compile-time checking and may require project-specific module or runner configuration.
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.




