# VHS: Write Terminal GIFs as Code Using Tape Files

> VHS is a CLI tool from Charmbracelet that records terminal sessions into GIFs, MP4s, or WebM files by executing a declarative script called a tape file. It turns reproducible, version-controlled demo recordings into a repeatable build step, suitable for documentation, README files, and CI pipelines.

**charmbracelet/vhs** — Your CLI home video recorder 📼

- Repository: https://github.com/charmbracelet/vhs
- Stars: 21,027 · Forks: 484
- Language: Go
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/charmbracelet-vhs

## What VHS solves and who it is for

Recording a terminal session by hand produces a video that cannot be version-controlled, is hard to update when a command changes, and is impossible to reproduce exactly. VHS replaces manual recording with a declarative script: a tape file that specifies every keystroke, wait, and setting. Running the same tape file twice produces the same output.

The README describes VHS as useful for integration testing and for creating demos of CLI tools. The primary audience is developers who want terminal GIFs for documentation, README files, or CI checks that verify CLI output visually. Tape files are plain text, so they can be stored in a Git repository alongside the code they document.

VHS requires two external programs: ttyd (for the terminal emulator backend) and ffmpeg (for video encoding and GIF generation). Both must be on the PATH before VHS can run locally. The Docker image at ghcr.io/charmbracelet/vhs installs both automatically, which is the safest way to ensure a consistent recording environment across team members and CI runners.

## Tape file format: commands and structure

A tape file is a series of VHS commands. Each command is a plain-text instruction that VHS executes on a virtual terminal. The README provides a concise example that demonstrates the core commands:

```elixir
# Where should we write the GIF?
Output demo.gif

# Set up a 1200x600 terminal with 46px font.
Set FontSize 46
Set Width 1200
Set Height 600

# Type a command in the terminal.
Type "echo 'Welcome to VHS!'"

# Pause for dramatic effect...
Sleep 500ms

# Run the command by pressing enter.
Enter

# Admire the output for a bit.
Sleep 5s
```

Once the tape file is saved, running `vhs demo.tape` produces a demo.gif file in the same directory. The Output command accepts .gif, .mp4, .webm, or a directory path (for a PNG frame sequence). Multiple Output commands in one tape produce multiple output files.

The Require command validates that programs are on the PATH before recording starts, which causes the tape to fail early with a clear error rather than recording a session where a required program is missing.

## Installing VHS and its dependencies

VHS is available through standard package managers. On macOS or Linux with Homebrew:

```sh
brew install vhs
```

On Arch Linux:

```sh
pacman -S vhs
```

With Go:

```sh
go install github.com/charmbracelet/vhs@latest
```

The Docker image bundles both ttyd and ffmpeg, making it the most portable option:

```sh
docker run --rm -v $PWD:/vhs ghcr.io/charmbracelet/vhs <cassette>.tape
```

For Debian or Ubuntu, the README documents the Charm apt repository:

```sh
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
# Install ttyd from https://github.com/tsl0922/ttyd/releases
sudo apt update && sudo apt install vhs ffmpeg
```

Note that ttyd is not in the apt repository for most distributions and must be installed separately from its own release page.

## Recording and publishing tape files

Beyond writing tape files by hand, VHS can generate one from a live terminal session:

```bash
vhs record > cassette.tape
```

This starts an interactive session. After performing the actions to record and then exiting, VHS writes a tape file capturing the sequence. The README recommends reviewing and editing the generated tape file afterward to add settings or clean up accidental keystrokes.

VHS also supports publishing GIFs to the Charm hosting service:

```bash
vhs publish demo.gif
```

The output includes links to share the GIF via browser, HTML, and Markdown. This feature is optional; GIFs can also be committed directly to a Git repository without publishing them to an external service.

The Source command allows splitting a long tape into smaller modules that include each other. The Env command sets environment variables inside the virtual terminal before commands run, which is useful for reproducible tests that depend on specific env state.

The Hide and Show commands control which commands appear in the recording output. Wrapping setup commands (such as clearing the terminal or setting a prompt) between Hide and Show keeps them out of the final GIF while still executing them. The Screenshot command captures the current terminal frame as a PNG without recording a full video, which is useful for generating static preview images alongside the GIF. The Wait command pauses recording until a regex matches the screen output or a line in the terminal buffer, preventing races between long-running commands and the next scripted input. The Copy and Paste commands allow clipboard operations within the tape, and the ScrollUp and ScrollDown commands control the terminal viewport for demonstrating content that extends beyond the visible screen height.

## The VHS SSH server mode

VHS includes an SSH server that allows running it on a remote machine and accessing it as if it were installed locally. Start the server with:

```sh
vhs serve
```

The Dockerfile exposes port 1976 and sets the default VHS_PORT, VHS_HOST, VHS_GID, and VHS_UID environment variables. The server accepts tape files over SSH and returns the output file, making it possible to use VHS without installing ttyd and ffmpeg on every developer machine:

```sh
ssh vhs.example.com < demo.tape > demo.gif
```

The server configuration is controlled by VHS_KEY_PATH (the SSH key) and VHS_AUTHORIZED_KEYS_PATH (optional restriction; empty means publicly accessible). An empty VHS_AUTHORIZED_KEYS_PATH means anyone who can reach the server port can submit tape files for processing, which is a security consideration for public-facing deployments.

## Limitations and comparison to screen recording

VHS records a virtual terminal, not a real one. The virtual terminal is provided by ttyd, and the fonts and rendering may differ from the developer's local terminal. The Dockerfile installs a curated set of fonts including JetBrains Mono, Fira Code, and Source Code Pro to give consistent results, but local VHS runs depend on whatever fonts are installed on the system.

VHS cannot record mouse interactions with TUI applications beyond what can be scripted with keyboard commands. For applications that require mouse input, the tape command set does not include mouse click commands, only keyboard events.

Compared to asciinema (another terminal recording tool), VHS produces a final image or video file from a script, while asciinema captures a live session into a JSON format that can be replayed in a browser player. VHS's output is a static file (GIF, MP4, or WebM) that embeds directly in a README or documentation page without requiring a player. Asciinema's replay format preserves exact timing and allows copy-pasting text from the recording, which VHS's GIF output does not.

The go.mod lists go-rod/rod as a dependency, which is a browser automation library. VHS uses it for screenshot capture and browser-based rendering during GIF generation rather than relying purely on the terminal emulator. The go.mod also lists hashicorp/go-version for version comparison in the Require command validation and agnivade/levenshtein for fuzzy-matching command names when a typo is made in a tape file.

The examples/ directory in the repository contains tape files and generated GIFs demonstrating how VHS is used to document tools including bubbletea, gum, glow, and the gh CLI. These examples serve both as documentation and as integration tests that verify VHS produces expected output. The most recent release is v0.12.1 from 2026-09-24, and the last push was on the same date. The licence is MIT.

## Conclusion

VHS is the right tool when you need terminal recordings that are reproducible, diff-able, and generated from code rather than captured manually. It is a meaningful improvement over screen recording software for CLI documentation and integration testing. The main constraint is the ttyd and ffmpeg dependency requirement: environments without these installed cannot run VHS locally, though the Docker image bundles everything. Before using VHS in a CI pipeline, verify that the Docker image or the package versions of ttyd and ffmpeg available on the CI runner match what your tape files require.

## FAQ

### What is the VHS CLI recording tool?

VHS is a Charmbracelet tool that records terminal GIFs, MP4s, and WebM files from declarative tape files. Each tape file specifies the exact keystrokes, pauses, and settings, making recordings reproducible and version-controllable. It requires ttyd and ffmpeg to run.

### How do I install VHS for terminal recording?

VHS is available via Homebrew (brew install vhs), pacman, Nix, and Go (go install github.com/charmbracelet/vhs@latest). The Docker image ghcr.io/charmbracelet/vhs bundles ttyd and ffmpeg, making it the easiest option if those dependencies are not already installed.

### What output formats does VHS produce?

VHS produces GIF, MP4, WebM, or a directory of PNG frames, determined by the Output command in the tape file. A single tape file can contain multiple Output commands to produce several formats in one run.

## Sources

- [charmbracelet/vhs on GitHub](https://github.com/charmbracelet/vhs)
- [Issues](https://github.com/charmbracelet/vhs/issues)
- [License: MIT](https://github.com/charmbracelet/vhs/blob/main/LICENSE)
- [README](https://github.com/charmbracelet/vhs/blob/main/README.md)
- [Releases](https://github.com/charmbracelet/vhs/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/charmbracelet-vhs
