Skip to content
dimk90Public

About

Your terminal video recorder πŸ–­

Topics

Resources

Stars

14 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

206 Commits

Folders and files

Repository files navigation

Animated S-VHS logo
License: MIT Release & Deploy Deployed


A terminal recorder like VHS, but superior:

  • Always sharp - no GIF quality loss (#625, #69).
  • No timing drift - 1 second is 1 second at any resolution (#69).
  • Sized in rows and cols - no dancing with pixel width and height (#578).
  • Animated SVG output (#644, #109, #105).
  • Animated WebP output - a modern alternative to GIF with better compression (#50).
  • Highlight what matters - a selection swept over text on screen, the way a mouse drag would (#66).
  • No Chromium downloaded behind your back - just tmux + asciinema + renderer (#528, #438, #150, #45).

Quick Start

A recording is a plain shell script that sources s-vhs.sh:

#!/usr/bin/env bash

# Import s-vhs straight from GitHub - no local copy needed.
# A local copy works too - use "source ./s-vhs.sh"
source <(curl -fsSL https://dimk90.github.io/s-vhs/v0.7.0) && wait "$!" || exit 1

# Where should we write the GIF?
SetOutput 'demo.gif'

# Set up a 60x4 terminal with a 40px font
SetCols 60
SetRows 4
SetFontSize 40

# Start the terminal, then the recorder
Start
Show

# Type a command in the terminal
Type "echo 'Welcome to S-VHS!"
Sleep 1 # Pause for dramatic effect...

Type " Stay awhile and listen...'"
Sleep 1

# Run the command by pressing Enter
Enter

# Admire the output for a bit
Sleep 5

# Stop recording and write every requested output
Render

Save it as demo.rec.sh, make it executable and run it:

chmod +x demo.rec.sh
./demo.rec.sh

You should see a new file called demo.gif in the same directory:

Quick Start recording rendered as a GIF

Installation

s-vhs is a bash script, so all you need are its dependencies tmux and asciinema:

  • macOS

    brew install tmux asciinema
  • Linux

    sudo pacman -S tmux asciinema

    The Linux instructions are for Arch, but you can easily adapt them to your favorite distro πŸ˜‰

Note

Renderers are needed only for the output formats you request. Cast-only recordings need none. See Output Formats.

Tip

Some installs below use cargo, which puts binaries into ~/.cargo/bin - make sure it is on your PATH (rustup's installer arranges that; a distro-packaged cargo does not).

Renderer: GIF

For GIF output, install agg:

  • macOS

    brew install agg
  • Linux

    cargo install --git https://github.com/asciinema/agg --locked

    agg is not on crates.io (the agg crate there is an unrelated project), hence the --git install. Some distros package it as agg or asciinema-agg (on Arch, the AUR asciinema-agg package).

Tip

GIF size can be reduced by gifsicle without quality loss, see Output Size Optimization.

Renderer: WebP, MP4 and WebM

For animated WebP, MP4 or WebM output, install agg and ffmpeg. MP4 and WebM require ffmpeg 6.1 or newer, built with the libx264 encoder for MP4 and libaom-av1 for WebM - as distro and Homebrew builds are:

  • macOS

    brew install agg ffmpeg
  • Linux

    cargo install --git https://github.com/asciinema/agg --locked
    sudo pacman -S ffmpeg

Renderer: SVG

For animated SVG output, install asg - no package on either platform, so cargo on both:

cargo install asg --locked

Examples

See examples for the complete scripts behind the demos below, and a few more.

Enter

Enter 4 0.5

Enter pressed four times, half a second apart

Type

# Regular typing
Type 'echo "whatever you want"'
Sleep 1

Enter; Sleep 0.5

# Slow typing
Type 'echo "slow down"' 0.25

A command typed at the default speed, then one typed slowly

Columns & Rows

SetCols 40
SetRows 8

Type 'tput cols'
Enter; Sleep 1
Type 'tput lines'
Enter; Sleep 1

tput cols and tput lines reporting 40 by 8 in the recorded terminal

Keys

Named keys are commands of their own: Enter, Tab, Space, Backspace, Escape, Up, Down, Left, Right, PageUp, PageDown, Home, End, Insert, Delete. Each takes an optional repeat count and delay. A modified key goes through Key in tmux notation: Key C-u, Key C-r, Key M-x, see examples/ctrl.rec.sh.

Arrows

Type 'echo navigate around'; Sleep 0.5
Left 10 0.12
Sleep 0.5
Right 10 0.05

The cursor walking left and right along the typed line

Backspace

Type 'echo delete anything...' 0.05; Sleep 0.5
Backspace 18 0.05

Typed text deleted by repeated backspaces

Wait

Wait polls the visible pane until an extended regular expression (grep -E) matches, so a recording keeps up with a slow command instead of guessing a sleep:

Type 'sleep 2 && echo "build succeeded"'
Enter

# Anchored: the pattern must match the output line, not the command echoed
# above it
Wait '^build succeeded'

Type 'echo "and on we go"'

The recording pausing until 'build succeeded' appears in the pane

Highlight

Highlight drags a selection across text that is already on screen, holds it, and lets go:

SetHighlightColors 'colour214' 'black' 'bold' # selection style
SetHighlightSpeed 0.03  # seconds per swept cell

Type 'echo "s-vhs records what VHS cannot"'
Enter; Sleep 1

# Select the match closest to the cursor and hold it for 1.5s
Highlight 'VHS cannot' 1.5

A selection sweeping across the words 'VHS cannot' in the terminal output

Color Theme

# A bundled theme uses the same palette in GIF and SVG.
# A comma-separated hex palette
# (background, foreground, then 8 or 16 colors) also works.
SetTheme 'kanagawa'

Red, green and blue text rendered in the kanagawa palette

See the theme gallery for all bundled themes, with GIF and SVG samples. Other names pass through to the renderer and may not work in both formats.

Shell & Prompt

A recording runs in an isolated shell by default: no personal rc files are read, your own prompt stays out of the frame, and nothing is written to your shell history.

SetShell 'fish'       # bash (default), zsh or fish
SetPrompt 'powerline' # arrow (default), plain, path or powerline
SetTheme 'nord'       # the prompt picks up the theme's colors

# The theme follows the working directory
Type 'cd /tmp'
Enter; Sleep 1.5
Type 'cd ~'
Enter; Sleep 3

The fish shell with the powerline prompt theme following the working directory

Every prompt theme is implemented for all three shells, so a recording looks the same in bash, zsh and fish.

Tip

SetPrompt 'native' records your own shell configuration instead, and any other value is taken as a literal prompt.

Font Size

The font size is the only pixel-sized setting: it scales the whole render without changing the terminal grid the recorded shell sees.

SetFontSize 40

A 34x2 grid rendered at a font size of 40

The same 34x2 grid rendered at a font size of 20

The same 34x2 grid rendered at a font size of 10

Hide / Show

Start # Start session / initialize terminal

# Off camera: export the variable and wipe the screen it was typed on
Run 'export HIDDEN=wow' 0.5
Run 'clear' 0.5

Show # Start recording

# The recorded shell expands it, not this script
Type 'echo $HIDDEN'
Enter; Sleep 2

Hide # Stop recording, the session keeps running

Run 'echo "nobody will ever see this"' 0.5
Run 'clear' 0.5

Show # Resume recording, appending to the same cast

Type 'echo "back on camera"'
Enter; Sleep 2

A recording that skips the commands run between Hide and the next Show

Tip

RunOffRecord 'clear' 0.5 is shorthand for a Hide + Run + Show sandwich, see examples/run-off-record.rec.sh.

Require & Env

Require checks the commands a recording depends on before anything starts, and Env hands the recorded shell the variables they need - here a git identity, so the recording does not depend on the host's git configuration:

# Reported now, not as a "command not found" frame in the middle of the GIF
Require 'git'

Env 'GIT_AUTHOR_NAME' 'Ada Lovelace'
Env 'GIT_AUTHOR_EMAIL' 'ada@example.com'
Env 'GIT_COMMITTER_NAME' 'Ada Lovelace'
Env 'GIT_COMMITTER_EMAIL' 'ada@example.com'

Start

# Off camera: an empty repository in a throwaway directory
Run 'cd "$(mktemp -d)" && git init -q . && clear' 1

Show

Type 'git commit -q --allow-empty -m "recorded"'
Enter; Sleep 1

Type 'git log -1 --format="%an <%ae>"'
Enter; Sleep 2.5

A commit recorded with the git identity exported by Env

Tip

Finally 'rm -rf "$TMP_DIR"' clears a fixture the recording script made in TMP_DIR however the script ends, including the Wait timeout that fails a recording mid-take.

Remote Import

Import an immutable release directly from GitHub via curl instead of keeping a local s-vhs.sh next to the recording script:

# Remote import instead of "source ./s-vhs.sh"
source <(curl -fsSL https://dimk90.github.io/s-vhs/v0.7.0) && wait "$!" || exit 1

SetOutput "remote-import.gif"

Start
Show

Type "printf 'Imported s-vhs %s\\n' '$(svhs_version)'"
Enter; Sleep 3

Render

The wait "$!" is a guard: without it, process substitution can hide a failed or truncated curl download.

Tip

Keep the version pinned so the same script always imports the same library.

Animated SVG Output

SetOutput 'multi-output.cast'
SetOutput 'multi-output.txt'
SetOutput 'multi-output.gif'
SetOutput 'multi-output.svg' # <---

The .svg written by that recording - animated by CSS, sharp at any zoom:

One recording rendered as an animated SVG

Note

An SVG names fonts instead of embedding them and animates through CSS, so where it is displayed might change how it looks. Check SVG pitfalls and workarounds.

S-VHS in the Wild

πŸ“Œ pi-context-view - demo GIFs, such as palettes.rec.sh.
πŸ“Œ S-VHS logo recording.

Output Formats

The extension of the path passed to SetOutput picks the format:

Format Description Render dependency
.cast Editable, replayable asciicast recording None
.txt Plain-text terminal log None
.gif Animated raster image agg
.webp Animated raster image with lossless compression, typically smaller than GIF agg + ffmpeg
.mp4 H.264 video, for players and pages that take one rather than an animation agg + ffmpeg
.webm AV1 video, about half the bytes of MP4; much slower to encode agg + ffmpeg
.svg Sharp, CSS-animated vector image asg

SetOutput is repeatable - one recording, several outputs:

SetOutput 'multi-output.cast'
SetOutput 'multi-output.txt'
SetOutput 'multi-output.gif'
SetOutput 'multi-output.webp'
SetOutput 'multi-output.svg'

Keeping the .cast next to the rendered files leaves the recording replayable with asciinema play and re-renderable at any size later, while the .txt log makes the recorded output greppable - see examples/multi-output.rec.sh.

Recording Template

Start a new recording without downloading s-vhs.sh:

curl -fsSL https://dimk90.github.io/s-vhs/latest | bash -s -- new demo.rec.sh

or if s-vhs.sh is already local:

./s-vhs.sh new demo.rec.sh

It writes:

#!/usr/bin/env bash

source <(curl -fsSL https://dimk90.github.io/s-vhs/v0.7.0) && wait "$!" || exit 1

SetOutput 'demo.gif'

# SetCols 100
# SetRows 40
# SetShell 'bash'
# SetPrompt 'arrow'

# SetFontSize 28
# SetFontFamily 'JetBrains Mono'
# SetTheme 'dracula'

# SetTypingSpeed 0.07
# SetPlaybackSpeed 1
# SetFramerate 30
# SetLoop on
# SetOptimize off

# Require 'git' 'jq'

Start
Show

Type 'echo "Hello from s-vhs"'
Enter
Sleep 3

Render

Documentation

  • For the full list of commands and settings, see REFERENCE.md.

  • For color themes with GIF and SVG samples, see THEMES.md.

  • For debugging a recording script, see DEBUG.md.

  • For smaller output files and per-format optimization trade-offs, see OPTIMIZATION.md.

  • For the agent skill with recording best practices, see SKILL.md. Install it with gh skill, which places it where Claude Code, pi, Codex and others look for it:

    gh skill install dimk90/s-vhs s-vhs-recording --agent claude-code --scope user
  • For the architecture behind tmux + asciinema + renderers and how s-vhs glues them together, see INTRO.md.

    s-vhs pipeline

License

MIT

About

Your terminal video recorder πŸ–­

Topics

Resources

Stars

14 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages

⌂ 捒网址