Recommended Free Tools
Use page.screenshot() when you need one rendered image and page.record() when you need the page’s activity saved over time. For a selected component, call elementHandle.screenshot(). A reliable workflow is to launch or connect to Chrome, navigate to the page, wait for the state you want, capture a still or start a recording, perform the interactions, stop the recorder, and only then close the browser.
This guide covers still images, full-page and clipped captures, video recording, timing issues, common failures, and an API alternative when maintaining a browser setup is unnecessary.
Choose between a screenshot and a recording
| Need | Puppeteer API | Result | Important detail |
|---|---|---|---|
| Preserve one visual state | Page.screenshot() |
Image bytes or a file such as PNG, JPEG or WebP | Wait until the exact visual state you want is rendered. |
| Capture one component | ElementHandle.screenshot() |
An image of the selected element | The element is scrolled into view; a detached element causes an error. |
| Preserve activity over time | Page.record() |
Documented as an MP4 video stream | Call stop() on the returned recording object before closing the browser. |
A screenshot is a single rendered state, not a timeline. If a page contains a video, an animation or changing data, the screenshot shows only the frame visible when the method runs. Use recording when the motion itself matters. Puppeteer’s current reference describes Page.record() as using Chrome DevTools Protocol’s Page.startScreenRecording mechanism.
The older Page.screencast() API is marked obsolete in the documentation; new code should use Page.record() instead. The screencast page says, “Use Page.record() instead.”
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
Prerequisites and a minimal project
Install Puppeteer in a Node.js project. The package normally downloads a compatible browser during installation; if your project uses a separately installed Chrome or Chromium, launch it with the appropriate executable path and check that the browser and Puppeteer versions support the APIs you call.
mkdir puppeteer-capture
cd puppeteer-capture
npm init -y
npm install puppeteer
Create a JavaScript file and run it with node filename.js. The examples below use modern async functions and write files in the current directory.
Take a normal page screenshot
The page-level method is the simplest answer to “How do I take a screenshot of a page with Puppeteer?” Navigate first, then capture.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'page.png' });
} finally {
await browser.close();
}
})();
networkidle2 waits for a low level of network activity. It is useful for ordinary pages, but it does not prove that a video has buffered, that a canvas animation has reached a particular frame, or that a web app has finished all visual work. Add a selector wait, an explicit delay, or an application-specific readiness signal when those conditions matter.
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 & 11Return image bytes instead of writing a file
const image = await page.screenshot({ type: 'png' });
// image is a Buffer; send it to storage, an HTTP response, or another process.
When path is supplied, Puppeteer writes the file. Without it, the method returns image data. Select the output format with type: 'png', type: 'jpeg', or type: 'webp' where supported by your installed release; JPEG and WebP options can include a quality value.
Rank #2
Capture an element, full page, or clipped region
Capture one element
const card = await page.waitForSelector('.product-card');
if (!card) throw new Error('Product card was not found');
await card.screenshot({ path: 'product-card.png' });
The element screenshot method scrolls the target into view when necessary. If a framework replaces the node between waitForSelector and screenshot, the handle may be detached. Re-select the element immediately before capture, or use a stable locator strategy in your installed Puppeteer version.
Capture the whole document
await page.screenshot({
path: 'full-page.png',
fullPage: true
});
Full-page capture is useful for long documents, but it can expose layout that appears only while scrolling and may interact oddly with sticky headers or virtualized lists. Confirm the exact behavior against the Puppeteer release installed in your project.
Capture a rectangle
await page.screenshot({
path: 'hero.png',
clip: { x: 0, y: 0, width: 1200, height: 700 }
});
The screenshot options reference also lists encoding, omitBackground, and captureBeyondViewport. That options page is published under a “next” reference, so verify option names and support in your installed version before making them part of a production interface. The API reference for Page.screenshot() is the authoritative place to check your release.
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 →Make a page with video ready for a still image
If the requirement is a still image of a page that contains video, decide which frame you want and wait for that condition. A fixed delay is easy but fragile; a page-controlled event or selector is better.
await page.goto('https://example.com/video-demo', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('video');
await page.evaluate(() => new Promise(resolve => {
const video = document.querySelector('video');
if (!video) return resolve();
if (video.readyState >= 2) return resolve();
video.addEventListener('loadeddata', resolve, { once: true });
}));
// Optional: allow the desired frame to appear.
await new Promise(resolve => setTimeout(resolve, 1000));
await page.screenshot({ path: 'video-page.png' });
Autoplay policies can prevent playback, especially when audio is enabled. If your test owns the page, configure the page or browser context for the behavior you need and do not assume that a loaded video is playing. A screenshot cannot capture a video frame that has not been painted yet.
Record page activity as MP4 video
The documented recording flow is: navigate, start the recorder, perform actions or wait through the animation, stop the recorder, then close the browser.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const recorder = await page.record({ path: 'recording.mp4' });
try {
await page.click('button.start-demo');
await page.waitForSelector('.demo-complete');
} finally {
await recorder.stop();
}
} finally {
await browser.close();
}
})();
The Page.record() reference states that it “Outputs mp4 video stream.” Always stop the returned object, including on an error path; closing the page first can leave an incomplete file. If the interaction has no completion selector, wait for a known duration and stop explicitly:
const recorder = await page.record({ path: 'animation.mp4' });
await new Promise(resolve => setTimeout(resolve, 5000));
await recorder.stop();
Record an animation and take a still at the end
const recorder = await page.record({ path: 'interaction.mp4' });
await page.click('#play');
await page.waitForSelector('#finished');
await recorder.stop();
await page.screenshot({ path: 'final-state.png' });
Stopping before the final screenshot makes the two operations easier to reason about and avoids competing capture work. In concurrent workflows, page creation and page closure wait for screenshot completion, while bringing a page to the front does not wait for screenshots already in progress.
Control timing, loading, and visual state
Use the narrowest readiness condition
- Navigation: choose
domcontentloaded,load, ornetworkidle2based on the page, not habit. - Selector: wait for the component that must appear, then verify its text, dimensions, or state.
- Network: wait for a request or response your application owns when data drives the screenshot.
- Animation: use a completion signal or a measured delay; network idle alone does not mean an animation is finished.
Stabilize output
- Set a deterministic viewport with
page.setViewportSizeor the equivalent API in your installed release. - Disable transitions in a test-only stylesheet if you need pixel-stable comparisons.
- Freeze dates, random values, and live data in the page you control.
- Wait for fonts and images that affect layout before capturing.
Do not claim a universal capture speed or resolution limit: the documentation does not publish a general benchmark, and the result depends on the page, browser, machine, and chosen options.
Version and compatibility cautions
The surfaced documentation labels differ: the screenshot guide and Page overview show Puppeteer 25.12.0, while the Page.record() and ScreenRecording references show 25.11.0. The Page overview also marks record() experimental. Those labels can reflect different publication points rather than a complete compatibility matrix. If page.record is undefined or recording fails, check the Puppeteer version actually installed, the Chrome or Chromium binary it launches, and the current API reference for that pair. Do not assume that a method documented online exists in every older release.
Troubleshooting checklist
“page.record is not a function”
Your installed Puppeteer may predate the documented API, or a different package version is being loaded. Run npm list puppeteer, verify the import, update deliberately, and confirm the browser/Puppeteer combination. Do not silently switch to obsolete screencast() code without checking its documented limitations.
Rank #4
The MP4 is empty or truncated
Make sure await recorder.stop() runs before page.close() or browser.close(). Put it in a finally block, as in the example, and retain the process until the stop promise resolves.
The screenshot is blank
Capture after navigation and after the required selector or data response appears. Check that the target is not behind a consent dialog, hidden by CSS, or rendered in a different frame. For an iframe, obtain its frame and wait for content inside that frame before taking the screenshot.
An element screenshot throws a detached-node error
The page replaced the element after you selected it. Wait for the final state and select the element again immediately before calling screenshot().
The video never plays
Inspect autoplay and media policy, muted state, source errors, and whether the page requires a user gesture. Wait for a meaningful media event rather than assuming that networkidle2 means playback has started.
Free tools Windows power users keep installed
One-click scans. No signup required.
Full-page output has missing lazy images
Some sites load images only after they approach the viewport. Scroll through the page or trigger the site’s own lazy-load mechanism, wait for image completion, and then capture. This behavior is page-specific.
Best Value
- Used Book in Good Condition
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, and its clean-shot pipeline accepts cookie or consent banners before removing more than 60 known consent platforms, newsletter popups, and chat widgets. Each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and whether the request was billed.
For a direct capture, see the ScreenshotNeo API documentation:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in 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)
And 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}`);
ScreenshotNeo also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Its options cover full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work.
Plans start with 1,000 screenshots per month free with no card. Paid plans are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is on every plan. Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without a card.
Frequently Asked Questions
Can Puppeteer extract still frames from an existing video file?
The documented workflow covers screenshots of the rendered page and recording page activity. It does not document extracting frames from an existing video file, so use a video-processing tool for that separate task.
Does Page.record() require FFmpeg?
The FFmpeg requirement is documented for the obsolete Page.screencast() path. Do not transfer it to Page.record() without documentation for your specific version.
Should I close the browser immediately after starting a recording?
No. Keep the page alive while the interaction runs, await the returned recorder’s stop() method, and close the browser afterward.
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.




