Rollberry is an MIT-licensed open source CLI and Node API for turning web
pages into smooth top-to-bottom scroll videos. It can capture one or more
URLs into a single MP4, or render a project JSON file into multiple output
variants such as desktop and mobile. It is built for real browser capture,
works with normal URLs and localhost, and is published for direct npx
usage.
Maintained by CORe Inc.
Requirements:
- Node.js
24.12.0+ ffmpegavailable onPATH
Install nothing globally. Run it directly:
npx rollberry capture http://localhost:3000 \
--out ./artifacts/demo.mp4 \
--viewport 1440x900 \
--fps 60 \
--duration auto \
--wait-for selector:body \
--hide-selector '#cookie-banner'On the first run, if Playwright Chromium is missing, Rollberry installs it
automatically. ffmpeg is not auto-installed.
For project-based rendering, start from the bundled sample:
cp rollberry.project.sample.json rollberry.project.json
npx rollberry render ./rollberry.project.jsonThe normal way to run Rollberry is npx.
Use the latest published version:
npx rollberry capture http://localhost:3000 --out ./artifacts/demo.mp4Pin a specific version:
npx rollberry@0.1.2 capture http://localhost:3000 --out ./artifacts/demo.mp4Capture a local development server:
npx rollberry capture http://localhost:3000 \
--out ./artifacts/local.mp4 \
--wait-for selector:bodyCapture a public page:
npx rollberry capture https://playwright.dev \
--out ./artifacts/playwright.mp4 \
--duration 8Render a project file with multiple outputs:
npx rollberry render ./rollberry.project.jsonNotes:
npxdownloads the published CLI package automatically- on the first run, Rollberry installs Playwright Chromium if needed
ffmpegmust already be available on your machine- if you want reproducible automation, pin the package version with
npx rollberry@<version> ...
Each run writes:
video.mp4: the rendered capturevideo.manifest.json: environment, scene definitions, capture metrics, artifact metrics, and failure detailsvideo.log.jsonl: structured operational logs
You can override the sidecar paths with --manifest and --log-file.
Project renders write the same sidecars for each configured output, plus a
project-level *.render-summary.json.
Capture a development site:
npx rollberry capture http://localhost:3000 --out ./artifacts/local.mp4Capture a public site at a fixed duration:
npx rollberry capture https://playwright.dev \
--out ./artifacts/playwright.mp4 \
--duration 8 \
--fps 60Wait for a selector and hide overlays:
npx rollberry capture https://example.com \
--wait-for selector:main \
--hide-selector '#cookie-banner' \
--hide-selector '.intercom-lightweight-app'Capture multiple pages into a single video:
npx rollberry capture \
https://example.com \
https://example.com/about \
https://example.com/contact \
--out ./artifacts/multi-page.mp4Add a pause between pages (the last frame of each page is held):
npx rollberry capture \
https://example.com \
https://example.com/about \
--page-gap 1.5 \
--out ./artifacts/with-gap.mp4Dump raw frames for debugging:
npx rollberry capture http://localhost:3000 \
--out ./artifacts/debug.mp4 \
--debug-frames-dir ./artifacts/debug-framesRender a project file into desktop and mobile outputs:
npx rollberry render ./rollberry.project.jsonRender only one named output from a project:
npx rollberry render ./rollberry.project.json --output mobileUse render when you want scene-by-scene control and multiple outputs from one
config file. actions run before capture as setup. timeline runs during the
capture itself and lets you interleave scroll, pause, and interactions inside
one scene.
{
"$schema": "./rollberry.project.schema.json",
"schemaVersion": 1,
"summaryManifest": "./artifacts/demo.render-summary.json",
"defaults": {
"fps": 60,
"viewport": "1440x900",
"waitFor": "selector:body",
"hideSelectors": ["#cookie-banner"]
},
"scenes": [
{
"name": "home",
"url": "http://localhost:3000",
"actions": [
{ "type": "click", "selector": "[data-open-menu]" },
{ "type": "wait", "ms": 300 }
],
"timeline": [
{ "type": "pause", "duration": 0.4 },
{
"type": "click",
"selector": "[data-open-menu]",
"holdAfterSeconds": 0.4
},
{ "type": "scroll", "toSelector": "#pricing", "duration": 1.4 },
{ "type": "scroll", "to": "bottom", "duration": "auto" }
],
"holdAfterSeconds": 0.75
},
{
"name": "pricing",
"url": "http://localhost:3000/pricing"
}
],
"outputs": [
{
"name": "desktop",
"viewport": "1440x900",
"out": "./artifacts/demo-desktop.mp4",
"audio": {
"path": "./assets/demo-narration.wav",
"volume": 0.8,
"loop": true
},
"subtitles": {
"path": "./assets/demo-captions.vtt",
"mode": "burn-in"
},
"transition": {
"type": "crossfade",
"duration": 0.25
},
"intermediateArtifact": {
"format": "mp4",
"preset": "veryfast",
"crf": 20
},
"finalVideo": {
"preset": "medium",
"crf": 19
}
},
{
"name": "mobile",
"viewport": "430x932",
"out": "./artifacts/demo-mobile.webm",
"format": "webm",
"subtitles": {
"path": "./assets/demo-captions.vtt",
"mode": "soft"
},
"transition": {
"type": "crossfade",
"duration": 0.25
},
"intermediateArtifact": {
"format": "mp4",
"preset": "fast",
"crf": 22
},
"finalVideo": {
"deadline": "best",
"crf": 30
}
}
]
}Supported setup actions:
waitclickhoverpresstypescroll-to
Supported timeline segments:
pausewaitscrollclickhoverpresstypescroll-to
Supported output extensions:
mp4webm
Supported media extensions:
- output-level background audio via
audio.path - output-level subtitles via
subtitles.path subtitles.mode: "soft"formp4andwebmsubtitles.mode: "burn-in"formp4andwebm.srt,.vtt, and.webvttsubtitle inputs- output-level
transition.type: "fade-in" - output-level
transition.type: "crossfade"between adjacent scenes - output-level
intermediateArtifact.format/preset/crffor scene clip profiles - output-level
finalVideo.preset/finalVideo.crfformp4encode control - output-level
finalVideo.deadline/finalVideo.crfforwebmencode control - project-level summary manifest via
summaryManifest - render manifest separates
captureMetricsandartifactMetrics crossfadeusesffprobe-backed clip timing when available and fails fast if scene clip probing is unavailable
rollberry capture <url...>
rollberry render <project.json>
--out <file> Output MP4 path
--viewport <WxH> Viewport size, example: 1440x900
--fps <n> Frames per second
--duration <seconds|auto> Explicit seconds or auto
--motion <curve> ease-in-out-sine | linear
--timeout <ms> Navigation timeout
--wait-for <mode> load | selector:<css> | ms:<n>
--hide-selector <css> Hide CSS selector before capture
--page-gap <seconds> Pause between pages (default: 0)
--debug-frames-dir <dir> Save raw PNG frames
--manifest <file> Manifest JSON output path
--log-file <file> Log JSONL output path
render:
--output <name> Render only the named output (repeatable)
--force Overwrite configured output files
- Supports
http://localhost:*,https://localhost:*,http://127.0.0.1:*, andhttp://[::1]:* - Retries connection-refused errors until
--timeout - Accepts self-signed certificates for localhost targets only
- Does not start your dev server for you
If ffmpeg is missing:
brew install ffmpegIf you are running the test suite, ffprobe may also be used for extra video
verification. Most FFmpeg installs include it alongside ffmpeg.
If capture fails, inspect:
*.manifest.jsonfor final status and error details*.log.jsonlfor per-step structured logs
If a site keeps shifting during capture:
- wait for a stable selector with
--wait-for selector:... - hide chat widgets, cookie banners, and sticky overlays with
--hide-selector - keep dynamic dev-only overlays out of the page when possible
- General contact: https://co-r-e.com/contact
- Security issues: see SECURITY.md
- Contribution guide: see CONTRIBUTING.md
For local CLI usage and captures, Rollberry requires ffmpeg on PATH.
When running pnpm test, the integration suite uses ffprobe when available
to inspect generated videos, but falls back to basic file validation if it is
missing.
corepack pnpm install
corepack pnpm exec playwright install chromium
corepack pnpm check
corepack pnpm test
corepack pnpm buildRun from the repository:
corepack pnpm dev -- capture http://localhost:3000 --out ./artifacts/demo.mp4
corepack pnpm dev -- render ./rollberry.project.sample.jsonRun the regression suite:
cp regression.sample.json regression.sites.json
corepack pnpm regression -- --config ./regression.sites.jsonRollberry stays on the v0.x.x line for now.
- Update
package.jsonversion andCHANGELOG.md - Commit the release prep
- Create an annotated tag like
git tag -a v0.1.2 -m "Release v0.1.2" - Push
mainand the tag to GitHub - GitHub Actions publishes to npm via trusted publishing
Trusted publishing setup expected by this repo:
- GitHub repository:
co-r-e/rollberry - Workflow filename:
.github/workflows/publish.yml - Trigger: push tag
v* - In npm package settings, add a trusted publisher that matches the repository and workflow above