SuperAPI AI Directory

record-tui

Testing & quality

View repository

Updated 10-15 19:56

About this skill

Record TUI — VHS Terminal Recording

Overview

Charmbracelet VHS renders a .tape script to GIF / MP4 / WebM / ASCII. Write the demo as code rather than capturing a screen, because a .tape re-renders unattended when the app changes and runs the same way in CI. For a throwaway recording nobody will re-run, asciinema is lighter.

Setup

# macOS
brew install charmbracelet/tap/vhs ffmpeg ttyd
# Debian/Ubuntu
sudo apt install ffmpeg && sudo snap install ttyd --classic && go install github.com/charmbracelet/vhs@latest

vhs --version && ffmpeg -version && ttyd --version   # all three are required

Tape file structure

Every Set goes above the first interaction command. A Set placed lower is silently discarded — see Gotchas.

Output demo.gif              # 1. output (gif/mp4/webm/ascii)
Require my-app               # 2. fail fast if the app is missing

Set Shell "bash"             # 3. settings, all grouped before any command
Set FontSize 20
Set Width 1200
Set Height 600
Set Theme "Catppuccin Frappe"
Set WindowBar Colorful
Set Padding 20
Set TypingSpeed 75ms

Hide                         # 4. hidden setup — Ctrl+L before Show
Type "export TERM=xterm-256color"
Enter
Sleep 500ms
Ctrl+L
Show

Type "my-app --demo"         # 5. visible interactions, with deliberate pauses
Sleep 500ms
Enter
Sleep 2s
Down 3                       # 6. app interaction
Enter
Sleep 3s                     # generous final frame before the loop restarts

Quick reference

Command Purpose
Output file.gif Output file + format (gif/mp4/webm/ascii); repeat for several formats
Require app Fail if the program is not on PATH
Set Key Value Terminal settings (FontSize, Width, Height, Theme, Framerate, …)
Type "text" / Type@100ms "text" Emulate typing (optionally at a per-line speed)
Enter / Tab / Space / Escape Key presses
Up/Down/Left/Right (Down 3) Navigation with optional repeat count
Ctrl+key, Alt+key, Shift+key Modifier combos
Sleep 2s Fixed pause (ms or s)
Wait+Screen@30s /regex/ Block until screen content matches, with a timeout
Hide / Show Control recording visibility (execution continues either way)
Screenshot file.png Capture the current frame
Env VAR "val" / Source other.tape Set an env var / include another tape
vhs validate demo.tape    # parse-check, no rendering
vhs demo.tape             # record
vhs themes                # list themes
vhs new demo.tape         # scaffold from template

Full command + settings detail: reference/COMMAND-REFERENCE.md.

Use case Width Height FontSize
README GIF 1200 600 20
Docs/tutorial 1400 800 18
Social media 1200 630 22
Full TUI app 1600 900 16
Compact CLI 800 400 20

Recording a tape for an app

Read the app's --help output or its source to learn how it launches, which keys it responds to, and which states are worth showing. Draft a tape covering those states, then vhs validate demo.tape → vhs demo.tape → watch the rendered file → adjust. Watching is the step that matters: validation parses syntax and cannot tell you the demo outruns the viewer.

Bound the demo before drafting: a README GIF shows one workflow in 10–30 seconds — three to five states, each held long enough to read (Sleep 2s–3s). Past 30 seconds, cut states or split into a second tape rather than adding scenes; a demo that shows everything shows nothing, and the GIF size grows with it.

Copy-paste starting points (Basic CLI, Interactive TUI, Build-and-Run, Multi-Panel, CI golden file, composable Source tapes): reference/TEMPLATES.md.

Examples

✅ Output first, settings grouped above the commands, Require guarding the binary, a pause after typing and a long final frame:

Output demo.gif
Require my-app

Set Shell "bash"
Set FontSize 20
Set Width 1200
Set Height 600

Type "my-app status"
Sleep 500ms
Enter
Sleep 3s

❌ Same tape, three defects — Set Width lands after Type and is dropped, nothing separates typing from execution, and a missing binary produces a confusing empty GIF instead of an error:

Output demo.gif

Type "my-app status"
Enter
Set Width 1200

✅ Pacing a TUI whose startup time varies — wait on a screen match instead of guessing a Sleep:

Type "my-tui"
Enter
Wait+Screen@30s /Ready/
Down 3
Sleep 500ms
Enter
Sleep 3s

Gotchas

  • A Set below the first command is silently dropped, and vhs validate still exits 0. Verified on vhs 0.11.0: Set FontSize 40 after a Type rendered at the default size, and the same holds for Set TypingSpeed — there is no per-setting exception. Keep every Set above the first Type/key command and confirm by watching the output, because nothing in the toolchain reports this. To vary typing speed mid-tape, use the per-line Type@100ms "…" form.
  • vhs validate only parses. It never launches the app, so a missing binary, a wrong keybinding, or a demo that races ahead of the app all pass. Add Require <app> so a missing dependency fails loudly rather than rendering an empty terminal.
  • Type "cmd" does not press Enter. Follow it with an explicit Enter, and put Sleep 500ms between them so the viewer can read the command before it runs.
  • Hide hides output, not execution. Commands inside a Hide block still run. End the block with Ctrl+L before Show so leftover setup output doesn't open the recording.
  • Wait times out. Raise it per command (Wait+Screen@30s /ready/) or globally (Set WaitTimeout 30s) for a slow-booting TUI, rather than padding with a longer Sleep that will still be wrong on a slower machine.
  • VHS records in a fresh shell, so your prompt customizations are absent. Pin Set Shell "bash" to get the same prompt on your machine and in CI.
  • A TUI that renders with wrong colors or wrapping is usually a terminal-setup problem. Pin it in the Hide block: Env TERM "xterm-256color" plus Type "stty rows 50 cols 120".
  • GIFs cross 5 MB quickly. GitHub renders them inline, but the README crawls. Try Set Framerate 15 first — it is the cheapest win and costs little perceived smoothness — then gifsicle -O3 --lossy=80, and switch to MP4 before shrinking dimensions past readability.

Optimize & CI

Size reduction, playback tuning, and the GIF-vs-MP4-vs-WebM decision tree: reference/OPTIMIZATION.md. Recording in GitHub Actions, plus ASCII output as a golden file for UI regression tests: reference/CI-INTEGRATION.md.

Integration

Pairs with build-tui — record the TUI right after building it, and commit the .tape next to the source so the GIF regenerates with the app. Use screenshot-local instead when the target is a web UI on localhost.

External: VHS · VHS Action · examples · themes · gifsicle.