Playwright Codegen records your browser actions; it does not automatically add screenshot steps. Start Codegen, perform the workflow you want to preserve, copy the generated test, then insert page.screenshot() or locator.screenshot() at the exact state you need. Use fullPage: true for an entire scrollable page, fixed emulation settings and disabled animations for repeatable captures, and masking for dynamic or private content.
What Codegen does—and where screenshots fit
Codegen opens a browser and Playwright Inspector, watches your interactions, and generates test actions. The generated script describes navigation, clicks, fills and assertions. Screenshot capture is normally a refinement you add after copying that script into your project.
Install Playwright Test in a Node.js project, then start recording:
npx playwright codegen https://example.com
The URL is optional; you can open a page manually in the Codegen browser. The command accepts options for browser selection, output-file selection and language targets, including Python. For a repeatable layout, set a viewport while recording:
Recommended Free Tools
#1 Best Overall
npx playwright codegen --viewport-size="800,600" https://example.com
Interact with the site until it reaches the state you want. Stop recording, copy the generated test from the Inspector, and place screenshot calls after the relevant navigation or interaction. Keep authentication state local: --save-storage=auth.json records it and --load-storage=auth.json replays it, but the file can contain credentials and cookies.
Complete generated-test example
This test captures the viewport, the complete page, and one component after the page has loaded:
import { test } from '@playwright/test';
test('capture page states', async ({ page }) => {
await page.goto('https://example.com');
await page.screenshot({ path: 'artifacts/viewport.png' });
await page.screenshot({ path: 'artifacts/full-page.png', fullPage: true });
await page.getByRole('banner').screenshot({
path: 'artifacts/banner.png',
animations: 'disabled'
});
});
Create the artifacts directory before running if your project does not create it automatically. A supplied path writes the image to disk. Without path, the method returns an image buffer that you can send to a comparison or processing system.
Choose the screenshot scope
| Need | Call | Result |
|---|---|---|
| Visible browser viewport | page.screenshot({ path }) |
Only the currently visible area |
| Entire scrollable document | page.screenshot({ path, fullPage: true }) |
A potentially very tall image including content below the fold |
| One component | locator.screenshot({ path }) |
A clip around the matched element |
| Downstream processing | const buffer = await page.screenshot() |
In-memory PNG, JPEG or WebP data |
Viewport capture
Use the page method without fullPage when the screenshot represents what a user sees at a particular scroll position. Set the viewport before navigation or through Codegen’s --viewport-size option so responsive breakpoints do not change between runs.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows 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 reinstallFull-page capture
fullPage: true captures the complete scrollable page. Long documents create large files and can expose lazy-loading problems. Navigate to the page, wait for the content that matters, and verify that images loaded before capturing.
Rank #2
Element capture
Use a stable locator rather than a brittle generated CSS path:
const card = page.getByRole('article').filter({ hasText: 'Release notes' });
await card.screenshot({ path: 'artifacts/release-card.webp', animations: 'disabled' });
Locator screenshots wait for actionability and scroll the target into view. If more than one element matches, narrow the locator so the intended component is unambiguous.
Make Codegen screenshots reproducible
Fix rendering inputs
Choose a named device when mobile rendering is the requirement:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →npx playwright codegen --device="iPhone 13" https://example.com
Also control --color-scheme, --timezone, --geolocation and --lang when the site changes content or styling for those inputs. Use the same browser, viewport, device scale and locale in CI as on a developer machine.
Stop motion and hide unstable data
Animations can produce different pixels at the same checkpoint. Disable them for a locator or page capture where supported, and mask timestamps, rotating ads, avatars or account data:
await page.screenshot({
path: 'artifacts/dashboard.png',
fullPage: true,
animations: 'disabled',
mask: [page.locator('[data-testid="last-updated"]'), page.locator('.avatar')],
scale: 'css'
});
scale: 'css' keeps output dimensions tied to CSS pixels. Use omitBackground: true when a transparent image is required. Mask sensitive regions before storing artifacts or sending them to a pixel-diff service.
Wait for the real state
Do not rely on an arbitrary short delay when a semantic signal exists. Add a locator assertion or wait for the selector that marks readiness. For pages that fetch data after navigation, wait until the relevant content is visible, then capture. This avoids screenshots of loading skeletons that happen to finish at different times.
Free tools Windows power users keep installed
One-click scans. No signup required.
Python Codegen and screenshots
Codegen can generate Python targets. A generated Python test can use the synchronous or asynchronous Playwright API; this synchronous example is directly runnable after installing Playwright:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 800, "height": 600})
page.goto("https://example.com")
page.screenshot(path="artifacts/viewport.png")
page.screenshot(path="artifacts/full-page.png", full_page=True)
page.get_by_role("banner").screenshot(
path="artifacts/banner.png", animations="disabled"
)
browser.close()
Use a Python Codegen command when you want Python output, then add the screenshot calls to the copied script. Option names use Python’s underscore style, such as full_page and omit_background.
Buffers, formats and visual regression
Playwright writes PNG, JPEG and WebP when the filename extension and options select that format. For an external visual comparison, keep the bytes in memory:
Rank #4
const buffer = await page.screenshot({ type: 'png' });
// Pass buffer to your pixel-diff or artifact uploader.
A comparison system should use the same browser version, viewport, device scale, fonts, locale, timezone and data fixture on every run. Mask content that is intentionally variable rather than weakening every threshold. Full-page images may be too tall for a particular diff tool; compare a stable component or split the page into meaningful regions when necessary.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Or skip the browser setup
ScreenshotNeo provides a one-request website screenshot API and an MCP server for AI agents. It accepts consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.
Use the same request from a shell, Python or Node.js. See the ScreenshotNeo documentation for parameters and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Options include full-page and CSS-selector captures, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, HTML/CSS rendering, custom JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, 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, which can simplify migration. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.
The Free plan includes 1,000 screenshots each month 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 started.
Troubleshooting Codegen captures
The screenshot is blank or shows a loading state
Cause: capture occurs before the page’s data or images arrive. Fix: wait for a meaningful selector, assertion or network-idle condition, and confirm the URL and authentication state.
The full-page image misses content
Cause: lazy content is loaded only after scrolling, or the page changes height during capture. Fix: scroll or trigger the component before fullPage: true, wait for images and capture after layout stabilizes.
The element locator fails
Cause: the generated selector is dynamic, matches multiple nodes or targets an iframe. Fix: use role, label, text or a test identifier; narrow duplicate matches; for an iframe, first select its frame and then locate the element inside it.
Visual diffs change on every run
Cause: animations, timestamps, random data, fonts, locale or viewport differences. Fix: disable animations, mask volatile regions, freeze test data, install consistent fonts and fix emulation settings.
Authentication works locally but not in CI
Cause: missing or stale storage state, environment variables or origin-specific cookies. Fix: create storage state in the same environment, keep it secret, load it explicitly and verify the account page before capture.
The image is too large
Cause: a full-page document or high device scale. Fix: capture a component, use scale: 'css', resize downstream, or divide the document into stable sections. Do not reduce fidelity until the comparison requirements are clear.
Practical checklist
- Record the workflow with Codegen and copy the generated test.
- Insert the screenshot at the state that matters, not merely after navigation.
- Choose viewport, full-page or locator scope deliberately.
- Fix viewport, device, locale, timezone, color scheme and authentication state.
- Wait for meaningful content and disable animations.
- Mask private or intentionally dynamic regions.
- Store artifacts securely and use buffers when a diff pipeline needs them.
Frequently Asked Questions
Can Codegen save a screenshot while I am recording?
Codegen records browser actions; add screenshot calls to the copied generated test after recording.
Which call captures only one component?
Call screenshot() on a locator, such as page.getByRole('banner').screenshot({ path: 'banner.png' }).
What is the difference between a file and a buffer?
A path writes the image to disk; omitting it returns image bytes for processing or visual comparison.
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.

