Record a terminal session and get a standalone SVG animation you can drop into any web page.
No video encoding, no player to embed, no JavaScript. The output is a single vector file where the text is still text.
Maintained by Gabor Lepsenyi Β· gaborl.hu
- πΌοΈ Gallery of examples
- π¨ Gallery of templates
- π Manual page Β· Writing templates
The animation above is a 26-second session at 82Γ19 characters, in 50 KB. That is one file, and the properties that come with it are not available from a screen recording:
| The text is real text | Readers can select, copy and search the commands in your demo. Search engines index them. |
| Resolution independent | Vector output stays crisp on a 4K display and at any zoom level. There is no "recorded at the wrong size" problem. |
| Themeable after the fact | Colours are CSS classes, not baked pixels. Restyle a finished animation by editing its stylesheet. |
| Self-contained | One .svg file. No player script, no external assets, no network requests. Works from file://. |
| Diff-friendly enough to commit | Plain XML in your repository, embedded with a relative Markdown link. |
termtosvg runs on Linux, macOS and the BSDs, and requires Python 3.10 or later.
python3 -m venv .venv
source .venv/bin/activate
pip install termtosvg-ngThree equivalent ways to run it β use whichever reads best to you:
termtosvg # the historical command name
termtosvg-ng # matches the name you installed
python3 -m termtosvgImportant
Why the install name carries -ng. The plain termtosvg name on PyPI
belongs to the original author and last shipped 1.1.0 in January 2020. That
release predates the removal of pkg_resources from setuptools, so on Python
3.12 and later it fails at import with ModuleNotFoundError: No module named 'pkg_resources' β every command, --version included, dies immediately.
termtosvg-ng is the same tool with that fixed.
The termtosvg command is kept so that existing scripts, tutorials and distro
packaging continue to work unchanged. The import package is termtosvg as well.
Installing straight from source
pip install git+https://github.com/gaborini/termtosvg.git@developOS packages maintained by the community
These package the original termtosvg distribution, so they carry 1.1.0 and the
caveat above applies to them.
| OS | Repository | Command |
|---|---|---|
| Arch Linux | extra/termtosvg | pacman -S termtosvg |
| FreeBSD | graphics/py-termtosvg | |
| Gentoo | media-gfx/termtosvg | emerge media-gfx/termtosvg |
| macOS | Homebrew | brew install termtosvg |
| NixOS | nixpkgs | |
| OpenBSD | graphics/termtosvg |
Start recording. You land in a subshell β type as you normally would.
$ termtosvg
Recording started, enter "exit" command or Control-D to endLeave the shell to finish:
$ exit
Recording ended, file is /tmp/termtosvg_exp5nsr4.svgOpen it in a browser to watch it, then embed it with a relative link:
termtosvg separates capturing from drawing, which is what makes the output editable and reproducible:
ββββββββββββββββ ββββββββββββββββ
your shell βββΊ β record β βββΊ .cast file βββΊ β render β βββΊ .svg
ββββββββββββββββ (asciicast v2) ββββββββββββββββ
β² β²
hand-editable SVG template
timings & text (theme + chrome)
Running termtosvg with no sub-command does both in one pass. Splitting them is
useful because the intermediate .cast file is plain text: you can fix a typo,
retime a pause, or re-render the same recording with a different theme without
performing the session again.
termtosvg record demo.cast # capture only
termtosvg render demo.cast out.svg # draw, as often as you likeBecause render reads the asciicast
format (v1 and v2), it also renders recordings made with
asciinema.
Three forms. The default records and renders in one go:
termtosvg [output_path] [-c COMMAND] [-D DELAY] [-g GEOMETRY] [-m MIN_DURATION]
[-M MAX_DURATION] [-s] [-t TEMPLATE] [-v] [-h]
termtosvg record [output_path] [-c COMMAND] [-g GEOMETRY] [-v] [-h]
termtosvg render input_file [output_path] [-D DELAY] [-m MIN_DURATION]
[-M MAX_DURATION] [-s] [-t TEMPLATE] [-h]
Not every option applies to every form β render takes its geometry from the
recording, so it has no -g:
| Option | Default | default form | record |
render |
What it does |
|---|---|---|---|---|---|
-c, --command COMMAND |
$SHELL, else sh |
β | β | β | Program to record, with arguments: -c 'ipython --pprint'. |
-g, --screen-geometry COLSxROWS |
your terminal, else 80x24 |
β | β | β | Screen size to record at, e.g. 82x19. |
-t, --template TEMPLATE |
powershell |
β | β | β | Built-in name (see below) or a path to your own template. |
--theme THEME |
the template's own palette | β | β | β | Override just the colours: auto to use the palette stored in the recording, a built-in name, or a path to a JSON palette. |
-m, --min-frame-duration MS |
1 |
β | β | β | Merge frames shorter than this. Raise it to shrink output from commands that redraw constantly. |
-M, --max-frame-duration MS |
noneΒΉ | β | β | β | Clamp long pauses, so thinking time does not stall the animation. |
-D, --loop-delay MS |
1000 |
β | β | β | Pause before the animation loops. |
-s, --still-frames |
off | β | β | β | Write one SVG per frame into a directory instead of one animation. |
-v, --version |
β | β | β | Print the version. | |
-h, --help |
β | β | β | Print usage. |
ΒΉ Unless the recording's header carries idle_time_limit, which is then used.
Durations accept a bare integer or an ms suffix β -m 17 and -m 17ms are the
same. output_path is optional everywhere; a temporary file is generated when it
is omitted (a directory, with --still-frames).
A template supplies the colour theme, the font, any window chrome, and how the
animation is driven. Pass one by name with -t:
| Template | Look | Animation |
|---|---|---|
powershell (default) |
Windows PowerShell palette | CSS |
base16_default_dark |
Base16 default dark | CSS |
dracula |
Dracula | CSS |
gjm8 |
Understated dark, no chrome | CSS |
gjm8_single_loop |
gjm8, plays once instead of looping |
CSS |
gjm8_play |
gjm8 with a play-icon drawn over the screen |
CSS |
progress_bar |
Adds a progress bar tracking the animation | CSS |
putty |
PuTTY palette | CSS |
solarized_dark / solarized_light |
Solarized | CSS |
terminal_app |
macOS Terminal.app | CSS |
ubuntu |
Ubuntu terminal | CSS |
xterm |
xterm palette | CSS |
window_frame |
Adds a terminal window frame | CSS |
window_frame_powershell |
Window frame, PowerShell palette | CSS |
window_frame_js |
Window frame with play/pause buttons | JavaScript |
A template bundles two independent things: the colour palette and the terminal
chrome (window frame, play button, progress bar). --theme lets you vary the
colours without authoring a template, so any palette combines with any chrome:
termtosvg render demo.cast out.svg -t window_frame --theme dracula--theme accepts:
| Value | Meaning |
|---|---|
| a built-in name | the palette from that template, e.g. --theme solarized_light |
auto |
the palette stored in the recording's own header |
| a path | a JSON file with fg, bg and palette attributes |
auto is useful for recordings made with asciinema, which store the terminal's
palette in the cast file. Recordings made by termtosvg record do not store one,
so auto warns and keeps the template's colours.
A palette file uses the same shape as the asciicast theme object, so it can be
copied straight out of a .cast header:
{
"fg": "#839496",
"bg": "#002b36",
"palette": "#073642:#dc322f:#859900:#b58900:#268bd2:#d33682:#2aa198:#eee8d5"
}Eight or sixteen colours are both accepted. With eight, the bright half is left as the template defined it rather than being guessed at.
Names are resolved in a fixed order β auto, then a built-in name, then a path β
so an argument never means something different depending on your working
directory. To load a file called dracula, write ./dracula.
Fifteen of the sixteen templates animate through pure CSS, so they play when
loaded as an ordinary image β including from a Markdown ![...]() in a GitHub
README.
window_frame_js is the exception: its play/pause controls need JavaScript, and
browsers do not execute scripts inside an SVG loaded via <img>. Embed that one
with <object> or <iframe>, or link to the file directly:
<object type="image/svg+xml" data="demo.svg"></object>Writing your own template is documented in
termtosvg-templates(5). The shipped templates under
termtosvg/data/templates/ are the best starting
point β copy one and edit its palette.
# Record a specific program instead of a shell
termtosvg -c 'ipython --pprint' demo.svg
# Pin the geometry so the animation is not tied to your window size
termtosvg -g 82x19 demo.svg
# Tame a chatty command: coalesce sub-17ms frames, cap pauses at 2s
termtosvg -m 17 -M 2000 demo.svg
# Re-theme an existing recording β no need to perform it again
termtosvg render demo.cast dracula.svg -t dracula
# Keep the chrome, swap only the colours
termtosvg render demo.cast out.svg -t window_frame --theme solarized_light
# Use the palette the recording was made with (asciinema casts store one)
termtosvg render downloaded.cast out.svg --theme auto
# Render someone else's asciinema recording
termtosvg render downloaded.cast out.svg
# Still frames, e.g. to pick a thumbnail
termtosvg render demo.cast frames/ --still-frames
# Two seconds of breathing room between loops
termtosvg -D 2000 demo.svg| Python | 3.10 β 3.14 |
| Operating systems | Linux, macOS, FreeBSD, OpenBSD (anything with a POSIX pty) |
| Not supported | Windows, which has no pty module. WSL works. |
| Dependencies | pyte (terminal emulation), lxml (SVG), wcwidth (wide-character widths) |
Windows shells can still be recorded β the powershell template exists for
styling such recordings β but termtosvg itself must run on a POSIX host.
make install # editable install with dev extras
make tests # unit tests with coverage
make lint # ruff
make build # sdist + wheel into dist/
make html # regenerate the example gallerymake lint runs ruff check. The formatter is intentionally not enforced;
see the note in pyproject.toml for why.
Rendering is deterministic, which makes it easy to prove a change did not alter
output: render the casts in docs/examples/casts/ before
and after, and compare bytes.
Bug reports and pull requests are welcome at github.com/gaborini/termtosvg.
Gabor Lepsenyi β gaborl.hu Β· github.com/gaborini
Licensed under the BSD 3-Clause License.
Originally created by Nicolas Bedos, who wrote the recorder, the rendering engine and the template system. Copyright is retained as required by the license.