Recommended Free Tools
For a normal full-page image, use Puppeteer’s built-in option: await page.screenshot({ path: 'page.png', fullPage: true }). If you must make the page load or reveal content while traversing it, use a different workflow: scroll in controlled increments, wait for page-specific content, capture overlapping viewport images, and stitch them with an image tool. Puppeteer documents the screenshot and scrolling primitives, but not one universal scroll-and-stitch recipe.
Choose the capture method first
There are two valid interpretations of “full-page screenshot.” Pick the one that matches the job rather than adding scrolling automatically.
| Approach | Best fit | Trade-offs |
|---|---|---|
fullPage: true |
One image of the page with minimal code | Uses Puppeteer’s documented screenshot option, but it does not model a workflow in which scrolling itself triggers or reveals content. |
| Scroll, capture, and stitch | Pages that require traversal, incremental loading, or inspection at each position | Requires readiness logic, overlap planning, image stitching, and checks for dynamic content, seams, duplicated fixed elements, and missed loads. |
The second row is an implementation pattern derived from Puppeteer’s documented screenshot and scrolling capabilities, not a built-in Puppeteer feature or a guaranteed recipe for every site.
Prerequisites and a stable viewport
- Install a current Puppeteer package in a Node.js project:
npm install puppeteer. - Use a fixed viewport before navigation. Width and height are CSS pixels, and
deviceScaleFactordefaults to1. - Record the viewport values so every segment has the same geometry.
- Configure the viewport before
goto()when possible. Changing it can reload a page in some circumstances, and some sites do not expect phone-like viewport changes.
A deliberate viewport also makes the output reproducible. For example, { width: 1440, height: 900, deviceScaleFactor: 1 } describes a 1× desktop capture; a higher device scale factor produces more physical pixels without changing the CSS layout dimensions.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →#1 Best Overall
- Record videos and take screenshots of your computer screen including sound
- Highlight the movement of your mouse
- Record your webcam and insert it into your screen video
- Edit your recording easily
- Perfect for video tutorials, gaming videos, online classes and more
Fast path: Puppeteer’s built-in full-page screenshot
Page.screenshot() is Puppeteer’s page screenshot method. In ScreenshotOptions, fullPage defaults to false; setting it to true requests the full page. Do not confuse it with captureBeyondViewport: that option controls capture outside the viewport and defaults to false when there is no clip and true otherwise.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
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 });
} finally {
await browser.close();
}
networkidle2 is only a starting readiness condition. A page with long-lived connections, delayed images, animations, or application-specific requests may need a selector wait, an explicit delay, or another page-specific signal before capture.
When this method is enough
- You need one document-like image and do not need to observe intermediate scroll positions.
- The page’s content is already present or becomes ready under a condition you can express before the screenshot.
- You can validate that lazy media, sticky elements, and dynamic sections appear correctly in the resulting file.
Manual scrolling workflow
Use this pattern when traversal is part of the requirement. It captures one viewport at a time and leaves stitching to an image-processing library. The loop below is intentionally conservative: it fixes the viewport, measures the page, scrolls by less than one viewport height to create overlap, waits briefly for page-specific work, and records each segment.
import puppeteer from 'puppeteer';
import fs from 'node:fs/promises';
const url = 'https://example.com';
const width = 1440;
const height = 900;
const overlap = 120;
const settleMs = 500;
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({ width, height, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle2' });
const pageHeight = await page.evaluate(() =>
Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight
)
);
const step = Math.max(1, height - overlap);
const files = [];
for (let y = 0; y < pageHeight; y += step) {
await page.evaluate((top) => window.scrollTo(0, top), y);
await new Promise(resolve => setTimeout(resolve, settleMs));
const actualY = await page.evaluate(() => window.scrollY);
const file = `segments/segment-${files.length.toString().padStart(3, '0')}.png`;
await fs.mkdir('segments', { recursive: true });
await page.screenshot({ path: file });
files.push({ file, top: actualY });
}
console.log(JSON.stringify({ width, height, pageHeight, overlap, files }, null, 2));
} finally {
await browser.close();
}
This code is an implementation pattern, not an official Puppeteer scroll-and-stitch API. The delay is deliberately a placeholder: replace it with a condition that reflects the page you are capturing. For example, wait for a lazy-image selector to become complete, for a “load more” operation to finish, or for a known application state. A fixed sleep alone cannot establish that every site’s content has finished loading.
Why overlap matters
Adjacent screenshots should overlap by a known number of CSS pixels. Overlap gives a stitching process shared visual material for alignment and lets you crop a repeated strip from each subsequent image. Without overlap, a one-pixel scroll discrepancy or a layout shift can produce a visible gap.
Stitching the segments
Puppeteer’s screenshot API does not stitch files. Use an image-processing tool after capture. A simple vertical compositor can place each segment at the recorded scroll offset, then crop the canvas to the page’s measured height. If your page contains fixed headers, cookie notices, or animated elements, inspect every seam: those elements can repeat in each segment or change position between captures.
Rank #2
- Mix an audio, music and voice tracks
- Record single or multiple tracks simultaneously
- Intuitive tools to split, trim, join, and many other editing features
- Loaded with audio effects including EQ, compression, reverb, and more.
- Load an audio file and export to all popular audio formats from studio quality wav to high compression formats
For reliable stitching, keep the viewport and device scale factor constant, preserve the measured offsets, and use the overlap to identify duplicate rows. If content changes height after the first measurement, recalculate the document height or stop only after the scroll position and page height have stabilized.
Making lazy content appear during traversal
Puppeteer documents locator scrolling through mouse-wheel events and waits for visibility and a stable bounding box before the scroll action. That behavior does not prove that all lazy-loaded content is complete. Your loop must define what “settled” means for this page.
Free tools Windows power users keep installed
One-click scans. No signup required.
Useful readiness checks
- Wait for a specific section or image selector that the page promises to render.
- After scrolling, check that targeted images report completion, or that a loading marker disappears.
- Allow a page-specific delay for animation or deferred layout, then verify the measured document height again.
- For infinite scroll, continue until a terminal condition (such as “no more results”) appears; do not assume the initial
scrollHeightis final.
Keep these checks close to the content they protect. A global “network idle” event can be misleading on pages that maintain analytics or streaming connections, while a short delay can be insufficient for a large image or client-rendered section.
Element-only captures
If the requirement is one DOM element rather than the entire document, obtain its ElementHandle and call its screenshot method:
const element = await page.$('.invoice');
if (!element) throw new Error('Missing .invoice element');
await element.screenshot({ path: 'invoice.png' });
ElementHandle.screenshot() scrolls the element into view when necessary and then uses Page.screenshot(). It errors if the element has been detached, so reacquire the handle after a framework re-render instead of reusing a stale reference.
Output, scale, and browser-protocol considerations
- Choose PNG when lossless text and transparent pixels matter; choose JPEG or WebP when file size is more important and your pipeline accepts those formats.
- Viewport dimensions are CSS pixels. A
deviceScaleFactorgreater than one increases output pixel density but also increases memory and file size. - Do not infer identical option support across every connection mode. Puppeteer’s WebDriver BiDi documentation lists supported screenshot parameters and notes that not all screenshot parameters are supported, so check the actual Puppeteer and browser versions used by your deployment.
- Validate very tall pages in your own environment. A single full-page bitmap can consume substantial memory, while segmented capture limits each screenshot but adds stitching work.
Troubleshooting common failures
The output covers only the viewport
Confirm that you called page.screenshot({ fullPage: true }) for the fast path. In a manual workflow, confirm that the scroll loop actually changes window.scrollY and that you are not saving the same file repeatedly.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #3
Lazy images are blank
Scrolling does not guarantee completion. Add a selector-based or image-completion check after each scroll, allow the page’s own loading state to settle, and capture again. If the page changes height as images arrive, measure height repeatedly and extend the loop.
Sticky headers or banners repeat
Repeated fixed elements are a normal risk of viewport segmentation. Record their height, crop or mask the repeated region during stitching, and verify that a consent banner or chat widget is not altering the layout between segments.
Segments have seams or gaps
Use a fixed viewport and an explicit overlap. Stitch using the recorded scroll offsets, then inspect seams for layout shifts. Avoid animations where possible, or wait until the relevant element’s bounding box is stable.
The page height keeps increasing
This usually indicates infinite scroll, deferred rendering, or a script that inserts content after every traversal. Define a stopping condition, recalculate scrollHeight, and protect the loop with a maximum segment count so a faulty page cannot run indefinitely.
An element handle is detached
The DOM node was replaced after you obtained the handle. Query the selector again immediately before element.screenshot(), and wait for the replacement element to be visible and stable.
Changing the viewport changes the page
Set the viewport before navigation and keep the same dimensions for every segment. A viewport change can reload the page or activate a different responsive layout, invalidating earlier measurements.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance and reliability checklist
- Set and record width, height, and device scale factor before navigation.
- Choose
fullPage: trueunless traversal itself is required. - For scrolling, define the page-specific readiness signal before writing the loop.
- Use overlap and retain each segment’s actual scroll offset.
- Re-measure page height when dynamic content can change it.
- Close the browser in a
finallyblock, even when navigation or capture fails. - Open the final image and inspect the top, bottom, lazy media, fixed elements, and every stitch seam.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API when you would rather make one request than maintain Puppeteer launch, viewport, waiting, and stitching code. Its capture pipeline accepts cookie and consent banners before the shot and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
Use the documented API call below (replace the URL and key):
PC 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 & 11Crashes, 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 minuteRank #4
- Transform audio playing via your speakers and headphones
- Improve sound quality by adjusting it with effects
- Take control over the sound playing through audio hardware
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)
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}`);
See the ScreenshotNeo documentation for the remaining options. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. 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 capture, selector capture, custom CSS and JavaScript, waits, request blocking, headers and cookies, device presets, PDF output, caching, signed links, asynchronous jobs, bulk capture, and a usage API.
Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without a card.
Frequently Asked Questions
Should I always scroll instead of using fullPage?
No. Use fullPage: true for a conventional single-image capture. Scroll-and-stitch is appropriate when traversal itself must trigger, reveal, or inspect content.
Can Puppeteer stitch screenshots by itself?
No. Puppeteer captures the page or viewport; stitching the resulting files is a separate image-processing step.
What does captureBeyondViewport replace?
It does not replace fullPage. It controls capture outside the viewport and has different defaults depending on whether a clip is supplied.
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.




