1---
2name: charm-vhs
3description: "Record terminal sessions as GIF/MP4/WebM from declarative .tape scripts with VHS. Use when creating terminal demos, recording CLI sessions, VHS tape files, or generating terminal GIFs."
4---
5
6# charm-vhs
7
8VHS records terminal sessions from `.tape` scripts. Requires `ttyd` and `ffmpeg` on PATH.
9
10```sh
11brew install vhs # also installs deps on macOS
12vhs demo.tape # run a tape file
13vhs new demo.tape # scaffold a new tape
14vhs record > out.tape # record interactively, then exit
15vhs publish demo.gif # host on vhs.charm.sh
16```
17
18## Tape File Structure
19
20Order matters: `Output` and `Set` must come before action commands. `Require` goes at the very top.
21
22```
23Require <program> # fail early if missing from PATH
24Output <path> # .gif / .mp4 / .webm / .ascii / frames/
25Set <Setting> Value # terminal config (must precede actions)
26<actions> # Type, Enter, Sleep, etc.
27```
28
29## Output Formats
30
31```elixir
32Output demo.gif
33Output demo.mp4
34Output demo.webm
35Output frames/ # PNG sequence
36Output golden.ascii # for CI golden file diffing
37```
38
39Multiple `Output` lines are fine - all render in one run.
40
41## Settings Reference
42
43```elixir
44Set Shell "zsh"
45Set FontSize 14
46Set FontFamily "JetBrains Mono"
47Set Width 1200
48Set Height 600
49Set Padding 20
50Set Margin 40
51Set MarginFill "#6B50FF"
52Set BorderRadius 10
53Set WindowBar Colorful # Colorful, ColorfulRight, Rings, RingsRight
54Set Theme "Catppuccin Frappe" # run `vhs themes` for full list
55Set TypingSpeed 0.05 # seconds per keypress
56Set Framerate 60
57Set PlaybackSpeed 1.0
58Set LoopOffset 50% # where GIF loop starts
59Set CursorBlink false
60```
61
62`TypingSpeed` is the only setting that can change mid-tape. All others are ignored after the first action command.
63
64## Action Commands
65
66### Typing + input
67
68```elixir
69Type "git status" # types the string
70Type@500ms "slowly..." # override typing speed for this line
71Type `VAR="backtick escapes quotes"`
72Enter
73Enter 2 # press N times
74Tab
75Tab@200ms 3
76Backspace 5
77Space 2
78```
79
80### Navigation
81
82```elixir
83Up / Down / Left / Right # arrow keys
84Up 3 # repeat N times
85PageUp / PageDown
86ScrollUp 10
87ScrollDown@100ms 5
88Ctrl+C
89Ctrl+Alt+Delete
90```
91
92### Timing
93
94```elixir
95Sleep 500ms
96Sleep 2s
97Sleep 0.5 # seconds (float ok)
98Wait /regex/ # wait until last line matches (default timeout 15s)
99Wait+Screen /regex/ # check whole screen
100Wait+Line@10ms /regex/ # poll every 10ms
101```
102
103`Wait` is better than `Sleep` for commands with unpredictable runtime (builds, network calls).
104
105### Hide / Show
106
107```elixir
108Hide
109Type "setup stuff not shown in recording"
110Enter
111Wait /\$/
112Show
113```
114
115Use `Hide`/`Show` to run setup or cleanup without polluting the demo.
116
117### Other
118
119```elixir
120Screenshot path/out.png # capture current frame as PNG
121Copy "text" # put text on clipboard
122Paste # paste clipboard
123Env KEY "value" # set env var
124Source config.tape # include another tape
125```
126
127## Example Tapes
128
129### 1. Simple CLI demo
130
131```elixir
132Output demo.gif
133
134Set FontSize 14
135Set Width 900
136Set Height 400
137Set Theme "Catppuccin Frappe"
138Set WindowBar Colorful
139Set TypingSpeed 0.05
140
141Type "ls -la"
142Sleep 300ms
143Enter
144Sleep 2s
145```
146
147### 2. Build + run with hidden setup
148
149```elixir
150Output demo.gif
151
152Set FontSize 13
153Set Width 1200
154Set Height 600
155Set Theme "Dracula"
156
157Require go
158
159Hide
160Type "go build -o myapp . && clear"
161Enter
162Wait /\$/
163Show
164
165Type "./myapp --help"
166Sleep 200ms
167Enter
168Sleep 3s
169
170Hide
171Type "rm myapp"
172Enter
173```
174
175### 3. Interactive TUI demo with Wait
176
177```elixir
178Output tui-demo.gif
179Output tui-demo.mp4
180
181Set FontSize 14
182Set Width 1000
183Set Height 500
184Set WindowBar Rings
185Set Margin 30
186Set MarginFill "#1a1b26"
187Set BorderRadius 8
188Set TypingSpeed 0.07
189
190Require gum
191
192Type "gum choose 'Option A' 'Option B' 'Option C'"
193Enter
194Sleep 500ms
195Down
196Sleep 300ms
197Down
198Sleep 500ms
199Enter
200Sleep 2s
201```
202
203## CI Integration
204
205Use the [vhs-action](https://github.com/charmbracelet/vhs-action) GitHub Action to regenerate GIFs on push.
206
207For integration testing, output `.ascii` and commit as golden files - diff them in CI to catch terminal output regressions.
208
209```elixir
210Output golden.ascii
211```
212
213## Tips
214
215- `vhs record > cassette.tape` then edit the generated tape to add `Set` blocks and clean up timing
216- Use `Source` to share a `config.tape` with common `Set` defaults across multiple tapes
217- `Wait` beats `Sleep` for anything async - no need to guess how long a build takes
218- `LoopOffset` makes the GIF preview frame more interesting than frame 0
219- `vhs themes` lists all built-in theme names