<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DemoVexa Blog]]></title><description><![CDATA[DemoVexa Blog]]></description><link>https://demovexa.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ad0de5ff3cc31c188c5d48f/7753e815-5651-44f7-b3fd-4defcd03a1a7.png</url><title>DemoVexa Blog</title><link>https://demovexa.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 31 Oct 2026 12:45:11 GMT</lastBuildDate><atom:link href="https://demovexa.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Nightly CI Job for Demo Video Renders: Queueing, Retries and Artifacts]]></title><description><![CDATA[Set up a nightly GitHub Actions job for demo video renders: cron in UTC, concurrency queueing, retries for flaky steps, and artifact retention.]]></description><link>https://demovexa.hashnode.dev/a-nightly-ci-job-for-demo-video-renders-queueing-retries-and-artifacts</link><guid isPermaLink="true">https://demovexa.hashnode.dev/a-nightly-ci-job-for-demo-video-renders-queueing-retries-and-artifacts</guid><category><![CDATA[Devops]]></category><category><![CDATA[video]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[ci-cd]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Thu, 29 Oct 2026 06:08:12 GMT</pubDate><content:encoded><![CDATA[<p>A nightly render job is a scheduled CI workflow that rebuilds your product demo videos from the latest code while nobody is waiting on it, then stores the files somewhere your team can grab them in the morning. The hard parts are not the render command. They are keeping two runs from colliding, retrying the flaky steps without hiding real failures, and not filling your storage with old video. Here is how I set it up in GitHub Actions.</p>
<h2>Key takeaways</h2>
<ul>
<li>Use a <code>schedule</code> trigger plus <code>workflow_dispatch</code>, so you can run the same job by hand.</li>
<li>A <code>concurrency</code> group stops a slow night from overlapping with the next one.</li>
<li>Retry only the steps that fail for outside reasons, like a voiceover API timeout. Never retry the render itself blindly.</li>
<li>Set <code>retention-days</code> on artifacts. Video adds up fast.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#why-render-demos-at-night-at-all">Why render demos at night at all?</a></li>
<li><a href="#what-does-the-workflow-look-like">What does the workflow look like?</a></li>
<li><a href="#how-do-you-stop-runs-from-overlapping">How do you stop runs from overlapping?</a></li>
<li><a href="#which-steps-should-retry">Which steps should retry?</a></li>
<li><a href="#where-should-the-videos-go">Where should the videos go?</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
<h2>Why render demos at night at all?</h2>
<p>Because rendering is slow and nobody needs the result in the next five minutes. A pull request check should stay fast. A demo render pulls fonts, starts a browser or a renderer, generates audio and encodes video, so it belongs in a job that runs once a day against the default branch.</p>
<p>The other reason is drift. If the UI changed today, tonight's render shows it. You find out the demo is stale from a diff, not from a prospect.</p>
<h2>What does the workflow look like?</h2>
<p>Here is a trimmed version of the file I use:</p>
<pre><code class="language-yaml">name: nightly-demo-renders

on:
  schedule:
    - cron: "30 21 * * 1-5"   # 21:30 UTC on weekdays
  workflow_dispatch:

concurrency:
  group: nightly-demo-renders
  cancel-in-progress: false

jobs:
  render:
    runs-on: ubuntu-latest
    timeout-minutes: 90
    strategy:
      fail-fast: false
      max-parallel: 2
      matrix:
        demo: [onboarding, release-notes, cli-quickstart]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - name: Generate voiceover
        run: ./scripts/retry.sh 3 npm run voiceover -- --demo ${{ matrix.demo }}
      - name: Render
        run: npm run render -- --demo ${{ matrix.demo }}
      - uses: actions/upload-artifact@v4
        with:
          name: demo-${{ matrix.demo }}
          path: out/${{ matrix.demo }}.mp4
          retention-days: 14
</code></pre>
<p>A few notes on the choices:</p>
<ol>
<li><strong>Cron is in UTC.</strong> Write the local time in a comment, or you will misread it in six months.</li>
<li><strong>Scheduled runs can start late.</strong> GitHub says scheduled workflows may be delayed at busy times, and the top of the hour is the busiest. I use minute 30 for that reason.</li>
<li><strong>Scheduled runs only use the default branch.</strong> Changing the cron on a feature branch does nothing until it merges.</li>
<li><strong><code>fail-fast: false</code></strong> means one broken demo doesn't cancel the others.</li>
</ol>
<h2>How do you stop runs from overlapping?</h2>
<p>Use a <code>concurrency</code> group. With <code>cancel-in-progress: false</code>, a new run waits for the current one to finish instead of killing it.</p>
<table>
<thead>
<tr>
<th>Setting</th>
<th>What happens when a new run starts</th>
<th>Good for</th>
</tr>
</thead>
<tbody><tr>
<td>No concurrency group</td>
<td>Both run at once</td>
<td>Nothing that writes shared output</td>
</tr>
<tr>
<td><code>cancel-in-progress: false</code></td>
<td>New run queues behind the current one</td>
<td>Nightly renders</td>
</tr>
<tr>
<td><code>cancel-in-progress: true</code></td>
<td>Current run is cancelled</td>
<td>Pull request previews</td>
</tr>
</tbody></table>
<p>For renders I want the queue. A cancelled run halfway through an encode leaves nothing useful.</p>
<h2>Which steps should retry?</h2>
<p>Only the ones that fail for reasons outside your code. In a demo pipeline that usually means:</p>
<ul>
<li>Text to speech calls that time out or hit a rate limit</li>
<li>Downloading fonts, models or other assets</li>
<li>Uploading the finished file to storage</li>
</ul>
<p>GitHub Actions has no built-in retry for a step, so I use a small script:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
# usage: retry.sh &lt;attempts&gt; &lt;command...&gt;
n=$1; shift
for i in $(seq 1 "$n"); do
  "$@" &amp;&amp; exit 0
  echo "attempt $i failed"; sleep $((i * 20))
done
exit 1
</code></pre>
<p>The render step does not retry. If a render fails, something in the code, the script or the selectors changed, and I want that failure in front of me the next morning.</p>
<p>Also set <code>timeout-minutes</code> on the job. The default is 360 minutes, and a renderer stuck on a frame will happily burn all of it.</p>
<h2>Where should the videos go?</h2>
<p>Artifacts are fine for review. They are tied to the run and expire after the retention period, which is what you want for nightly output. Use <code>retention-days</code> to keep them short; the repository or organization setting caps how long they can live.</p>
<p>For files that ship, such as the clip on a release page, copy them to proper storage with a stable name. I keep nightly artifacts for two weeks and only publish a render after someone has watched it.</p>
<p>I wrote more about the planning side, including what to render each night and how to review it, in <a href="https://demovexa.com/blog/overnight-batch-demo-videos?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=wk1026-07">batch rendering demo videos overnight</a>. If you want the render command itself without building the pipeline, the <a href="https://demovexa.com/features?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=wk1026-07">DemoVexa CLI for code-to-video demos</a> is what I built for exactly this job.</p>
<h2>FAQ</h2>
<h3>Can I trigger the nightly job by hand?</h3>
<p>Yes. Add <code>workflow_dispatch</code> under <code>on</code>, and a "Run workflow" button appears on the Actions tab for that workflow.</p>
<h3>Why didn't my scheduled workflow run?</h3>
<p>Check that the file is on the default branch. In public repositories, GitHub also disables scheduled workflows after 60 days with no repository activity.</p>
<h3>Should every demo render in parallel?</h3>
<p>Not always. Renders are CPU heavy and may share API limits for voiceover. <code>max-parallel</code> lets you cap how many matrix jobs run at once.</p>
<h3>How long should I keep nightly videos?</h3>
<p>Long enough to compare against the last few nights. Two weeks works for me; set it with <code>retention-days</code>.</p>
<p><em>Baltej Singh is the founder of DemoVexa, a desktop app and CLI that turns a local codebase into narrated product demo videos. He writes from Delaware, USA.</em></p>
]]></content:encoded></item><item><title><![CDATA[Generate WebVTT Captions from Your Demo Script in CI]]></title><description><![CDATA[Build WebVTT captions from your demo script in CI: valid cue syntax, timings from per-line audio with ffprobe, lint rules, and a GitHub Actions job.]]></description><link>https://demovexa.hashnode.dev/generate-webvtt-captions-from-your-demo-script-in-ci</link><guid isPermaLink="true">https://demovexa.hashnode.dev/generate-webvtt-captions-from-your-demo-script-in-ci</guid><category><![CDATA[WebVTT]]></category><category><![CDATA[Accessibility]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Mon, 26 Oct 2026 06:19:36 GMT</pubDate><content:encoded><![CDATA[<p>If your demo narration lives in a file in the repo, you can build WebVTT captions from it in CI, like any other artifact. The script already has the exact words. You only add timing, a few validation rules and a job that runs on every change. Here is the format, a small Node script that writes the <code>.vtt</code> file, and a GitHub Actions job that checks it.</p>
<h2>Key takeaways</h2>
<ul>
<li>The script is better caption text than a transcript, because product names are already spelled right.</li>
<li>WebVTT is strict about the <code>WEBVTT</code> header, blank lines between cues, and a period before milliseconds.</li>
<li>One audio clip per script line, measured with ffprobe, is a simple and reliable timing source.</li>
<li>CI should fail on overlapping cues, long lines and misspelled product names, not only on syntax.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#why-build-captions-from-the-script-instead-of-the-audio">Why build captions from the script instead of the audio?</a></li>
<li><a href="#what-does-a-valid-webvtt-file-look-like">What does a valid WebVTT file look like?</a></li>
<li><a href="#where-do-the-cue-timings-come-from">Where do the cue timings come from?</a></li>
<li><a href="#what-should-ci-check-before-accepting-the-file">What should CI check before accepting the file?</a></li>
<li><a href="#how-do-you-wire-it-into-github-actions">How do you wire it into GitHub Actions?</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
<h2>Why build captions from the script instead of the audio?</h2>
<p>Because the script is the source of truth and transcription is a guess. Speech recognition is weakest on the words you care about most: product names, API names and button labels. The script has them right. Only timing is missing, and timing is the easier problem.</p>
<p>When narration changes in a pull request, the captions change in the same commit.</p>
<h2>What does a valid WebVTT file look like?</h2>
<p>A WebVTT file is UTF-8 text that starts with <code>WEBVTT</code>, followed by cues separated by blank lines.</p>
<pre><code class="language-text">WEBVTT

intro-1
00:00:00.000 --&gt; 00:00:03.200
Open Settings and choose Billing.
</code></pre>
<p>Each cue has an optional identifier, a timing line and one or more lines of text. Hours are optional, and the cue text can't contain the timing arrow. Escape <code>&amp;</code>, <code>&lt;</code> and <code>&gt;</code> as entities. The differences from SRT cause most conversion bugs:</p>
<table>
<thead>
<tr>
<th></th>
<th>WebVTT</th>
<th>SRT</th>
</tr>
</thead>
<tbody><tr>
<td>Header</td>
<td><code>WEBVTT</code> required</td>
<td>None</td>
</tr>
<tr>
<td>Millisecond separator</td>
<td>Period</td>
<td>Comma</td>
</tr>
<tr>
<td>Cue numbers</td>
<td>Optional identifiers</td>
<td>Sequential numbers</td>
</tr>
<tr>
<td>Positioning</td>
<td>Cue settings such as <code>line</code> and <code>align</code></td>
<td>No standard positioning</td>
</tr>
<tr>
<td>HTML <code>&lt;track&gt;</code> support</td>
<td>Yes</td>
<td>No</td>
</tr>
</tbody></table>
<h2>Where do the cue timings come from?</h2>
<p>The simplest reliable source is one audio clip per script line. Each clip's duration gives the cue length, and a fixed pause between clips gives the gap. Many text-to-speech APIs can also return word timings.</p>
<p>Here is the per-line version, reading <code>demo/script.json</code>, a list of <code>{ id, text, audio }</code> objects:</p>
<pre><code class="language-js">import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { execFileSync } from "node:child_process";

const lines = JSON.parse(readFileSync("demo/script.json", "utf8"));
const GAP = 0.2; // seconds between clips
const duration = (f) =&gt; Number(execFileSync("ffprobe", ["-v", "error",
  "-show_entries", "format=duration", "-of", "csv=p=0", f], { encoding: "utf8" }));
const ts = (s) =&gt; new Date(Math.round(s * 1000)).toISOString().slice(11, 23);
const esc = (t) =&gt; t.replace(/&amp;/g, "&amp;amp;").replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;");

let t = 0, out = "WEBVTT\n";
for (const l of lines) {
  const d = duration(l.audio);
  out += `\n${l.id}\n${ts(t)} --&gt; ${ts(t + d)}\n${esc(l.text)}\n`;
  t += d + GAP;
}
mkdirSync("dist", { recursive: true });
writeFileSync("dist/demo.en.vtt", out);
</code></pre>
<p>The <code>toISOString</code> trick formats times as <code>HH:MM:SS.mmm</code> for videos under 24 hours. This only works if the video is assembled with the same gap, so keep <code>GAP</code> in a config both steps read.</p>
<h2>What should CI check before accepting the file?</h2>
<p>Valid WebVTT can still be bad captions. My check script fails the build when:</p>
<ol>
<li>The first line is not <code>WEBVTT</code>.</li>
<li>A cue ends before it starts, or overlaps the next one.</li>
<li>A cue is shorter than one second.</li>
<li>A line exceeds 42 characters, or a cue has more than two lines. That is my house rule, close to common broadcast guidance.</li>
<li>A product name from a glossary file appears with different capitalization, such as "Add Seat" when the button says "Add seat".</li>
<li>The last cue ends after the rendered video does.</li>
</ol>
<h2>How do you wire it into GitHub Actions?</h2>
<p>Run both scripts on pull requests that touch the demo.</p>
<pre><code class="language-yaml">name: captions
on:
  pull_request:
    paths: ["demo/**"]
jobs:
  vtt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: sudo apt-get update &amp;&amp; sudo apt-get install -y ffmpeg
      - run: node scripts/build-vtt.mjs &amp;&amp; node scripts/check-vtt.mjs
      - uses: actions/upload-artifact@v4
        with:
          name: captions
          path: dist/*.vtt
</code></pre>
<p>If audio is in Git LFS, set <code>lfs: true</code> on checkout, or ffprobe reads pointer files. On the page, attach the file with a <code>&lt;track kind="captions"&gt;</code> element, served as <code>text/vtt</code>.</p>
<p>In DemoVexa, I took a different route: captions come from the same Script step as the voiceover and export as SRT or VTT, as the <a href="https://demovexa.com/faq?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=wk1026-02">FAQ on caption and video export formats</a> lists. The pipeline above is what I'd build when narration lives elsewhere. Then read why <a href="https://demovexa.com/blog/srt-vtt-subtitles-youtube-seo?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=wk1026-02">uploading SRT and VTT subtitles helps YouTube index your demo</a>.</p>
<h2>FAQ</h2>
<h3>Can I convert an SRT file to WebVTT?</h3>
<p>Yes. <code>ffmpeg -i in.srt out.vtt</code> adds the header and switches the comma before milliseconds to a period.</p>
<h3>Why doesn't my track show in the browser?</h3>
<p>Check that the file starts with <code>WEBVTT</code> and is served as <code>text/vtt</code>. Cross-origin files also need CORS headers and <code>crossorigin</code> on the video.</p>
<h3>Should captions match the narration word for word?</h3>
<p>For a scripted demo, yes. Viewers notice when captions and voice disagree.</p>
<h3>What about other languages?</h3>
<p>Build one file per translated script, such as <code>demo.de.vtt</code>, each with its own <code>&lt;track&gt;</code> and <code>srclang</code>.</p>
<p><em>Baltej Singh is the founder of DemoVexa, a desktop app and CLI that turns a local codebase into narrated product demo videos. He writes from Delaware, USA.</em></p>
]]></content:encoded></item><item><title><![CDATA[Code-to-Video Demo Tools Compared: DemoVexa, PushPlay and Screenwright]]></title><description><![CDATA[Three tools make product demo videos from developer artifacts instead of manual recording. They start from different things: a local code folder, GitHub…]]></description><link>https://demovexa.hashnode.dev/code-to-video-demo-tools-compared-demovexa-pushplay-and-screenwright</link><guid isPermaLink="true">https://demovexa.hashnode.dev/code-to-video-demo-tools-compared-demovexa-pushplay-and-screenwright</guid><category><![CDATA[devtools]]></category><category><![CDATA[webdev]]></category><category><![CDATA[video]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Sat, 17 Oct 2026 06:06:41 GMT</pubDate><content:encoded><![CDATA[<p><em>Three tools make product demo videos from developer artifacts instead of manual recording. They start from different things: a local code folder, GitHub pull requests and Playwright tests.</em></p>
<p>A small group of tools now make product demo videos from code rather than from someone clicking through the app with a screen recorder. If you're a developer looking at them, the useful question isn't "which is best" but "which one starts from what I already have".</p>
<p>Disclosure first: I make DemoVexa, one of the three. I've tried to describe the other two accurately from their own websites and repositories (checked October 2026) and to say where each one is a better fit. Features change quickly in this space, so check each tool's site before deciding.</p>
<h2>Key takeaways</h2>
<ul>
<li>DemoVexa reads a local code folder and recreates the UI as animation. It's a desktop app and CLI.</li>
<li>PushPlay works from GitHub. It watches merged pull requests and renders the changed screens from React components.</li>
<li>Screenwright works from Playwright tests. It records your real running app, then adds cursor animation and voiceover.</li>
<li>Choose by your starting point: a local folder, a GitHub workflow, or an existing Playwright test suite.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#heading-key-takeaways">Key takeaways</a></li>
<li><a href="#heading-what-does-each-tool-start-from">What does each tool start from?</a></li>
<li><a href="#heading-how-does-demovexa-work">How does DemoVexa work?</a></li>
<li><a href="#heading-how-does-pushplay-work">How does PushPlay work?</a></li>
<li><a href="#heading-how-does-screenwright-work">How does Screenwright work?</a></li>
<li><a href="#heading-recreated-ui-or-recorded-ui">Recreated UI or recorded UI?</a></li>
<li><a href="#heading-how-should-you-choose">How should you choose?</a></li>
<li><a href="#heading-faq">FAQ</a></li>
</ul>
<h2>What does each tool start from?</h2>
<table>
<thead>
<tr>
<th></th>
<th>DemoVexa</th>
<th>PushPlay</th>
<th>Screenwright</th>
</tr>
</thead>
<tbody><tr>
<td>Starts from</td>
<td>A local code folder (or a website and documents)</td>
<td>A GitHub repository and its merged pull requests</td>
<td>Playwright end to end tests</td>
</tr>
<tr>
<td>How screens are made</td>
<td>Recreated as React components and animated with Remotion</td>
<td>Rendered from your React components as animated panels</td>
<td>Recorded from your real running app</td>
</tr>
<tr>
<td>Runs where</td>
<td>Desktop app (macOS, Windows, Linux) or npm CLI</td>
<td>Hosted service, connected to GitHub</td>
<td>Open source CLI (MIT on npm); screenwright.io describes capture in CI</td>
</tr>
<tr>
<td>Framework needs</td>
<td>Any language or framework, per its FAQ</td>
<td>React components</td>
<td>Any web app Playwright can drive</td>
</tr>
<tr>
<td>Typical output</td>
<td>Intros, feature demos, launches in 16:9, 9:16 and 1:1</td>
<td>60 to 90 second videos per PR, plus overview and feature demos</td>
<td>MP4 of a scripted flow through the real app</td>
</tr>
</tbody></table>
<h2>How does DemoVexa work?</h2>
<p>According to its FAQ, you point a workspace at a local code folder, write a brief or let Claude draft one, Claude writes the script, ElevenLabs voices it, and Claude builds the video in React with Remotion. It doesn't take a GitHub URL; you clone the repo first. Since version 0.3.5 a workspace can also come from the product's website and uploaded documents, with less exact screens. Claude is billed by Anthropic on your own account, and ElevenLabs is optional. More detail is on the site for the <a href="https://demovexa.com">desktop app that turns a local code folder into product demo videos</a>.</p>
<p><strong>Good fit if</strong> you want marketing style videos (intros, feature demos, launches) in several formats and languages from a local codebase, without connecting a Git host.</p>
<p><strong>Less good fit if</strong> you want videos generated automatically on every merge, or a recording of exactly what the live app does.</p>
<h2>How does PushPlay work?</h2>
<p>According to PushPlay's website, you sign in with GitHub, paste a public repository URL or install its GitHub App on a private repository, and PushPlay indexes your components. When a pull request merges, it reads the diff, writes a script with one scene per user facing change, voices it and renders the changed screens from your React components with your colours and fonts. It produces a 1080p MP4, a shareable page and a comment on the PR. Its site says the Pro plan can generate a video on every merge automatically.</p>
<p><strong>Good fit if</strong> your product is built with React, your team works in GitHub, and you want changelog or client update videos tied to each PR.</p>
<p><strong>Less good fit if</strong> your UI isn't React, or you'd rather not connect a Git host.</p>
<h2>How does Screenwright work?</h2>
<p>Screenwright's GitHub README describes an open source CLI that turns Playwright end to end tests into product demo videos. It generates a demo scenario from a test (with an LLM prompt or a Claude Code skill), lets you edit it, records the flow against your running app, and composites cursor animation and voiceover into an MP4. The screenwright.io site describes a fuller workflow: declarative flows, capture inside your CI, re-rendering on each release through a GitHub Action, and a demo lint step that fails the build when a demo breaks.</p>
<p><strong>Good fit if</strong> you already have Playwright tests, want the video to show your real running app, and like treating demos as build artifacts in CI.</p>
<p><strong>Less good fit if</strong> you don't have end to end tests, or your app is hard to run in a clean, seeded state.</p>
<h2>Recreated UI or recorded UI?</h2>
<p>This is the main technical difference:</p>
<ul>
<li><strong>Recreated</strong> (DemoVexa, PushPlay): no running app or login needed, sample data by design, and each format can have its own layout. The screens are a reproduction, so you should check them against the real product.</li>
<li><strong>Recorded</strong> (Screenwright): the pixels are your real app, so there's no question of accuracy. You need a running, seeded environment, and layout changes per format are harder.</li>
</ul>
<p>Neither is better in general. It depends on whether you value the real pixels or a polished, easy to update animation.</p>
<h2>How should you choose?</h2>
<ul>
<li><strong>You have Playwright tests and CI:</strong> try Screenwright.</li>
<li><strong>You're on React and GitHub and want per PR videos:</strong> try PushPlay.</li>
<li><strong>You want intros and feature demos from a local folder, any stack, several formats and languages:</strong> try DemoVexa.</li>
</ul>
<p>If you're still deciding between this whole category and an ordinary screen recorder, the post on <a href="https://demovexa.com/blog/software-demo-without-screen-recording">making a software demo without screen recording</a> covers that choice.</p>
<h2>FAQ</h2>
<h3>What tools make product demo videos from code?</h3>
<p>DemoVexa, PushPlay and Screenwright are three examples. DemoVexa reads a local code folder, PushPlay works from GitHub pull requests and React components, and Screenwright turns Playwright tests into recorded demos.</p>
<h3>Does DemoVexa connect to GitHub?</h3>
<p>No. According to its FAQ, DemoVexa doesn't take a GitHub URL or connect to GitHub. You clone the repository and point a workspace at the local folder.</p>
<h3>Is Screenwright open source?</h3>
<p>Its CLI is published on npm under the MIT license, with its source on GitHub. The screenwright.io site also describes CI capture and a hosted workflow; check the site for what's included.</p>
<h3>Do code-to-video tools work with any framework?</h3>
<p>It varies. PushPlay renders React components. Screenwright works with web apps Playwright can drive. DemoVexa's FAQ says it works with a code folder in any language or framework.</p>
]]></content:encoded></item><item><title><![CDATA[How Do You Make One Product Demo in Many Languages?]]></title><description><![CDATA[Translating a demo video is more than swapping the voiceover. Here's what has to change, what should stay the same, and how to plan for it before you make…]]></description><link>https://demovexa.hashnode.dev/how-do-you-make-one-product-demo-in-many-languages</link><guid isPermaLink="true">https://demovexa.hashnode.dev/how-do-you-make-one-product-demo-in-many-languages</guid><category><![CDATA[localization]]></category><category><![CDATA[SaaS]]></category><category><![CDATA[video]]></category><category><![CDATA[i18n]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Sat, 17 Oct 2026 06:03:30 GMT</pubDate><content:encoded><![CDATA[<p><em>Translating a demo video is more than swapping the voiceover. Here's what has to change, what should stay the same, and how to plan for it before you make the first version.</em></p>
<p>Once a product demo works in English, the next request is often "can we have it in Spanish, German and Japanese too?" It sounds like a translation job. In practice it touches the script, the voice, the captions, the timing, the fonts and sometimes the layout.</p>
<p>This post goes through each of those, with a plan you can use whichever tool you make the video with.</p>
<p>Disclosure: I build DemoVexa, a tool to <a href="https://demovexa.com">create multilingual product demo videos from code</a>, and I describe how it handles languages near the end.</p>
<h2>Key takeaways</h2>
<ul>
<li>A localised demo has five layers: script, voice, on screen text, captions and timing. Each needs its own decision.</li>
<li>Keep your product name and the labels on your app's screens as they are, unless your app itself is localised.</li>
<li>Translated speech is rarely the same length as English, so scenes need to re-time to the new voiceover.</li>
<li>Scripts that aren't written in Latin letters, and right to left languages, can need different fonts and layouts.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#heading-key-takeaways">Key takeaways</a></li>
<li><a href="#heading-what-actually-changes-between-language-versions">What actually changes between language versions?</a></li>
<li><a href="#heading-should-you-translate-the-script-or-rewrite-it">Should you translate the script or rewrite it?</a></li>
<li><a href="#heading-how-do-you-handle-timing-when-the-voiceover-changes-length">How do you handle timing when the voiceover changes length?</a></li>
<li><a href="#heading-which-voice-should-you-use-for-each-language">Which voice should you use for each language?</a></li>
<li><a href="#heading-what-about-captions-fonts-and-right-to-left-languages">What about captions, fonts and right to left languages?</a></li>
<li><a href="#heading-how-does-demovexa-handle-languages">How does DemoVexa handle languages?</a></li>
<li><a href="#heading-a-simple-plan-for-your-first-localised-demo">A simple plan for your first localised demo</a></li>
<li><a href="#heading-faq">FAQ</a></li>
</ul>
<h2>What actually changes between language versions?</h2>
<p>Here's how the layers of a demo usually behave when you make another language version:</p>
<table>
<thead>
<tr>
<th>Layer</th>
<th>Changes per language?</th>
<th>Notes</th>
</tr>
</thead>
<tbody><tr>
<td>Spoken voiceover</td>
<td>Yes</td>
<td>New script and a voice that speaks the language natively</td>
</tr>
<tr>
<td>Captions and subtitles</td>
<td>Yes</td>
<td>Must match the new voiceover, not the English one</td>
</tr>
<tr>
<td>Title and end cards</td>
<td>Yes</td>
<td>Short text, but it's the first and last thing people read</td>
</tr>
<tr>
<td>Product name</td>
<td>Usually no</td>
<td>Brand names normally stay the same</td>
</tr>
<tr>
<td>App screen labels</td>
<td>Only if your app is localised</td>
<td>Showing translated labels your app doesn't have is misleading</td>
</tr>
<tr>
<td>Timing of clicks and scene changes</td>
<td>Yes</td>
<td>Translated sentences run longer or shorter</td>
</tr>
<tr>
<td>Fonts</td>
<td>Sometimes</td>
<td>Arabic, Hindi, Japanese, Thai and others need fonts that cover the script</td>
</tr>
<tr>
<td>Layout direction</td>
<td>Sometimes</td>
<td>Arabic, Hebrew, Persian and Urdu read right to left</td>
</tr>
</tbody></table>
<p>The row people most often get wrong is screen labels. If your product only ships in English, a Spanish demo should still show English labels, with the Spanish voice explaining them. Otherwise the video shows an app that doesn't exist.</p>
<h2>Should you translate the script or rewrite it?</h2>
<p>Rewrite it, using the English version as a brief. A word for word translation keeps English sentence shapes and idioms that sound odd when spoken. A native speaker, or a model you then check with a native speaker, should write the line the way someone would actually say it.</p>
<p>A few rules help:</p>
<ul>
<li>Keep each line to one idea, so it can be timed to one action on screen.</li>
<li>Avoid puns and wordplay in the source script. They rarely survive.</li>
<li>Write numbers, dates and currencies the way that market expects.</li>
<li>Keep UI names in quotes or as they appear, so viewers can match them to the screen.</li>
</ul>
<h2>How do you handle timing when the voiceover changes length?</h2>
<p>This is the part that breaks screen recordings. If you recorded the English demo with clicks matched to English narration, a German voiceover that runs longer will drift out of sync. You either re-record per language or edit each version by hand.</p>
<p>Animated demos made with tools that create product demo videos from code have an easier time, because the timing is data rather than footage. Each scene can be stretched or shortened to the new voiceover, and clicks can be placed on the matching word again. That's how <a href="https://demovexa.com/blog/multilingual-product-demo-videos">multilingual product demo videos made from code</a> stay in sync without a re-record.</p>
<h2>Which voice should you use for each language?</h2>
<p>Use a native speaker for each language, whether that's a person or an AI voice. A voice cloned from an English speaker will usually keep an English accent in other languages, which some audiences find distracting.</p>
<p>If you use AI voices, keep one chosen voice per language and reuse it for every video, so your Spanish videos sound like a series.</p>
<h2>What about captions, fonts and right to left languages?</h2>
<p>Captions should be generated from the new voiceover so they match what's actually said. Subtitle files (SRT or VTT) should be in the video's language too, so platforms like YouTube can label them correctly.</p>
<p>For languages written in a different script, check your brand font. Many brand fonts only cover Latin characters, so you'll need a fallback that covers the script. Right to left languages may also need captions and cards laid out in the other direction.</p>
<h2>How does DemoVexa handle languages?</h2>
<p>This section describes one tool, based on its docs. DemoVexa's <a href="https://demovexa.com/docs/more/languages">languages documentation</a> lists 85 languages, the ones ElevenLabs' Eleven v3 model speaks. Claude writes the voiceover, captions, title and end cards and posts in the chosen language, while the product name, module names and app labels stay as they are. For scripts like Arabic, Hindi, Japanese, Thai or Russian, the build agent uses a font that covers the script when the brand font doesn't, and Arabic, Hebrew, Persian, Urdu, Pashto and Sindhi are laid out right to left. Native voices are listed first on the Voice step, and you can make a version of an existing project in another language from the project menu. Each language version counts as a new video on its plans.</p>
<h2>A simple plan for your first localised demo</h2>
<ol>
<li>Finish the English version first and get it approved.</li>
<li>Pick two languages based on where your users or leads actually are.</li>
<li>Have the script rewritten, not translated, and checked by a native speaker.</li>
<li>Pick one native voice per language and save it for later videos.</li>
<li>Rebuild or re-time the video to each new voiceover.</li>
<li>Watch every version end to end with captions on before you publish.</li>
</ol>
<h2>FAQ</h2>
<h3>Can I translate a product demo video without re-recording it?</h3>
<p>With a screen recording you can add a new voiceover and subtitles, but clicks and scene changes may drift out of sync. Demos animated from code can re-time each scene to the new voiceover, which avoids re-recording.</p>
<h3>Should the app's screen labels be translated in a localised demo?</h3>
<p>Only if your app is actually available in that language. Otherwise keep the real labels and let the voiceover and captions explain them in the viewer's language.</p>
<h3>How many languages does DemoVexa support?</h3>
<p>According to its docs, 85 languages, which are the languages of the ElevenLabs Eleven v3 voice model. All of them are available on every plan.</p>
<h3>Do I need an AI voice to make a demo in another language?</h3>
<p>No. You can record the translated script yourself or with a native speaker, or make a version with captions and music only.</p>
]]></content:encoded></item><item><title><![CDATA[How Does a Code-Generated Product Demo Work?]]></title><description><![CDATA[What happens between reading a codebase and rendering an MP4, where the accuracy comes from, and what you still need to check yourself. Most product demo…]]></description><link>https://demovexa.hashnode.dev/how-does-a-code-generated-product-demo-work</link><guid isPermaLink="true">https://demovexa.hashnode.dev/how-does-a-code-generated-product-demo-work</guid><category><![CDATA[webdev]]></category><category><![CDATA[React]]></category><category><![CDATA[devtools]]></category><category><![CDATA[video]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Thu, 15 Oct 2026 17:55:52 GMT</pubDate><content:encoded><![CDATA[<p><em>What happens between reading a codebase and rendering an MP4, where the accuracy comes from, and what you still need to check yourself.</em></p>
<p>Most product demo videos start with someone recording their screen. A code-generated demo starts somewhere else: with the source code of the product. Instead of capturing pixels from a running app, a tool reads the code, works out what the screens look like, and rebuilds them as animation.</p>
<p>That sounds like magic until you break it into steps. Then it's a fairly ordinary pipeline, and knowing the steps helps you judge where these tools are strong and where they need a human.</p>
<p>Disclosure: I build DemoVexa, a desktop app for <a href="https://demovexa.com">making product demo videos from your codebase</a>, so I'll use it as the worked example. The general process applies to other tools in this category too.</p>
<h2>Key takeaways</h2>
<ul>
<li>A code-generated demo rebuilds your screens from the source code instead of recording a running app.</li>
<li>The pipeline is roughly: read the code, write a brief and script, add a voice, build animated scenes, render.</li>
<li>Accuracy comes from reading real labels, routes and components, so the video uses the same words as the app.</li>
<li>You still need to review the script, the scene plan and the final video, because the tool can misread intent.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#key-takeaways">Key takeaways</a></li>
<li><a href="#what-is-a-code-generated-product-demo">What is a code generated product demo?</a></li>
<li><a href="#what-does-the-pipeline-look-like">What does the pipeline look like?</a></li>
<li><a href="#where-does-the-accuracy-come-from">Where does the accuracy come from?</a></li>
<li><a href="#what-does-it-get-wrong-and-what-do-you-still-review">What does it get wrong, and what do you still review?</a></li>
<li><a href="#when-is-a-code-generated-demo-a-bad-fit">When is a code generated demo a bad fit?</a></li>
<li><a href="#what-does-this-look-like-in-demovexa">What does this look like in DemoVexa?</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
<h2>What is a code generated product demo?</h2>
<p>It's a video where the screens are recreated from your product's code rather than captured from a screen. The result looks like your app: same layout, same labels, your brand colours. But every frame is drawn by code, which means it can use clean sample data, stay sharp at any size, and be rebuilt when the code changes.</p>
<p>This is different from two things it's often confused with:</p>
<ul>
<li><strong>Screen recording with automation.</strong> Some tools drive a real browser (often with Playwright) and record it. That's still capture, just scripted.</li>
<li><strong>Generic AI video.</strong> Text to video models produce footage from a prompt, but they don't know what your product's screens actually contain.</li>
</ul>
<h2>What does the pipeline look like?</h2>
<p>The details differ between tools, but most code-to-video pipelines follow the same stages.</p>
<table>
<thead>
<tr>
<th>Stage</th>
<th>Input</th>
<th>Output</th>
<th>What a human should check</th>
</tr>
</thead>
<tbody><tr>
<td>Read the product</td>
<td>Code folder, docs, navigation</td>
<td>A model of the screens, names and flows</td>
<td>That the right app and folder were used</td>
</tr>
<tr>
<td>Brief</td>
<td>A one line idea plus what was learned</td>
<td>Audience, key message, length, formats</td>
<td>That the key message is the one you want</td>
</tr>
<tr>
<td>Script</td>
<td>The brief</td>
<td>Scene by scene narration</td>
<td>Every claim and every UI name</td>
</tr>
<tr>
<td>Voice</td>
<td>The script</td>
<td>Voiceover with word timings</td>
<td>Pronunciation of product names</td>
</tr>
<tr>
<td>Build</td>
<td>Script, timings, code</td>
<td>Animated scenes, often as React components</td>
<td>That each screen matches the real app</td>
</tr>
<tr>
<td>Render</td>
<td>The built scenes</td>
<td>MP4 files in each format</td>
<td>Readability in each format</td>
</tr>
</tbody></table>
<p>The order matters. The voice usually comes before the animation, because the animation is timed to the spoken words. Change the script after the build and you usually need a new voiceover and a rebuild.</p>
<h2>Where does the accuracy come from?</h2>
<p>A screen recording is accurate on the day you record it. A code-generated demo is accurate to the code it read. That has a few practical effects:</p>
<ul>
<li><strong>Labels match.</strong> If the button in your code says "Export", the scene says "Export".</li>
<li><strong>Screens match the current version.</strong> Pull the latest changes, rebuild, and the video follows.</li>
<li><strong>Sample data is a choice.</strong> The tool shows example records instead of whatever happens to be in your database.</li>
<li><strong>Brand comes from the source.</strong> Colours, fonts and logos can be read from code or set once in a brand kit.</li>
</ul>
<p>The limit is that code doesn't explain intent. It tells a tool what a screen contains, not why a user cares. That's why the brief and script steps still matter.</p>
<h2>What does it get wrong, and what do you still review?</h2>
<p>From the way these pipelines work, the usual weak spots are predictable:</p>
<ol>
<li><strong>The story.</strong> A tool can find every feature and still pick the wrong one to lead with.</li>
<li><strong>Claims.</strong> Narration can drift into promises the screen doesn't prove. Strip any line the viewer can't see.</li>
<li><strong>Complex state.</strong> Screens that depend on real data, permissions or third party content are harder to recreate exactly.</li>
<li><strong>Pronunciation.</strong> Product names and acronyms often need a fix in the voice step.</li>
</ol>
<p>A good habit is to review at the cheapest point. Fixing a scene plan takes minutes. Fixing a rendered video means rebuilding it.</p>
<h2>When is a code generated demo a bad fit?</h2>
<p>It isn't the right tool for every video. A screen recording is often simpler when:</p>
<ul>
<li>You need a quick, informal walkthrough for a teammate or a support ticket.</li>
<li>The demo depends on live data, such as a real integration responding.</li>
<li>You don't have access to the code and the product's website doesn't describe it well.</li>
<li>The video is mostly about a person, such as a founder explaining a vision on camera.</li>
</ul>
<h2>What does this look like in DemoVexa?</h2>
<p>DemoVexa is one way to create demo videos from code. It's a desktop app (and a CLI) that runs on your computer. You point a workspace at your code folder, write a brief or let Claude draft one, review the script, pick a voice, and Claude builds the video in React and animates it with Remotion. The docs describe <a href="https://demovexa.com/docs/videos/build">how the build agent recreates your UI</a>: it reads the code, recreates the screens, times clicks to the voiceover, adds captions and an end card, and checks still frames of its own work before rendering a preview. According to the docs, a build usually takes 15 to 30 minutes.</p>
<p>Since version 0.3.5 a workspace can also start from a product's website and uploaded documents, though screens recreated that way are less exact than with the code.</p>
<h2>FAQ</h2>
<h3>Is a code-generated demo the same as a screen recording?</h3>
<p>No. A screen recording captures a running app. A code-generated demo recreates the screens from the source code and animates them, so nothing is captured from a screen.</p>
<h3>Do I need to know React or Remotion to use a code-to-video tool?</h3>
<p>It depends on the tool. Some expect you to write the animation code yourself. Others, like DemoVexa, have a build agent write it, and you change things by editing the brief and script or by asking in plain words.</p>
<h3>Can a code-generated demo show my real customer data?</h3>
<p>It doesn't need to. Because the screens are drawn by code, they use sample data you choose or that the tool generates, which keeps real customer details out of the video.</p>
<h3>What happens when my UI changes?</h3>
<p>You rebuild the video from the updated code instead of recording it again. The rebuilt video still needs a quick review before it replaces the old one.</p>
]]></content:encoded></item><item><title><![CDATA[Treat Your Demo Environment Like a Test Fixture]]></title><description><![CDATA[Most demo retakes come from the state of the app, not from the person recording. Seed it, reset it and pin it down, the same way you would for a test. When…]]></description><link>https://demovexa.hashnode.dev/treat-your-demo-environment-like-a-test-fixture</link><guid isPermaLink="true">https://demovexa.hashnode.dev/treat-your-demo-environment-like-a-test-fixture</guid><category><![CDATA[Productivity]]></category><category><![CDATA[startup]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Thu, 15 Oct 2026 17:39:56 GMT</pubDate><content:encoded><![CDATA[<p><em>Most demo retakes come from the state of the app, not from the person recording. Seed it, reset it and pin it down, the same way you would for a test.</em></p>
<p>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.</p>
<p>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.</p>
<p>This post is about that setup. It helps a lot if you record your screen, and most of it still helps if you <a href="https://demovexa.com">create demo videos from code</a> instead.</p>
<h2>Key takeaways</h2>
<ul>
<li><p>Most demo retakes come from app state you don't control, the same thing that makes tests flaky.</p>
</li>
<li><p>A seed script plus a reset command means every take starts from exactly the same state.</p>
</li>
<li><p>Fixed dates, a fixed clock, a production build and a fixed window size remove most surprises on screen.</p>
</li>
<li><p>Demo the feature flags your users actually have, and check the flow with a scripted walk through before recording.</p>
</li>
</ul>
<h2>Contents</h2>
<ul>
<li><p><a href="#key-takeaways">Key takeaways</a></p>
</li>
<li><p><a href="#what-deterministic-means-for-a-demo">What "deterministic" means for a demo</a></p>
</li>
<li><p><a href="#1-a-seed-script-you-can-run-in-one-command">1. A seed script you can run in one command</a></p>
</li>
<li><p><a href="#2-a-reset-command">2. A reset command</a></p>
</li>
<li><p><a href="#3-pin-the-screen">3. Pin the screen</a></p>
</li>
<li><p><a href="#4-freeze-time-and-quiet-the-background">4. Freeze time and quiet the background</a></p>
</li>
<li><p><a href="#5-match-feature-flags-to-what-users-will-see">5. Match feature flags to what users will see</a></p>
</li>
<li><p><a href="#6-check-it-like-a-test">6. Check it like a test</a></p>
</li>
<li><p><a href="#if-you-dont-record-at-all">If you don't record at all</a></p>
</li>
<li><p><a href="#faq">FAQ</a></p>
</li>
</ul>
<h2>What "deterministic" means for a demo</h2>
<p>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:</p>
<ul>
<li><p>Relative times ("2 hours ago") and today's date</p>
</li>
<li><p>Random IDs, avatars or colours generated per user</p>
</li>
<li><p>Sort orders that depend on when rows were inserted</p>
</li>
<li><p>Background jobs, websockets and polling that push updates mid take</p>
</li>
<li><p>Feature flags that differ between your machine and production</p>
</li>
<li><p>UI that only exists in development: error overlays, devtools buttons, debug banners</p>
</li>
</ul>
<p>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.</p>
<p>Here's how each one maps to a fix covered below:</p>
<table>
<thead>
<tr>
<th>Source of flakiness</th>
<th>Fix</th>
</tr>
</thead>
<tbody><tr>
<td>Relative times and today's date</td>
<td>Fixed demo day in the seed data, plus a fixed clock</td>
</tr>
<tr>
<td>Random IDs, avatars or colours</td>
<td>Fixed values in the seed script</td>
</tr>
<tr>
<td>Insert order sorting</td>
<td>Explicit positions or created dates</td>
</tr>
<tr>
<td>Background jobs, websockets, polling</td>
<td>A demo mode that turns live updates off</td>
</tr>
<tr>
<td>Flags that differ from production</td>
<td>Flag values checked in with the demo config</td>
</tr>
<tr>
<td>Development only UI</td>
<td>A production build run locally</td>
</tr>
</tbody></table>
<h2>1. A seed script you can run in one command</h2>
<p>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.</p>
<pre><code class="language-js">// 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);
}
</code></pre>
<p>A few rules that make seed data look right on camera:</p>
<ul>
<li><p><strong>Believable names.</strong> "Test Co" and "asdf" pull attention away from the feature. Made up but plausible names don't.</p>
</li>
<li><p><strong>Fixed dates.</strong> Build every date from one fixed demo day, so "last week" means the same thing every time you run it.</p>
</li>
<li><p><strong>Stable order.</strong> Give rows explicit positions or created dates, so lists don't reshuffle between takes.</p>
</li>
<li><p><strong>Nothing real.</strong> No customer names, emails or internal URLs, even ones that look harmless.</p>
</li>
</ul>
<h2>2. A reset command</h2>
<p>The seed script gets you a good starting state once. The reset command gets you back to it after every take.</p>
<pre><code class="language-json">{
  "scripts": {
    "demo:reset": "node scripts/reset-demo.js &amp;&amp; node scripts/seed-demo.js"
  }
}
</code></pre>
<p>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.</p>
<h2>3. Pin the screen</h2>
<ul>
<li><p><strong>Run a production build locally.</strong> Development builds often show framework overlays and indicators. A production build looks like what users see.</p>
</li>
<li><p><strong>Fix the window size.</strong> 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.</p>
</li>
<li><p><strong>Pick one zoom level</strong> and keep it for every scene and every video.</p>
</li>
<li><p><strong>Quiet the OS.</strong> Turn on Do Not Disturb or Focus, close chat apps, and hide anything on the desktop.</p>
</li>
</ul>
<h2>4. Freeze time and quiet the background</h2>
<p>Relative timestamps are the classic flaky demo problem. If you drive the flow with Playwright, its clock API can pin the time:</p>
<pre><code class="language-js">await page.clock.setFixedTime(new Date("2026-03-02T10:00:00Z"));
</code></pre>
<p>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:</p>
<pre><code class="language-js">if (process.env.DEMO_MODE === "1") {
  // fixed clock, no live updates, no onboarding tours
}
</code></pre>
<p>Stub slow third party calls with fixed responses too, so a spinner never hangs in the middle of a scene.</p>
<h2>5. Match feature flags to what users will see</h2>
<p>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.</p>
<h2>6. Check it like a test</h2>
<p>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.</p>
<p>A short checklist for the day you record:</p>
<ul>
<li><p>[ ] <code>npm run demo:reset</code> ran without errors</p>
</li>
<li><p>[ ] Production build, fixed window size, one zoom level</p>
</li>
<li><p>[ ] Clock fixed, live updates off</p>
</li>
<li><p>[ ] Flags match production</p>
</li>
<li><p>[ ] Screenshot walk through looked right</p>
</li>
</ul>
<h2>If you don't record at all</h2>
<p>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 <a href="https://demovexa.com/blog/software-demo-without-screen-recording">creating software demo videos without screen recording</a> on the DemoVexa blog, which covers that workflow from planning to export.</p>
<p>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.</p>
<p>What usually breaks your demo takes? I'm curious which items people would add to the list above.</p>
<h2>FAQ</h2>
<h3>How do I make demo data look realistic?</h3>
<p>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.</p>
<h3>How do I stop timestamps changing between demo takes?</h3>
<p>Fix the clock. If you drive the flow with Playwright, <code>page.clock.setFixedTime()</code> 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.</p>
<h3>Should I record a demo on a development build or a production build?</h3>
<p>A production build, run locally against your demo data. Development builds often show framework overlays, indicators and debug UI that users never see.</p>
<h3>Can Playwright record video of a test run?</h3>
<p>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.</p>
<h3>How do I keep customer data out of a product demo video?</h3>
<p>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.</p>
]]></content:encoded></item><item><title><![CDATA[Your Codebase Already Contains Most of Your Demo Script]]></title><description><![CDATA[Routes, labels, fixtures, tests and git history can tell you what to show, in what order, and in which words. Here's how to mine them before you plan a…]]></description><link>https://demovexa.hashnode.dev/your-codebase-already-contains-most-of-your-demo-script</link><guid isPermaLink="true">https://demovexa.hashnode.dev/your-codebase-already-contains-most-of-your-demo-script</guid><category><![CDATA[webdev]]></category><category><![CDATA[React]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[documentation]]></category><dc:creator><![CDATA[DemoVexa]]></dc:creator><pubDate>Thu, 15 Oct 2026 15:28:00 GMT</pubDate><content:encoded><![CDATA[<p>When developers plan a product demo, they usually start by opening the app and clicking around to remember how things work. That's a slow way to find information you already have in a more precise form: the code.</p>
<p>Your repository knows the name of every screen, the exact label on every button, the order of every flow, and what changed recently. A demo script built from those facts is more accurate than one written from memory, and it uses the same words the viewer will see on screen.</p>
<p>This is the same reading that tools which <a href="https://demovexa.com">turn your codebase into a demo video</a> do automatically, but it works just as well by hand. Here's where to look, and what each source gives you.</p>
<h2>Key takeaways</h2>
<ul>
<li>Your code already holds the screen names, exact labels, flow order and recent changes a demo script needs.</li>
<li>Navigation gives the map, UI strings give the vocabulary, fixtures give the sample data and end to end tests give the flow.</li>
<li>Git history shows what's new, and docs show what a feature is for. If code and docs disagree, fix that first.</li>
<li>A one page plan built from these sources can be traced back to the code line by line.</li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#key-takeaways">Key takeaways</a></li>
<li><a href="#1-routes-and-navigation-the-map-of-the-video">1. Routes and navigation: the map of the video</a></li>
<li><a href="#2-ui-strings-the-vocabulary-of-the-narration">2. UI strings: the vocabulary of the narration</a></li>
<li><a href="#3-fixtures-and-seed-data-what-the-screens-should-contain">3. Fixtures and seed data: what the screens should contain</a></li>
<li><a href="#4-end-to-end-tests-the-flow-already-written-down">4. End to end tests: the flow, already written down</a></li>
<li><a href="#5-git-history-whats-actually-new">5. Git history: what's actually new</a></li>
<li><a href="#6-docs-and-how-to-guides-the-intent">6. Docs and how to guides: the intent</a></li>
<li><a href="#putting-it-together-a-one-page-demo-plan">Putting it together: a one page demo plan</a></li>
<li><a href="#letting-a-tool-do-the-reading">Letting a tool do the reading</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
<h2>1. Routes and navigation: the map of the video</h2>
<p>Start with whatever defines your screens. In a React app that might be a router config, a <code>pages</code> or <code>app</code> folder, or a sidebar component that lists the main sections.</p>
<pre><code class="language-bash"># Next.js app router: every page in the app
find app -name "page.tsx"

# A sidebar or nav config
rg -n "label:" src/components/Sidebar.tsx
</code></pre>
<p>The navigation tells you two things. First, the names of the main areas, exactly as users see them. Second, the shortest path to any screen. If your feature lives three levels deep, the demo needs a plan for getting there, or it should start on that screen directly.</p>
<h2>2. UI strings: the vocabulary of the narration</h2>
<p>The most common accuracy mistake in a demo is the narration calling something by a different name than the screen does. The voiceover says "download" and the button says "Export". The script says "workspace" and the app says "project".</p>
<p>If your app has translation files, those are a ready made glossary:</p>
<pre><code class="language-bash">rg -n "\"export" locales/en.json
</code></pre>
<p>If it doesn't, search the components for the visible strings in the flow you're demoing. Copy the exact labels into your script notes. When you write narration later, use those words and only those words for UI elements.</p>
<h2>3. Fixtures and seed data: what the screens should contain</h2>
<p>Test fixtures and seed scripts are often the best sample data you have. They were written to exercise the feature, so they usually cover the interesting cases: an overdue invoice, a user without permission, an empty state.</p>
<p>Read them for two reasons. They tell you which data makes the feature look like itself. And they tell you what to avoid: if a fixture is named <code>broken_user</code> or full of placeholder text, it's a sign you need a cleaner demo dataset before anything goes on camera.</p>
<h2>4. End to end tests: the flow, already written down</h2>
<p>An end to end test is close to a demo script with the narration missing. It lists steps in order, names the elements a user touches, and asserts the result.</p>
<pre><code class="language-js">test("send an invoice", async ({ page }) =&gt; {
  await page.getByRole("link", { name: "Invoices" }).click();
  await page.getByRole("button", { name: "New invoice" }).click();
  await page.getByLabel("Client").fill("Northwind Studio");
  await page.getByRole("button", { name: "Send" }).click();
  await expect(page.getByText("Invoice sent")).toBeVisible();
});
</code></pre>
<p>Turn that into scenes and you have: open Invoices, start a new invoice, pick a client, send, see the confirmation. The last assertion is usually the moment the demo is built around.</p>
<h2>5. Git history: what's actually new</h2>
<p>For a feature launch or changelog video, the commit log tells you what changed and when. A quick look at recent history for the feature's folder is often enough:</p>
<pre><code class="language-bash">git log -20 src/invoices
</code></pre>
<p>Look for the commit that introduced the user facing change, and read its diff. It tells you which screens changed, which strings were added, and sometimes why. That "why" is worth putting in the first line of your narration, if it's something a user cares about.</p>
<h2>6. Docs and how to guides: the intent</h2>
<p>Code tells you what the product does. Docs tell you what it's for. If you have a how to guide for the task you're demoing, it already describes the steps in user language and usually says who the feature is for. Use it to check that your scene order matches what you tell users in writing.</p>
<p>If the docs and the code disagree, fix that first. A demo that matches the code but not the docs will confuse people who read both.</p>
<h2>Putting it together: a one page demo plan</h2>
<p>After going through these sources, you can write a plan in a few minutes:</p>
<table>
<thead>
<tr>
<th>Scene</th>
<th>Screen (from routes)</th>
<th>Action (from tests)</th>
<th>Words to use (from strings)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Invoices</td>
<td>Open the list</td>
<td>"Invoices"</td>
</tr>
<tr>
<td>2</td>
<td>New invoice</td>
<td>Start one, pick a client</td>
<td>"New invoice", "Client"</td>
</tr>
<tr>
<td>3</td>
<td>Invoice</td>
<td>Send it</td>
<td>"Send"</td>
</tr>
<tr>
<td>4</td>
<td>Invoice</td>
<td>Confirmation shows</td>
<td>"Invoice sent"</td>
</tr>
</tbody></table>
<p>Add one line on top: who the video is for and the one thing they should remember. That's a complete brief, and every detail in it can be traced back to the code.</p>
<h2>Letting a tool do the reading</h2>
<p>This kind of reading is exactly what DemoVexa automates. You point a workspace at a local code folder (in any language), plus docs if you have them, and Claude reads it to learn the product's screens, names and flows before it drafts a brief and script. If the folder is a git repository, DemoVexa can also suggest video ideas from recent history. The docs page on <a href="https://demovexa.com/docs/workspaces/app-and-docs">choosing a code folder and docs for demo videos</a> has practical advice on choosing the right folder and writing how to guides that turn into clear scenes.</p>
<p>Whether you do it by hand or with a tool, the principle is the same: the code is the most up to date description of your product you have. Start the demo there, not from memory.</p>
<h2>FAQ</h2>
<h3>How do I list every page in a Next.js app?</h3>
<p>With the app router, each route is a folder under <code>app</code> that contains a page file, so <code>find app -name "page.tsx"</code> lists them (adjust the extension if you use .jsx or .js). With the older pages router, the files under <code>pages</code> are the routes.</p>
<h3>Can an end to end test be used as a demo script?</h3>
<p>It's a good starting point. A test already lists the steps in order, names the elements a user interacts with and checks the result. You still need to add narration and decide which steps are worth showing.</p>
<h3>How do I make sure the narration matches the labels in the app?</h3>
<p>Copy the exact strings from your translation files or components into your script notes, and use only those words for UI elements. If the button says "Export", the voiceover should say "Export" too.</p>
<h3>Can I make a demo video straight from a GitHub URL?</h3>
<p>Not with DemoVexa. It reads a local folder, so you clone the repository to your computer first and point a workspace at that folder. Pull the latest changes before making a video about a new feature.</p>
]]></content:encoded></item></channel></rss>