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
- ๐ผ๏ธ Gallery of examples
- ๐จ Gallery of templates
- ๐ Manual page ยท Writing templates
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
[!IMPORTANT] Why the install name carries
-ng. The plaintermtosvgname on PyPI belongs to the original author and last shipped 1.1.0 in January 2020. That release predates the removal ofpkg_resourcesfrom setuptools, so on Python 3.12 and later it fails at import withModuleNotFoundError: No module named 'pkg_resources'โ every command,--versionincluded, dies immediately.termtosvg-ngis the same tool with that fixed.The
termtosvgcommand is kept so that existing scripts, tutorials and distro packaging continue to work unchanged. The import package istermtosvgas well.
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file termtosvg_ng-1.4.0.tar.gz.
File metadata
- Download URL: termtosvg_ng-1.4.0.tar.gz
- Upload date:
- Size: 84.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0889bcfb7f911928bade398bca90bcbfcc5411aca3a66b2ee59f31a9f219699d
|
|
| MD5 |
ccb309c862865503c352ef3839b989fe
|
|
| BLAKE2b-256 |
39fcabd2bc8d9ce1dd775964adfb5e688954263904f57c827da344da5ba5ab6b
|
Provenance
The following attestation bundles were made for termtosvg_ng-1.4.0.tar.gz:
Publisher:
release.yml on gaborini/termtosvg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
termtosvg_ng-1.4.0.tar.gz -
Subject digest:
0889bcfb7f911928bade398bca90bcbfcc5411aca3a66b2ee59f31a9f219699d - Sigstore transparency entry: 2283128954
- Sigstore integration time:
-
Permalink:
gaborini/termtosvg@4610d783f5f640fe84e0fce58d996b252b784a99 -
Branch / Tag:
refs/tags/1.4.0 - Owner: https://github.com/gaborini
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4610d783f5f640fe84e0fce58d996b252b784a99 -
Trigger Event:
push
-
Statement type:
File details
Details for the file termtosvg_ng-1.4.0-py3-none-any.whl.
File metadata
- Download URL: termtosvg_ng-1.4.0-py3-none-any.whl
- Upload date:
- Size: 85.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9891153876368a038bd88915221e8a99ef58da128b31f98885971c4adde829f8
|
|
| MD5 |
db1304cea7fadc5970a17a80c8389320
|
|
| BLAKE2b-256 |
027921e2e0151e7582715302085f56d82cfb663560867e8b0d6d7ab8bf57ab9f
|
Provenance
The following attestation bundles were made for termtosvg_ng-1.4.0-py3-none-any.whl:
Publisher:
release.yml on gaborini/termtosvg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
termtosvg_ng-1.4.0-py3-none-any.whl -
Subject digest:
9891153876368a038bd88915221e8a99ef58da128b31f98885971c4adde829f8 - Sigstore transparency entry: 2283129209
- Sigstore integration time:
-
Permalink:
gaborini/termtosvg@4610d783f5f640fe84e0fce58d996b252b784a99 -
Branch / Tag:
refs/tags/1.4.0 - Owner: https://github.com/gaborini
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4610d783f5f640fe84e0fce58d996b252b784a99 -
Trigger Event:
push
-
Statement type: