webreel: turning a JSON script of clicks into an MP4 product demo
Record scripted browser demos as video
At a glance
- What is it?
- webreel drives a headless Chrome instance from a JSON config of clicks, key presses, drags and pauses, captures frames at about 60fps and encodes them with ffmpeg, adding a cursor animation and a keystroke overlay. It suits teams that want marketing and documentation videos kept in version control rather than filmed by hand.
- Who is it for?
- Adopt webreel if you publish product videos or documentation clips for a web application, and you want them reproducible, reviewable in a pull request and regenerated on every deploy rather than re-recorded by hand. Do not adopt it for content that depends on a logged in user session, on camera, or on anything a scripted click cannot reach.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 37 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A JSON script that becomes a video file
webreel is described as a tool to record scripted browser videos as MP4 files, with sound effects, cursor animation and keystroke overlays. The mechanism is straightforward enough to state in one sentence: you define steps in a JSON config, webreel drives a headless Chrome instance, captures screenshots at roughly 60fps, and encodes the result with ffmpeg.
The step vocabulary is the interesting part. The README lists clicks, key presses, drags and pauses, and the config examples show the shape of each: an action of click with a text selector, an action of key with a key combo, an action of pause with a duration in milliseconds.
That combination is what makes the output usable as a product demo rather than a screen recording. A cursor is drawn where the click lands, a keystroke overlay shows what was typed, and the timing is deterministic because every pause is written down rather than captured from a human. Two people running the same config on different days get the same video.
The project sits under the Vercel Labs experiments badge, which is a reasonable signal about its maturity. The version line is still early, with published releases at v0.1.2, v0.1.3 and v0.1.4, and the last push to the repository was on 2026-09-16. The repository is not archived, and the licence is Apache 2.0, with the npm package published as webreel and the documentation hosted at webreel.dev.
Init, record, preview: the three commands that cover the loop
The quick start is three commands.
npm install webreel
npx webreel init --name my-video --url https://example.com
npx webreel recordThe init command scaffolds a config file, and the record command runs it. Both are also available without the npx prefix once the package is installed locally, since the README documents the same subcommands in that form.
Preview is the third command and it is the one to reach for while writing a config. It runs a video in a visible browser window without recording, which means you can watch the sequence play out and check the selectors before spending time on an encode.
webreel preview
webreel preview hero
webreel preview hero --verboseRecording has a few modifiers worth knowing. Naming a video on the command line records only that one, passing a config path with -c records from a file other than the default, --watch keeps a recording session running while you edit, and --verbose turns on detailed output. There is also a validate command that checks a config for errors without running it, which is the cheap first pass on a config that came from a teammate or a template.
webreel validate
webreel validate -c custom.config.jsonFor a single video the config has a videos object keyed by name, and each entry carries a url, a viewport, a defaultDelay and a list of steps.
The config file is a schema, so editors can complete it
The generated config is a webreel.config.json, and it carries a $schema key pointing at a JSON schema URL. That single line is what gives the file autocompletion in an editor, and it is the reason the config is pleasant to write by hand rather than assembled in a UI.
{
"$schema": "https://webreel.dev/schema/v1.json",
"videos": {
"my-video": {
"url": "https://example.com",
"viewport": { "width": 1080, "height": 1080 },
"defaultDelay": 500,
"steps": [
{ "action": "pause", "ms": 500 },
{ "action": "click", "text": "Get Started" },
{ "action": "key", "key": "cmd+a", "delay": 1000 }
]
}
}
}Reading the structure, the model is per video rather than per project. A videos object holds named entries, and each entry is an independent recording with its own url, viewport, default delay and step list. The multi-demo example shows why that matters: several videos can be defined in a single config file, each producing its own output from the same page.
The viewport is a width and a height pair, and the mobile-viewport example records at phone dimensions, so aspect ratio is a config value rather than a separate mode. The defaultDelay sets the baseline pause between actions, and individual steps can override it with their own delay, which is how a step that needs the page to settle gets more time.
Steps are declarative and ordered, and the shared-steps example adds an include mechanism for reusing common setup, such as dismissing a cookie consent banner, across several videos. The screenshots example captures PNG stills at chosen points during the recording, so a single run can produce both a video and a set of static images for documentation.
Chrome and ffmpeg land in ~/.webreel on first use
The dependency story is handled for you, and the README is explicit about it. Chrome and ffmpeg are downloaded automatically on first use to ~/.webreel if they are not already installed.
There is an install command for doing it ahead of time, and a --force flag for repairing binaries that are corrupted or broken.
webreel install
webreel install --forceFor CI or for a machine where you would rather use the system copy, the repository ships an .env.example showing two overrides. Setting CHROME_PATH points the recorder at a specific Chrome binary, and setting FFMPEG_PATH does the same for ffmpeg. If either is unset, webreel downloads the Chrome for Testing build or a static ffmpeg build automatically.
# CHROME_PATH=/usr/bin/google-chrome
# FFMPEG_PATH=/usr/local/bin/ffmpegThis arrangement is what makes the tool usable on three different kinds of machine: a developer laptop that gets a private copy, a CI runner that caches the ~/.webreel directory, and a locked down environment where the system binaries are the only permitted ones.
Output format is a config choice rather than a separate tool. MP4 is the default, the gif-output example produces an animated GIF instead, and the webm-output example encodes VP9 WebM. The same capture pipeline feeds all three, so the format decision does not change how the video is scripted.
Composite re-renders without re-recording the browser
The composite command is the one that shows the design is aimed at iteration rather than one-off capture. It re-composites videos from stored raw recordings and timelines without re-recording, which means a change to the cursor theme, the overlay styling or the framing can be applied to an existing capture instead of driving the browser again.
webreel composite
webreel composite heroThat distinction matters for the workflow the project is built around. A raw recording is the expensive part, since it depends on the live page behaving the same way it did when you captured it. The overlay and the final encode are cheap, and they are the parts you iterate on. Splitting them means a designer can change the cursor and the keystroke HUD without waiting for the page to be in the right state again, and without the risk that a rerun of a live page produces a slightly different video.
The examples list makes the same point from another direction. The custom-theme example demonstrates fully customising the cursor overlay and the keystroke HUD appearance, and it is a separate example precisely because it is a different kind of change from the recording itself. The keyboard-shortcuts example shows key combos appearing in the keystroke HUD, and the modifier-clicks example covers clicking with modifier keys held down, which is how a multi-select in a file manager is reproduced.
A pnpm and turbo monorepo with changesets and recorded examples
The repository is a monorepo rather than a single package. The root package.json is private and the real packages live under packages, with apps alongside them, and the workspace uses pnpm with turbo running the tasks. The declared package manager is pnpm at version 10.6.2, and the lockfile is pnpm-lock.yaml.
The scripts show how the repo is maintained. Build, type check, dev and test all fan out through turbo, lint runs eslint over the tree, and format is prettier with a check mode. Tests run under vitest, and husky is wired up as the prepare script with lint-staged running prettier over staged files, so formatting is enforced at commit time rather than in review.
Releases are handled with changesets, which is the part worth understanding if you depend on the package. The changeset command creates a changeset entry, the ci:version script runs changeset version and reinstalls with a non frozen lockfile, and ci:publish builds and then runs changeset publish. Because the root package is private and the public artefact is the npm package, the versioning flow is the standard changesets flow, with a version bump and a changelog driven by the entries contributors write.
There are also scripts for the examples themselves. record-examples.sh records the example videos, and sync-examples.sh synchronises them, and the README embeds the results in the documentation so a change to the tool is visible in the example gallery. The examples directory contains a folder per scenario, covering form filling, drag and drop, page scrolling, mobile viewport, multi video configs, shared steps, screenshots, GIF output and WebM output, plus a hello-world that opens a landing page and clicks the call to action button.
Where webreel is the wrong tool
Three boundaries are clear from how the tool works.
The first is state. A webreel video is a literal sequence of steps against a URL, not a session that adapts. If the page depends on a logged in account, on a payment step, or on anything that changes between the moment you record and the moment someone watches, the recording will not reproduce. The examples are all public or self contained pages, and that is not a coincidence.
The second is content that is not a browser interaction. A demo that needs a camera, a physical device, a terminal window or a person talking is out of scope, because the capture pipeline is Chrome frames plus ffmpeg and an overlay.
The third is editorial control. The tool produces what the script says the page did. It has no notion of camera framing, pacing that is not a declared pause, or cuts. Someone who wants a polished product video with intent will still edit the output, and the editing step is not part of this pipeline.
There is also the question of maintenance, and it deserves a straight answer. The published releases are v0.1.2, v0.1.3 and v0.1.4, all dated in the first days of March, while the last push was on 2026-09-16. The project is not archived and it carries a Labs experiment badge, but the gap between the newest release and the last commit means the npm version you install may be well behind the working tree, and the pre one version tag is a fair signal of how early the project is. Pin the version you test against, and expect the command surface to change.
For a team that wants demos regenerated on every release, that is a workable deal, because the value is in the regeneration loop rather than in the tool being permanent.
Editorial conclusion
Adopt webreel if you publish product videos or documentation clips for a web application, and you want them reproducible, reviewable in a pull request and regenerated on every deploy rather than re-recorded by hand. Do not adopt it for content that depends on a logged in user session, on camera, or on anything a scripted click cannot reach. Verify first that the target page renders without interaction, since the recording is a literal sequence of steps rather than a responsive session, then run webreel validate on the config, use webreel preview to check the sequence in a visible browser, and confirm the output opens on the machine your team watches videos on.
Frequently asked questions
What is webreel and how does it record a video?
webreel drives a headless Chrome instance from a JSON config of steps such as clicks, key presses, drags and pauses, captures screenshots at about 60fps and encodes the result with ffmpeg into MP4 with sound effects, cursor animation and keystroke overlays.
How do I create a webreel project and record it?
Run npm install webreel, then npx webreel init --name my-video --url https://example.com to scaffold a webreel.config.json, and npx webreel record to produce the video. The config also carries a $schema key so editors can complete it.
Do I need to install Chrome and ffmpeg for webreel?
No. Chrome and ffmpeg are downloaded automatically on first use to ~/.webreel if they are not already installed, and you can run webreel install ahead of time or use webreel install --force to repair broken binaries. Setting CHROME_PATH or FFMPEG_PATH in the environment overrides the download with your own binaries.
What can I do before recording a webreel video?
webreel validate checks a config for errors without running it, and webreel preview runs a video in a visible browser window without recording, so you can check selectors and timing before spending time on an encode.
What output formats does webreel support?
MP4 is the default output, the gif-output example produces an animated GIF instead, and the webm-output example encodes VP9 WebM. The screenshots example also captures PNG stills at chosen points during a recording.
What is the difference between webreel record and webreel composite?
Record drives the browser and captures frames, while composite re-composites videos from stored raw recordings and timelines without re-recording, so overlay or framing changes do not require driving the page again.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vercel-labs-webreel)Was this useful?