Contents

Recording a product walkthrough

pnpm video:record plays a scripted walkthrough of the site in a browser and films it: the admin, a page, and its translation, with captions on screen and no sound. It writes the full walkthrough, three short loops for a landing page and a thumbnail. Because the script runs against the code of the version it is recorded from, the video can be made again for every release and always shows what that release does.

The walkthrough is written for the demo site (pnpm seed:demo). Recording your own site explains how to adapt it.

What it records #

Ten scenes, about two minutes in all. The captions are in scripts/video/scenes.ts; the steps that play each scene are in scripts/video/steps.ts.

SceneWhat is on screen
openingThe "Why Varde" page on the site, in English and then in Danish.
problemThe English and Danish pages side by side, after the English headline was changed and published: the Danish one still says the old thing.
kitThe admin's dashboard and the Translation group in the sidebar.
translate✨ Translate on the English page, into every other language at once; the progress and the result; the German draft with the "Editing DE" pill and its draft status.
glossaryThe Glossary, the brand name and the approved German word ("Lichtring") in the German draft, and the Translation style guide.
syncAn English text is changed; Translation status shows which languages are behind; the Danish page catches up with "Sync and translate all changed"; a German block is detached.
filesA Word data sheet in Translate a file: checked, translated into every language, ready to download.
learningAn editor corrects the Danish title and publishes it; the wording turns up approved in Translation memory and as a correction; the quality panel offers Distill.
developersA card with a block's config and the ANTHROPIC_API_KEY line of .env.example, read from the repository as it is, then the block picker.
closingA title card, with the line given with --outro under it.

A browser cannot show a code editor, so the developers scene shows the code on a card drawn by the recorder. Each loop (translate, sync, files) is cut from its scene at a moment with no caption on screen.

Page loads are cut out, and so are waits for the AI and for background jobs: the recording pauses while they run and goes on from the finished screen. A pointer and click ripples are drawn in, since a browser without a screen has no pointer to film.

Run it locally #

You need the database running (docker compose up -d), PAYLOAD_SECRET in .env and Playwright's Chromium (pnpm exec playwright install chromium). Stop pnpm dev first: Next.js runs one development server per folder.

pnpm video:record
  1. It seeds the demo into a throwaway database: VIDEO_DATABASE_URL, or a database named tolk-video beside a MongoDB DATABASE_URL. A database it seeded before is rebuilt; any other database with content is refused.
  2. It starts the development server on a free port, with maps drawn without tiles and background jobs left to the recording, which runs them when a scene needs them.
  3. It starts the AI stand-in (see below) and points the site at it.
  4. It logs in with DEMO_ADMIN_EMAIL and DEMO_ADMIN_PASSWORD, or with an account it makes for the run, made an admin in that database: the walkthrough runs background jobs, which only admins may do. It opens every view once, so the development server has compiled them before filming starts.
  5. It plays the scenes, encodes the files, stops the server and deletes the demo's content and files again.

A full run takes five to fifteen minutes, most of it the development server compiling each view the first time.

Options:

  • --url http://localhost:3000 films a site that is already running and seeded with the demo. It needs DEMO_ADMIN_EMAIL and DEMO_ADMIN_PASSWORD for an admin of that site; the demo seed makes that login an editor, and pnpm payload run scripts/docs/demo-db.ts -- seed seeds a throwaway database with it made an admin, as the workflow does. The steps change the site's content (they edit, translate and publish the demo pages), so use it only on a demo site.
  • --only translate,sync films only those scenes. A scene that needs a translation an earlier scene would have made translates the page off camera first.
  • --outro "Your line" adds a line under the closing card, such as where to sign up. The repository holds no such text.
  • --ai real uses the site's real AI key instead of the stand-in. See The AI.
  • --out <folder> writes the files elsewhere; --port <n> picks the server's port; --keep-frames keeps the recorded frames in <folder>/.work.

What it writes #

The files go to recordings/, which git ignores:

FileWhat it is
walkthrough.mp4, walkthrough.webmThe whole walkthrough, 1920 × 1080, 30 frames a second.
loop-translate, loop-sync, loop-files (.mp4, .webm)About 6 to 9 seconds each, without captions, to play on a loop beside a section of a landing page.
thumbnail.jpgThe demo page in every language side by side, as a poster image.
manifest.jsonWhat was recorded: the version, the AI used, each file and each scene's place in the video.

A recording made with the AI stand-in has -simulated in every file name (walkthrough-simulated.mp4), says "simulated": true in its manifest and carries a badge in every frame.

Quality and size #

The browser window is 1536 × 864 at 125 % zoom, so the admin's text is large enough to read in a video, and every frame is filmed at 1920 × 1080 from the browser's own screencast, each time the page changes.

  • With ffmpeg installed (GitHub's Ubuntu runners have it), the files are H.264 MP4 and VP9 WebM, at settings that keep text sharp.
  • Without it, the ffmpeg that Playwright downloads with Chromium writes VP8 WebM only. It plays in every current browser; the files are larger for the same quality. A local run on a Mac without ffmpeg wrote the 2 minute 10 second walkthrough in about 25 MB (some 1.6 Mbit/s), the loops in 0.5 to 0.8 MB each and the thumbnail in 0.13 MB, with text as sharp as on screen in still frames.

Screen recordings are mostly still, so they compress well; a scene with scrolling or a moving pointer costs the most.

No ffmpeg ships with the kit: the npm packages that bundle one are GPL-licensed.

Captions and scenes #

Edit scripts/video/scenes.ts, and run the recording again. Nothing else needs to change:

  • Each entry is one scene, in the order shown. Remove one to leave it out.
  • A scene shows its captions one after another; its steps decide when the next one comes. Fewer captions simply show fewer.
  • {languageCount} (the number of languages, in words) and {blocks} (the blocks in the block picker, counted while recording) are filled in, so the numbers stay true when the configuration changes. A placeholder the recording does not know stops the run.
  • Each caption stays on screen at least as long as it takes to read, at about fifteen characters a second.
  • Keep every caption true of what the video shows. tests/unit/video.test.ts checks the scene list, the length of each caption and its placeholders.

The AI #

The AI scenes (translate, sync, files, and translations made off camera for --only) need the AI to answer.

By default no call leaves your machine. The site is started with a placeholder key and its AI address (ANTHROPIC_BASE_URL) pointing at a stand-in on your machine (scripts/lib/aiStandIn.ts). It answers in the API's own format, so every feature runs end to end, but it does not translate: it returns the English text behind a language tag, "[DE] Five pieces", with the glossary's wording put in. Nobody can take it for a translation, and the files are marked -simulated. pnpm video:standin runs it on its own, for a site you start yourself.

With --ai real, the site uses ANTHROPIC_API_KEY, and the files are a real recording. The run sets the style guide's default quality to Fast for its duration, so every translation it starts, the block sync's included, uses the cheapest model, without the review pass; the previous setting is put back afterwards. If the answers turn out to be the stand-in's, the run stops and writes nothing.

What a real run costs: it translates the demo page into two languages, one changed block, and a one-page Word file into two languages: about five calls at the Fast level, a few thousand words in all, which comes to a few cents at Anthropic's prices (anthropic.com/pricing). Nothing else in the walkthrough calls the AI.

In CI: a video for every release #

.github/workflows/video.yml records the walkthrough for every release, and on demand (Actions → Demo video → Run workflow, with an optional release tag and closing line). A release made by the release workflow starts it with the new tag; a release a person publishes starts it by itself. It starts MongoDB, builds and starts the production server, seeds the demo (its login made an admin), records with --url, encodes with the runner's ffmpeg and attaches the files to the release with gh release upload.

  • The secret: add ANTHROPIC_API_KEY under Settings → Secrets and variables → Actions. Only the site's process gets it.
  • Without the secret, the workflow records with the stand-in, keeps the result as a workflow artifact for checking, and says in the job summary that the release video was skipped. scripts/video/release-files.ts refuses any simulated recording, so it can never be attached to a release.
  • The closing line comes from the run's "outro" input, or else from the repository variable VIDEO_OUTRO.

Every run also keeps its files, the site's log and the stand-in's log as a workflow artifact for two weeks.

Recording your own site #

The steps are written for the demo content (the "Why Varde" page, its glossary and its data sheet), but the parts are general:

  • scripts/video/record.ts starts and seeds the site, films and encodes; it knows nothing about the scenes.
  • scripts/video/steps.ts holds one function per scene, written with Playwright and a few helpers: ctx.goto opens a page off camera, ctx.click and ctx.moveTo move the drawn pointer, ctx.caption() shows the next caption, ctx.highlight frames an element, ctx.offCamera pauses filming during a wait, and ctx.loopStart() and ctx.loopEnd() mark a loop.
  • scripts/video/scenes.ts holds the order and the captions.

To film your own site, add a scene with its id in scenes.ts and its steps in steps.ts, using your pages, and record a staging copy with --url: the steps change content as they go.

These guides ship with Tolk Starter 0.6.0: buyers get the same pages in their repository, next to the code they describe.