Skip to content

Recorded demos

DevBench records a narrated video of a change and posts it where you will see it, because a green test says the code does not break, not that it does what you asked.

Reading time
4 min read

How it works

Every workspace box carries devbench video. It drives the deployed app with a real browser, draws a cursor and a highlight ring so you can see what is being pointed at, records the session, and burns a camera move and captions into an MP4. Nobody operates it and there is no editing pass: an agent writes a take, and the video falls out of running it.

It exists because of what a green test run cannot tell you: whether the thing you asked for is the thing that was built. A test says the code does not break; only a video says what it does, and it is what lets somebody give feedback without checking the branch out and clicking through it.

A take is content, and it lives in the repo at devbench/takes/<name>.py, committed next to the feature it shows. It reads like a narration script:

def take(t):
    t.goto("/")
    t.mark_ready()                       # everything before this is trimmed off
    t.say("DevBench records a video of every change it finishes.", 4.0)
    t.beat("[data-testid='kanban-board']", "Work is tracked per workspace.", 3.6)
    t.click("a[href^='/workspaces/']", after=3.0)

The one architectural rule: the live page is never transformed. The obvious way to zoom is transform: scale() on the page while it records, and it fails twice, the browser's click coordinates stop matching where elements are, and the app's own responsive layout shifts while it is being filmed, so the video stops showing the app as users see it. Instead the browser captures a flat frame and the take merely logs where the camera should have been; the camera move is a post-process:

   real page (untouched)
        |
        |  Playwright  ------------>  capture.webm   (flat, no transform)
        |
        +- take logs -------------->  timeline.json  {zooms, captions, trim}
                                            |
                    ffmpeg: trim -> zoompan -> drawtext -> H.264
                                            |
                                       <name>.mp4

Two consequences follow. Captions are drawn after the zoom, so a push-in can never magnify a caption and slice it off at the frame edge. And because the capture and the timeline are separate artefacts, re-rendering is free, which matters, because a take that submits a form submits it again for real, so a caption typo must never mean re-shooting.

The finished MP4 is uploaded to the workspace's shared files and can be posted straight into the chat and attached to the task it belongs to, where it plays inline rather than arriving as a link somebody has to decide to open.

Steps

Following along? Tick each number off as you go.
  1. In a workspace's box, check the recorder is there: devbench video check.

    It reports Playwright, ffmpeg, the font and the browser separately, because they fail differently.

  2. If something is missing, run devbench video setup.

    It builds a venv in your home directory and fetches a static ffmpeg, neither needs root, which a workspace box does not have.

  3. Scaffold a take next to the feature: devbench video new <name>.

    It is written to devbench/takes/<name>.py.

  4. Write the take: goto, mark_ready, then say / beat / click / type_into through the feature.

    Anchor selectors on data-testid, aria-label and href, workspace names and task titles are real data and change between deployments.

  5. Deploy the branch so there is something to record: devbench deploy.

    With no --base, the recorder points itself at this workspace's latest deployment, which is exactly the preview of the change.

  6. Record it: devbench video record <name> --notify "what changed" --task <id>.

    The MP4 is shared, posted into the chat with the video attached, and added to the task's note timeline.

  7. Fix a caption with devbench video rerender <name>.

    It rebuilds the MP4 from the kept capture, no browser, no side effects, seconds rather than minutes.

Worth knowing

  • A take runs for real. Anything it clicks, it actually clicks, so a tour take should navigate, point and explain, and leave submitting forms to the step-by-step text.

  • Use a GPL ffmpeg build. The widely-linked "static" tarballs are compiled without libfreetype, and an ffmpeg with no drawtext filter produces a video that quietly has no captions rather than an error anybody would think to check. devbench video setup fetches the right one.

  • No ffmpeg still records: you get the capture with no camera move and no captions, which is better than losing a recording that succeeded.

Commands

The same job from a workspace's box, for people who live in the terminal.

devbenchinside the box
  • $ devbench video checkCan this box record? What is missing?
  • $ devbench video setupInstall Playwright and ffmpeg here, no root needed.
  • $ devbench video new <take>Scaffold a take next to the feature it shows.
  • $ devbench video takesThe takes committed in this repo.
  • $ devbench video record <take> --notify "…" --task <id>Record against the deployed app, render, share, post and attach.
  • $ devbench video rerender <take>Rebuild the MP4 from the kept capture after editing captions.
Report a problem