Product demo videos,
as build artifacts.
Describe your demo in files. Capture your real app in CI. Let the voiceover drive the cut. Then regenerate the whole film — in every language — on every release.
The pipeline behind Screenwright shipped a 4-minute product film in 21 locales — captured from the real app, regenerated from files, directed once, by one developer. Watch it in the wild →
Demo videos rot.
Every sprint moves a button. Every new market needs a new voice. Whatever you recorded last quarter is already lying about your product.
Beautiful. Once.
Hand-recorded demos age the day you ship. Re-recording means re-doing the cursor choreography, the zooms, the voiceover — per language. Nobody does. So the video lies.
Captured by hand, frozen in time.
Chrome-extension captures can't run in CI. The video is an export, not an artifact. When the UI changes, a human clicks through everything again.
They already walk the product. Perfectly. Every time.
Your test suite performs the exact flow your demo needs, on every commit. Screenwright turns that determinism into a film — with a voice, a cut, and a director.
Four files. One film. Every release.
Everything is a file in your repo. The video is output — never input.
The voice commands the cut
Write the script as beats. TTS reads each line and measures it — every shot takes exactly the time its line takes. Change a sentence, the whole film re-times itself. Durations are never hand-written. Anywhere.
// voice/locales/en-US.json
"b12": {
"text": "And here’s the invoice total — VAT already included.",
"marks": { "total": 4 } // word the camera reacts to
}In your CI, from your real app
Declarative Playwright flows with selectors derived from your i18n keys — never transcribed text. A frozen clock and seeded data make every capture identical. The runner executes inside your CI: what leaves is a capture bundle you can inspect — redacted before upload.
{ "op": "fill", "target": { "i18nKey": "invoice.form.customer" }, "value": "Acme GmbH" },
{ "op": "click", "target": { "role": "button", "i18nKey": "invoice.actions.issue" } },
{ "op": "screenshot", "id": "invoice-issued",
"elements": ["invoice.total"] } // bounding box recorded per localeDirect it in 20 minutes
A timeline editor in beat-space: click a word, drag a rectangle, and you've made a punch-in — anchored to the word and the UI element, not to seconds and pixels. Your polish survives every regeneration.
{ "op": "punch_in",
"at": { "type": "word", "beat": "b12", "mark": "total", "offsetMs": -120 },
"target": { "type": "element", "element": "invoice.total", "padding": 0.18 },
"params": { "zoom": 1.9, "ease": "punch" } }
// lands on “Gesamtbetrag” in German, on “total” in EnglishForever. In every language.
New release? The GitHub Action re-captures and re-renders. New market? One command. If the demo breaks, demo-lint fails your build and names the exact step, key and locale — a free end-to-end test that happens to produce a film.
# .github/workflows/release.yml - uses: screenwright/capture-action@v1 with: film: invoice-tour locales: all # 21 locales, one direction
An editor that doesn't betray you
at the next render.
Other tools make you choose: reproducible or beautiful. Screenwright's editor writes an overrides file, anchored to your script and your UI — so the machine can regenerate everything and your taste stays.
Beat-space timeline
The ruler is the voiceover, not the clock. You physically cannot type a duration — the voice owns time, your keyframes own attention.
Word + element anchors
Punch-ins fire on the spoken word and frame the actual DOM element, per locale. German text runs 18% longer? The crop adapts. Or warns you. Never lies.
Reconciliation, not conflicts
UI changed under a keyframe? Orphaned anchors are listed with a suggested re-anchor — accept or reject per item. Nothing moves silently, nothing is silently lost.
One direction, 21 locales
A single structural timeline. Switch locale to audit any market; lint flags the punch-in that would crop the Japanese UI — computed from element maps, before rendering.
Agent-native (MCP)
Your coding agent writes the first cut: it probes your app, drafts script + shots, reads contact sheets frame by frame. You direct the last 20 minutes — the fun part.
QA that watches every beat
A vision jury reviews one frame per beat, per locale — currency, language, coherence. An ASR pass re-listens to every line and diffs it against the script.
Priced per film. Never per render.
Rebuilding your film on every release is the whole point — we will never tax it. Rebuilds are free. Seats never gate your devs. Languages are how you grow.
- 1 active film
- 2 locales
- Cloud render + hosting
- Discreet end-card watermark
- 3 active films
- 5 locales per film
- Visual editor, no watermark
- GitHub Action + demo-lint
- CDN hosting + embed player
- 10 active films
- Unlimited locales (fair use)
- QA jury + ASR — monthly allowance, then metered
- Light/poster/VTT variants
- Priority render queue
- Self-hosted capture runner
- SSO / SAML, audit log
- Bring your own TTS key
- SLA + private support
The capture + assembly CLI is Apache-2.0, free forever, and works entirely on your machine — npx screenwright init
The questions engineers ask first.
Do you need access to our staging environment?
No. The primary capture mode runs inside your CI — the same place your e2e tests already run. The runner is a pinned Docker image; what leaves your infrastructure is a capture bundle (screenshots, element maps, event logs) that you can inspect, and that is redacted before upload if you declare regions to blur.
What happens when our UI changes?
That's the product. The next capture re-measures every element; keyframes anchored to
elements follow their new bounding boxes automatically. If an element disappears,
demo-lint fails the build and names the exact shot, element and locale —
a silently wrong film is the one thing Screenwright refuses to produce.
How do translations work?
You direct once. The script is a structure with stable beat IDs; each locale carries its own text, word marks and TTS pronunciation hints. Timing is re-measured per language, and the whole film re-times itself — a punch-in fires on “total” in English and on “Gesamtbetrag” in German, because it's anchored to the mark, not the word index.
Which voices? Who owns the audio?
Gemini TTS by default — clean commercial terms; on the paid tier your text is never used for training, and output carries an inaudible SynthID watermark. ElevenLabs via bring-your-own-key. Voices are pinned per film: a provider retiring a voice can never silently change your narrator. You own your masters — they're portable MP4s.
Can we self-host?
The capture + assembly CLI is Apache-2.0 and runs entirely on your hardware. The cloud adds the visual editor, render farm, TTS ops, QA jury and hosting. Enterprise plans include a self-hosted capture runner for air-gapped staging.
Your demo, always current.
In every language you sell in.
Screenwright is in private preview. Early teams get onboarding with the founders and lifetime preview pricing.