Complete guide · Published October 18, 2026 · 12 min read

How to Record a Website as Video: Screen Recorders, Code and APIs

Quick answer

To record a website as video once, use your computer's screen recorder (Shift+Command+5 on Mac, the Snipping Tool on Windows 11). To record a website video automatically, for example a smooth scrolling video of a landing page, use Playwright's recordVideo or Puppeteer's page.screencast(), then convert the WebM to an H.264 MP4 with ffmpeg. Avoid GIF for anything longer than a few seconds: it is several times larger than MP4.

4ways to record a site, from one click to fully automated
5.7 MBour 37-second scroll as H.264 MP4 at 1280×720
21.7 MBthe same scroll as a GIF at half size and 12 fps
VP9 in .mp4what Puppeteer produced; re-encode for compatibility

"Record a website" covers very different jobs: a quick clip for a bug report, a polished scrolling video of your landing page for social media, an automated recording of a test run, or a video an AI agent makes while it browses. We tested the code in this guide on our own homepage on macOS with Chrome, Playwright 1.64.1, Puppeteer 25.14.0 and ffmpeg 8.0.1, and the file sizes below are from those runs.

Pick your scenario

You want toBest methodOutput
Show a bug to a colleague right nowOS screen recorderMOV / MP4
Record a narrated product walkthroughScreen recorder or a recording extensionMP4
Make a smooth scrolling video of a landing pagePlaywright or Puppeteer + scripted scrollWebM → MP4
Capture a hero animation or micro-interactionPlaywright with a fixed viewport, short clipMP4 or short GIF
Keep a video of every failed end-to-end testPlaywright Test video optionWebM in the report
Record what an AI agent did in the browserPlaywright MCP with DevTools capabilityWebM
Record many URLs on a scheduleScript in CI, or a video APIMP4
Record a streaming service's DRM-protected playerThe protected video area records as black; the rest of the page records normallySee why

1Built-in screen recorders

No installCaptures your cursorManual every time
SystemHow
macOSPress Shift+Command+5, choose "Record Selected Portion", draw around the browser window, click Record.
Windows 11Open the Snipping Tool, switch to the video (record) mode, select the area and start.
Windows (games bar)Win+Alt+R records the active app window.

For a cleaner result, hide bookmarks and extensions, set the browser zoom to 100%, close chat widgets and cookie banners first, and scroll with the keyboard (Space or arrow keys) for a steadier motion than a trackpad. Recording extensions such as Loom add webcam and narration on top, which is useful for walkthroughs.

Pick it when you need one video now, with your own narration.
Skip it when you need the same recording again, at an exact size, or for many pages.

2Playwright: a scrolling video in 20 lines

Exact sizeRepeatableWebM output
install
npm init -y && npm pkg set type=module npm install playwright npx playwright install chromium # browser for recording npx playwright install ffmpeg # only if recording reports a missing ffmpeg
record-scroll.mjs
import { chromium } from "playwright"; const browser = await chromium.launch(); try { const context = await browser.newContext({ viewport: { width: 1280, height: 720 }, recordVideo: { dir: "videos/", size: { width: 1280, height: 720 } }, }); const page = await context.newPage(); await page.goto("https://example.com/", { waitUntil: "load" }); await page.waitForTimeout(1000); // let the first screen settle // Smooth scroll, with limits so infinite feeds cannot run forever await page.evaluate(async ({ step, delay, maxMs, maxPx }) => { const start = performance.now(); let scrolled = 0; while ( window.scrollY + window.innerHeight < document.documentElement.scrollHeight && performance.now() - start < maxMs && scrolled < maxPx ) { window.scrollBy(0, step); scrolled += step; await new Promise((r) => setTimeout(r, delay)); } }, { step: 12, delay: 16, maxMs: 30_000, maxPx: 20_000 }); await page.waitForTimeout(800); const video = page.video(); await context.close(); // the file is written on close console.log(await video.path()); } finally { await browser.close(); }

Run it with node record-scroll.mjs. The 30-second and 20,000-pixel limits stop the loop on pages with infinite scrolling; on our homepage the 30-second limit stopped the scroll before the bottom, giving a 32-second video including the first and last pauses. The demo and the file sizes below come from an earlier run without the limits, which scrolled all the way down in 37 seconds.

Our homepage recorded with this script without the limits (37 s), converted to H.264 with ffmpeg and sped up 2x (960×540, 18 s, 1.8 MB).

Four things we ran into:

  • Set recordVideo.size. Without it, Playwright scales the video to fit 800×800, or uses 800×450 if no viewport is set, so a 1280×720 page comes out smaller.
  • Close the context. The video file is only finished when context.close() resolves. Read page.video().path() afterwards.
  • The video starts blank. Recording begins when the page is created, before goto finishes, so our file opened with about a second of white frames. Trim it when converting with ffmpeg -ss 1.2 -i recording.webm ....
  • ffmpeg is a separate download. It usually comes with browsers installed by npx playwright install; if you launch system Chrome with channel: "chrome", recording fails until you run npx playwright install ffmpeg.

For tests, you do not need the code above. Playwright Test records automatically with use: { video: "retain-on-failure" }, keeping videos only for failed tests (the same modes as traces, such as on-first-retry). Our screenshot-on-failure guide shows how to upload them from CI.

Pick it when you want repeatable videos at a fixed size, in tests or scripts.
Skip it when you need audio or your own cursor in the video; Playwright records the page only.

3Puppeteer: page.screencast()

WebM, MP4, GIFCrop, scale, speedExperimental API
install
npm install puppeteer # downloads a matching Chrome brew install ffmpeg # macOS; on Debian/Ubuntu: sudo apt install ffmpeg
screencast.mjs
import puppeteer from "puppeteer"; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({ width: 1280, height: 720 }); await page.goto("https://example.com/", { waitUntil: "networkidle2" }); const recorder = await page.screencast({ path: "scroll.mp4", format: "mp4", fps: 30 }); await page.evaluate(async () => { for (let i = 0; i < 120; i++) { window.scrollBy(0, 15); await new Promise((r) => setTimeout(r, 16)); } }); await recorder.stop(); await browser.close();

page.screencast() is marked experimental in Puppeteer's types and needs ffmpeg on your PATH. Its options include crop (record part of the viewport), scale, speed (for example 2 for a double-speed video) and fps.

Check the codec. In our run, format: "mp4" produced VP9 video inside an MP4 container (ffprobe reported codec_name=vp9). Many editors, social platforms and older devices expect H.264, so re-encode it as shown in the next section before you share it.
Pick it when you already use Puppeteer and want crop, speed or GIF output in one call.
Skip it when you need a stable API; it can still change between versions.

4Recording what an AI agent does

Playwright MCPOpt-in capability

Playwright MCP, the browser server many coding agents use, can record its own session. Start it with the DevTools capability:

npx @playwright/mcp@latest --caps=devtools --output-dir ./recordings

The agent then has browser_start_video and browser_stop_video tools. Without a filename, videos are saved in the output directory as video-{timestamp}.webm. Ask the agent to start recording before a task and stop afterwards; the video is a reviewable record of every click. Details on where files go are in our Playwright MCP screenshot guide.

Convert and compress with ffmpeg

Browsers record WebM (VP8 or VP9). These ffmpeg commands turn a recording into something you can post anywhere:

webm → H.264 mp4 (broad compatibility)
ffmpeg -i recording.webm -c:v libx264 -pix_fmt yuv420p -crf 23 -movflags +faststart scroll.mp4
take the first 20 seconds of the recording and play them at 2x (10-second result)
ffmpeg -t 20 -i scroll.mp4 -vf "setpts=0.5*PTS" -an fast.mp4

Placing -t before -i limits how much of the input is read; placed after -i it limits the length of the output instead.

short GIF with a proper palette
ffmpeg -i scroll.mp4 -vf "fps=12,scale=640:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" scroll.gif

What these produced for our 37-second, 1280×720 homepage scroll (the run without limits):

FileSettingsSize
Playwright WebMVP9, 1280×720, as recorded9.0 MB
H.264 MP4CRF 23, faststart5.7 MB
GIF640 px wide, 12 fps, palette21.7 MB

-pix_fmt yuv420p is what makes the MP4 play in Safari, QuickTime and most social apps we know of; +faststart lets it start playing before it has fully downloaded. One run on one page, so treat the sizes as an example, not a benchmark. We read sizes and codecs with ls -lh and ffprobe -v error -show_entries format=duration:stream=codec_name,width,height -of compact file.mp4.

Tips for a smooth, clean scrolling video

ProblemFix
Video looks like a slideshowScroll 10 to 20 px per step with ~16 ms between steps instead of jumping by a screen
Empty boxes below the foldLazy-loaded images only start loading when scrolled near. Do a quick scroll pass before recording, scroll back to the top, then wait for the images you care about (or a specific element) with a timeout. networkidle alone does not help, and on pages with constant requests it may never fire
Sections appear late or not at allScroll-reveal animations; inject CSS that disables them for the recording
Cookie banner covers the pageAccept or reject it before recording, or hide it with injected CSS on your own site
Sticky header jumpsNative scroll-behavior: smooth fights scripted scrolling; set it to auto for the recording
Video is too longSpeed it up with setpts afterwards instead of scrolling faster, which looks choppy

Recording website videos through an API

Running browsers and ffmpeg yourself works, but at volume it becomes infrastructure: browser crashes, disk space for raw video, encoding time and queues. A website video API moves that to a hosted service. The model is the same across providers:

  1. Send the URL and the recording settings: viewport, length, frame rate, output format.
  2. Describe what should happen on the page: a list of actions such as "wait 1 s, scroll 800 px, click this button, wait 2 s". This replaces the scroll loop in your script.
  3. Wait for the result. Video takes longer than a screenshot, so APIs usually offer an async mode: you get a job ID, then poll for it or receive a webhook when the file is ready.
  4. Get the file: streamed back directly, or as a link to stored MP4 or WebM.
Question to ask a providerWhy it matters
Maximum length and frame rateLonger, smoother videos cost more and take longer to render
Codec of the MP4H.264 plays almost everywhere; VP9 or HEVC may need re-encoding
Which actions are supportedScroll and click are common; typing, hovering and waiting for elements vary
Cookie banners, ads, lazy loadingThe same cleanup problems as screenshots, but they are visible for the whole video
Async jobs and webhooksSynchronous requests for long videos can hit HTTP timeouts
Pricing unitPer video, per second of video or per rendering time; compare on your typical length

Some screenshot APIs already offer scrolling videos as part of their paid plans; our ScreenshotOne comparison notes this, for example. Use cases where an API makes more sense than a script: product pages recorded on a schedule, video previews generated for user-submitted URLs, social clips for many landing pages, and recordings triggered from no-code tools or AI agents.

How video capture is planned in SnapshotFlow

Not available yet. Video capture is on the SnapshotFlow roadmap. Everything in this section describes the planned design from our API docs; names and limits may change before release, and requests to /video do not work today.

The idea is the same as our screenshot endpoint: one HTTP request, a hosted browser on our side, and a file back. According to the planned design, GET /video will record a page interaction as MP4 or WebM, replay scroll and click actions during the recording, and follow the same response_type convention as /screenshot: stream the file directly, or return a storage path or an async job_id.

Planned parameterDefaultMeaning
urlrequiredPage to record
duration5 sRecording length, up to 60 seconds
fps25Frames per second
formatmp4mp4 or webm
width / height1280 / 800Viewport size
actionsnoneJSON list of scroll and click actions to replay, for example [{"type":"scroll","y":800,"delay":1000}]

A request would look like this once it ships (illustrative, based on the planned parameters):

planned, not live
curl --get "https://api.snapshotflow.com/video" \ -H "X-Api-Key: $SNAPSHOTFLOW_API_KEY" \ --data-urlencode "url=https://example.com/" \ --data-urlencode "duration=12" \ --data-urlencode "format=mp4" \ --data-urlencode 'actions=[{"type":"scroll","y":800,"delay":1000},{"type":"scroll","y":1600,"delay":1000}]' \ -o scroll.mp4

Until then, the Playwright script above gives you the same result on your own machine or CI, and our /screenshot endpoint covers full-page images, PDFs and mobile captures today. The roadmap section of the docs and the changelog will show when video lands.

Rights and privacy

  • Other people's websites: a video of a page is a copy of its content, so the same copyright rules apply as for screenshots. Recording for internal review is usually low risk; publishing needs permission or a legal exception. See is it legal to screenshot a website.
  • Protected video: streaming players use DRM, and their video area records as black. Getting around that is restricted by law in many countries and is not covered here.
  • Recording your visitors is different: this guide is about recording pages yourself. Recording real users' sessions on your site (session replay) involves personal data and usually needs consent and a privacy notice.
  • Logged-in pages: before sharing a recording of a dashboard, check it for names, emails, tokens and customer data.

Takeaways

  • One-off: your OS screen recorder. Repeatable: Playwright or Puppeteer.
  • Set Playwright's recordVideo.size and close the context to get the file.
  • Check Puppeteer's codec and re-encode to H.264 for sharing.
  • Prefer MP4 over GIF; our GIF was almost 4× larger at half the size.
  • SnapshotFlow's /video endpoint is planned, not live.

Sources

FAQ

How do I record a website as a video?

For a one-off, use your operating system's screen recorder: Shift+Command+5 on a Mac or the Snipping Tool's record mode on Windows 11. For repeatable or automated recordings, use Playwright's recordVideo option or Puppeteer's page.screencast() and convert the result with ffmpeg.

How do I make a scrolling video of a website?

Open the page in Playwright or Puppeteer with video recording on, scroll in small steps with a short pause between them, then stop the recording. Small steps of 10 to 20 pixels every frame look smooth; big jumps look like a slideshow.

Why is my Playwright video only 800x450?

If you do not set recordVideo.size, Playwright scales the video to fit 800x800 (or uses 800x450 when no viewport is set). Pass recordVideo: { dir, size: { width: 1280, height: 720 } } to keep the full resolution.

Why won't my Puppeteer MP4 play in some apps?

In our test, page.screencast() with format mp4 produced VP9 video inside the MP4 container. Many tools expect H.264. Re-encode with ffmpeg -c:v libx264 -pix_fmt yuv420p for the widest compatibility.

Should I use GIF or MP4 for a website recording?

MP4 in almost every case. In our test a 37-second scroll was 5.7 MB as H.264 MP4 and 21.7 MB as a GIF at half the resolution and 12 frames per second. Use GIF only for very short loops where video is not supported.

Can SnapshotFlow record videos?

Not yet. A GET /video endpoint is on the SnapshotFlow roadmap and its planned parameters are listed in the API docs. Today SnapshotFlow captures screenshots and PDFs; for video, use one of the methods in this guide.

Need the page as an image today?

Video capture is on our roadmap. Full-page screenshots, PDFs, mobile captures and visual diffs work right now, from one HTTP request. The free tier gives you 300 screenshots, no credit card required.