When a feature demo takes far more takes than it should, the cause is often the app, not the person recording it. A toast from a background job slides in. A table sorts differently than it did last time. A timestamp says "3 minutes ago" in one take and "11 minutes ago" in the next. A dev overlay sits in the corner of every frame.
Developers already know this kind of problem. It's the same thing that makes tests flaky: state you don't control. So the fix is the same too. Treat the demo environment like a test fixture: known data, a known starting point, and nothing that changes between runs.
This post is about that setup. It helps a lot if you record your screen, and most of it still helps if you create demo videos from code instead.
Key takeaways
- Most demo retakes come from app state you don't control, the same thing that makes tests flaky.
- A seed script plus a reset command means every take starts from exactly the same state.
- Fixed dates, a fixed clock, a production build and a fixed window size remove most surprises on screen.
- Demo the feature flags your users actually have, and check the flow with a scripted walk through before recording.
Contents
- Key takeaways
- What "deterministic" means for a demo
- 1. A seed script you can run in one command
- 2. A reset command
- 3. Pin the screen
- 4. Freeze time and quiet the background
- 5. Match feature flags to what users will see
- 6. Check it like a test
- If you don't record at all
- FAQ
What "deterministic" means for a demo
A test is deterministic when the same input gives the same output every run. For a demo, that means the same data, the same screen and the same timing every time. Things that commonly break it:
- Relative times ("2 hours ago") and today's date
- Random IDs, avatars or colours generated per user
- Sort orders that depend on when rows were inserted
- Background jobs, websockets and polling that push updates mid take
- Feature flags that differ between your machine and production
- UI that only exists in development: error overlays, devtools buttons, debug banners
Click through your flow once and write down every one of these you see. That list is your to do list for the rest of this post.
Here's how each one maps to a fix covered below:
| Source of flakiness | Fix |
|---|---|
| Relative times and today's date | Fixed demo day in the seed data, plus a fixed clock |
| Random IDs, avatars or colours | Fixed values in the seed script |
| Insert order sorting | Explicit positions or created dates |
| Background jobs, websockets, polling | A demo mode that turns live updates off |
| Flags that differ from production | Flag values checked in with the demo config |
| Development only UI | A production build run locally |
1. A seed script you can run in one command
Create the demo data in code, not by clicking around in the app. A seed script is reviewable, repeatable and safe to commit, as long as it holds no real customer data.
// scripts/seed-demo.js
const DEMO_DAY = new Date("2026-03-02T10:00:00Z");
const companies = [
{ name: "Northwind Studio", plan: "Team", seats: 12 },
{ name: "Harbor and Pine", plan: "Solo", seats: 1 },
{ name: "Lumen Labs", plan: "Team", seats: 8 },
];
function daysBefore(days) {
return new Date(DEMO_DAY.getTime() - days * 86400000);
}
A few rules that make seed data look right on camera:
- Believable names. "Test Co" and "asdf" pull attention away from the feature. Made up but plausible names don't.
- Fixed dates. Build every date from one fixed demo day, so "last week" means the same thing every time you run it.
- Stable order. Give rows explicit positions or created dates, so lists don't reshuffle between takes.
- Nothing real. No customer names, emails or internal URLs, even ones that look harmless.
2. A reset command
The seed script gets you a good starting state once. The reset command gets you back to it after every take.
{
"scripts": {
"demo:reset": "node scripts/reset-demo.js && node scripts/seed-demo.js"
}
}
Scope the reset to a demo account or tenant, and make it refuse to run against production. The whole point is that take two starts exactly like take one, without you undoing by hand whatever the last take changed.
3. Pin the screen
- Run a production build locally. Development builds often show framework overlays and indicators. A production build looks like what users see.
- Fix the window size. If you automate the flow with Playwright, set the viewport in the config. If you click by hand, keep one browser profile just for demos: no extensions, no bookmarks bar, no personal logins.
- Pick one zoom level and keep it for every scene and every video.
- Quiet the OS. Turn on Do Not Disturb or Focus, close chat apps, and hide anything on the desktop.
4. Freeze time and quiet the background
Relative timestamps are the classic flaky demo problem. If you drive the flow with Playwright, its clock API can pin the time:
await page.clock.setFixedTime(new Date("2026-03-02T10:00:00Z"));
If you click by hand, add a demo mode to the app that reads a fixed "now" from an environment variable. While you're there, turn off the things that push surprise updates into the frame:
if (process.env.DEMO_MODE === "1") {
// fixed clock, no live updates, no onboarding tours
}
Stub slow third party calls with fixed responses too, so a spinner never hangs in the middle of a scene.
5. Match feature flags to what users will see
Demo the state your users actually get. If a flag is on locally but off for customers, the video promises something they can't find. Write the flag values the demo depends on into the demo config, so they're checked in next to the seed data and not living in someone's local settings.
6. Check it like a test
Before you record, walk the flow once with a script. A short Playwright test that clicks through each step and takes a screenshot will catch most surprises: a missing row, a wrong label, a banner you forgot about. Playwright can also record video of a test run, which is meant for debugging, so check whether the quality is good enough for your use before you rely on it.
A short checklist for the day you record:
- [ ]
npm run demo:resetran without errors - [ ] Production build, fixed window size, one zoom level
- [ ] Clock fixed, live updates off
- [ ] Flags match production
- [ ] Screenshot walk through looked right
If you don't record at all
Some tools skip screen capture entirely. DemoVexa is one of them: it reads your code and rebuilds the screens as animated React components with sample data, so there's no recording session to stabilise. I wrote a separate guide on creating software demo videos without screen recording on the DemoVexa blog, which covers that workflow from planning to export.
The fixture mindset still applies there. Clear, consistent labels in your UI, realistic sample data and docs that describe each task in order make any demo easier to get right, whether a person or a tool is putting it together.
What usually breaks your demo takes? I'm curious which items people would add to the list above.
FAQ
How do I make demo data look realistic?
Create it with a seed script instead of by hand. Use plausible made up names and numbers, build every date from one fixed demo day, and give rows a fixed order. Keep real customer names, emails and internal URLs out of it completely.
How do I stop timestamps changing between demo takes?
Fix the clock. If you drive the flow with Playwright, page.clock.setFixedTime() pins the time the page sees. If you click by hand, add a demo mode to your app that reads a fixed "now" from an environment variable.
Should I record a demo on a development build or a production build?
A production build, run locally against your demo data. Development builds often show framework overlays, indicators and debug UI that users never see.
Can Playwright record video of a test run?
Yes. Playwright can save a video of each test run, set through the video option in its config or the recordVideo option when creating a browser context. It's designed for debugging, so check whether the quality suits your use before relying on it for a public demo.
How do I keep customer data out of a product demo video?
Use a separate demo account or tenant filled only by your seed script, and check every screen before you record. Tools that rebuild the UI rather than recording it, such as DemoVexa, show sample data instead of your real customers.
Top comments (0)