Skip to content

Works on Windows, macOS and Linux

Record your terminal.
Get one file.

ttysvg captures a session into a single animated SVG. The text stays real text, so it is sharp at any zoom and you can select it, and one file carries a light and a dark palette at once.

git clone https://github.com/Nuu-maan/ttysvgcd ttysvgcargo install --path .
ttysvgttysvg $ttysvg $ttysvg $ ttysvgttysvg $ ttysvg tttysvg $ ttysvg thttysvg $ ttysvg thttysvg $ ttysvg thettysvg $ ttysvg thettysvg $ ttysvg themttysvg $ ttysvg themttysvg $ ttysvg themettysvg $ ttysvg themettysvg $ ttysvg themesttysvg $ ttysvg themesttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ tttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttyttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttyttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg rttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg rettysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg rettysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recorttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recordttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recordttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg recordttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record -ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --ottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --ottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --outtysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --outtysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --outttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --outttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out dttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out dottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docsttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docsttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/dttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/dettysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.sttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svgttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg --ttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- gttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- gttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glottysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfettysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfettysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfetttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfetttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfetcttysvg $ ttysvg themestokyonightcatppuccingruvboxnordttysvg $ ttysvg record --out docs/demo.svg -- glowfetch

Nothing is re-recorded. The theme is eighteen CSS variables, which is exactly how ttysvg render --theme does it.

Playground

Change the render, not the recording

This is ttysvg compiled to WebAssembly, running in your browser on a real capture. Every control below is a flag you would pass on the command line, and the file it produces is the file you would get. Drop in a capture of your own if you have one.

A cargo test run recorded with ttysvg
85.9 KB842 × 514

Loading the renderer. The controls come alive when it lands.

Why this exists

A command line tool is judged by the demo at the top of its README

If you develop on Windows, there was no good way to make one.

asciinema
Unix only. There is no Windows recorder, because it depends on a Unix pty.
VHS
Requires ttyd and ffmpeg. The Windows path is unofficial and breaks easily.
svg-term-cli
Unmaintained, and it needs an asciinema recording as its input.
Screen capture to GIF
Multi megabyte, blurry when scaled, wrong colors in dark mode, and a single typo means recording the whole thing again.

ttysvg talks to the Windows pseudo console directly. The same code runs on macOS and Linux through a normal pty, so a project can use one tool everywhere.

The short way in

Run it with no arguments and answer five questions

It asks the two things only you can answer, the command to record and how it should look, and fills in everything else.

The ttysvg interactive mode, from the first prompt through to the finished recording
  1. 01

    preset

    Size, padding, font size, window chrome and timing, in one choice.

  2. 02

    command

    The lines to run, in order, each typed into your shell.

  3. 03

    theme

    Arrow through the themes with a sample frame beside them.

  4. 04

    details

    Output path, title bar, sanitizing, and how long to hold.

  5. 05

    record

    A summary, then enter. You watch the session run for real.

It writes two files, not one. Next to the SVG you get the tape that produces it, so you leave with something repeatable without having had to learn the tape format first.

Examples

Write the demo as a script, regenerate it whenever your output changes

Recording by hand is fine once. The problem comes later, when your output changes and every demo in the repo is out of date. Each tape below is a real file in the repository, and each recording is what running that exact file produced.

A README banner for a command line tool

The most common case. A fixed size, a clean prompt with no machine name in it, and a title bar so it reads as a terminal rather than a screenshot of text.

Recording produced by banner.tape
output "../docs/examples/banner.svg"theme "tokyonight"width 78height 16padding 20window ontitle "ttysvg"shell "powershell.exe" "-NoLogo" "-NoProfile" "-NoExit" "-Command" "function prompt { 'ttysvg $ ' }; Clear-Host"type-delay 55mstrim-idle 700mstail 2500mswait  "ttysvg $" 20stype  "ttysvg --help"enterwait  "Usage" 10ssleep 1500ms

The wait on Usage is doing the real work. It holds until the help text is actually on screen, so the recording cannot end early on a slow machine.

Output formats

An SVG is right for a README and wrong nearly everywhere else

Social sites, chat apps and issue trackers mostly reject it. Six other formats cover that, and all of them work on all three commands.

  • --webp34 KB

    True color, and the smallest by a wide margin

  • --apng96 KB

    True color, only the changed part of each frame is stored

  • SVG424 KB

    Vector text, light and dark in one file

  • --gif900 KB

    Crushed to 256 colors, and the format everything accepts

Pick WebP unless something in the chain refuses it. The same recording is usually an order of magnitude smaller as WebP than as GIF, and it keeps every color instead of being crushed to a palette of 256. GIF remains the safe answer for anywhere that has not caught up, notably X.

Recording something real

A recording is a publishing format

Whatever was on screen ends up in a file that goes into a README, and a capture stores it as plain readable text. Two flags exist so that does not become a problem.

On the command line

ttysvg record --sanitize \  --redact "sk-[A-Za-z0-9]+" \  --redact "ghp_[A-Za-z0-9]+" \  --out demo.svg -- ./deploy

Or in the tape

sanitize onredact "sk-[A-Za-z0-9]+"redact "ghp_[A-Za-z0-9]+"

--sanitize handles the boring case with no regex. It rewrites your home directory to a tilde and replaces your username and hostname, which covers most of what makes people delete a demo and record it again.

Masking runs the moment a frame is captured, so a secret is never written to the SVG or to a saved capture. It covers the command line itself, and it keeps the original character count so the layout does not move.

Make the demo your README deserves

You need Rust. On Windows you also need version 1809 or newer, which is when the pseudo console arrived.

git clone https://github.com/Nuu-maan/ttysvgcd ttysvgcargo install --path .