termtosvg
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
Why SVG
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. |
Installation
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-ng
Three 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 termtosvg
Installing straight from source
pip install git+https://github.com/gaborini/termtosvg.git@develop
OS 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 |
Quick start
Start recording. You land in a subshell — type as you normally would.
$ termtosvg
Recording started, enter "exit" command or Control-D to end
Leave the shell to finish:
$ exit
Recording ended, file is /tmp/termtosvg_exp5nsr4.svg
Open it in a browser to watch it, then embed it with a relative link:

How it works
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 like
Because render reads the asciicast
format (v1 and v2), it also renders recordings made with
asciinema.
Command line reference
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).
Templates
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 |
Colours separately from chrome
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.
Embedding, and one caveat worth knowing
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.
Recipes
# 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
Compatibility
| 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.
Development
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 gallery
make 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.
Maintainer
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.
Metadata
Release files for termtosvg-ng 1.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| termtosvg_ng-1.4.1.tar.gz | 85.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| termtosvg_ng-1.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 171.6 kB
Release files / termtosvg_ng-1.4.1.tar.gz
| Download URL | termtosvg_ng-1.4.1.tar.gz |
|---|---|
| Size | 85.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0c35b0a62ab8c825228716f5051b771c04765ce327ffc21fcc20dd9891141e4e
|
|
BLAKE2b-256 checksum How to use checksums |
a602824d27b1d0b9ed793b0138c7ee7bac66219634d3d39b24959b813a2492e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.
Transparency logRelease files / termtosvg_ng-1.4.1-py3-none-any.whl
| Download URL | termtosvg_ng-1.4.1-py3-none-any.whl |
|---|---|
| Size | 86.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a725b4b575db775def9c6ca207a91f3d906ca619ab45e41a5b6ac838eeea6620
|
|
BLAKE2b-256 checksum How to use checksums |
e22770587d37f57fe7b06cb2435c7f5b4f0b07f40019b7add63d536160786ff7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 29, 2026.
Transparency log