By Evan Sangaline | November 14, 2017
Recording a terminal session and converting into a nice animated GIF to embed on a website. Sounds pretty simple, right? I’ve occasionally wanted to embed terminal recorders into blog posts, but it wasn’t until recently that I decided to actually look into some of the tools available to do it. It turns out that there are a lot of them. That alone isn’t necessarily a bad thing, but it also unfortunately turns out that most of them have some pretty serious issues.
With appologies to Randall Munroe for butchering a classic, I think this kind of sums up the state of terminal recorders pretty well:
The bar is quite low for writing a crude terminal recorder; you can easily throw together a bash script that uses ImageMagick’s import
command to grab a sequence of screenshots and then stitch them together into an animated GIF with the convert
command.
That’s all hunky dory until the set of raw images get too large to fit in memory and the GIF conversion crashes.
Or maybe the screenshots take a few tens of milliseconds to save to disk which increases the effective frame delay and results in animation that seems too fast.
These are just a couple of minor examples, but the point is that many of the terminal recorder tools that are out there have some subtle issues that make them kind of annoying to deal with.
Don’t get me wrong, not all of them are that annoying… just most of them. What’s even more annoying is that if you search for “terminal GIF recorders,” or something equivalent, you mostly end up with blogspam that lazily links to completely broken tools and services which have been discontinued for years. I ended up having to go through and try each of them out myself, and so I thought it might be useful to compile the results of my research here for the benefit of others. If you’re trying to make a decision about which terminal recorder to use, then hopefully seeing the results from each tool and reading my recommendations will save you some time.
Methodology
To make for a fair comparison between the different terminal recorders, I decided to record a macro of keypresses and then to replay the exact same sequence for each recording. This insured that the recordings were all roughly the same length, the typing was the same speed, the same programs were launched, etc. I made the recordings using the awesome xmacro tool, which lets you record X events into a nice human-readable script.
# record a macro
xmacrorec2 > recording.macro
Here’s a snippet of what the macro looks like, but I’ve also uploaded the entire macro file that I used during testing.
KeyStrPress Shift_R
Delay 192
KeyStrPress 3
KeyStrRelease 3
KeyStrRelease Shift_R
Delay 192
KeyStrPress space
KeyStrRelease space
Delay 144
KeyStrPress l
This recording.macro
can then be replayed (on a Linux system with X11) by runnning:
cat recording.macro | xmacroplay
which emulates the same sequence of keypresses and pauses that I initially recorded. You’ll get pretty familiar with this sequence shortly, but the star of the show is running an ncurses version of Conway’s Game of Life. This involves some fairly complex and fast-paced terminal updates so my hope was that it would catch potentially glitchy behavior (it did).
I should also mention that I use powerline-shell for command prompt and that it uses several unicode symbols.
In fact, it not only includes unicode symbols–the font has been patched so that the “▶” lines up cleanly in the powerline. The use of these symbols will be helpful in determining whether any of the terminal recorders struggle with unicode (they do).
Beyond that, my basic methodology was to record an animation of the macro playing. I checked for any glitchiness, the size of the generated animation, excessive memory usage, excessive runtime, and whether non-standard terminal sizes recorded correctly. Most of the tools didn’t have any issues with memory or runtime, so I’ll only mention these numbers if they’re particularly relevant (there are a few that blow up).
The animation outputs are going to be included below and some of them are quite large. I recommend making sure that you aren’t on mobile data before scrolling down much further.
Results
The tools roughly fall into three categories: general screen recorders that can record any window, tools that use a non-graphical tty recorder like ttyrec as an intermediary format, and recorders designed for HTML/JavaScript playback. I’ll group the results into these categories and sort the results within each category from best to positively dreadful. The worst ones in each category can get pretty bad… so I recommend at least checking out the top one or two in each category if you’re trying to make a decision for yourself.
Here’s an index of all the ones that I tried, so you can jump directly to any one that interests you.
You can also skip ahead directly to the conclusion, if you want the tl;dr version of the results.
General Screen Recorders
I’ll start by pointing out that this is the only method that was able to give pixel perfect reconstructions. Reproducing the same font and color scheme might stray a little into the realm of vanity, but there are some more practical implications as well. Many of the other methods suffer from display glitches and unsupported unicode characters; these don’t come into play with screen recordings. The trade off for this is larger file sizes, but that’s probably worth it in some cases.
There are a lot of general screen records out there. A comparison of them would really warrant it’s own separate write-up, so I’ll just pick one representative example for macOS and one for GNU/Linux. As an aside, I also found these tools to be satisfyingly consistent with my preconceived notions of software on Linux and macOS. The macOS screen recorder is proprietary, super easy to use, and produces a reasonably satisfactory result with little effort. The Linux screen recorder is open source, uses battle-tested technologies under the hood, and is a little rough around the edges–but is far more flexible and ultimately produces a better result.
gifine (Linux)
Gifine is far from the most popular screen recorder on Linux, but I like its simplicity and reliability. It’s written in MoonScript and it’s basically a thin UI on top of ffmpeg and gifsicle. It can be installed by running
luarocks install --local --server=http://luarocks.org/dev gifine
and then adding .luarocks/bin/
to your path
export PATH=$PATH:.luarocks/bin/
if you haven’t done so in the past.
You can then simply run it with gifine
and you get a nice little GUI which allows you to record frames, trim the start and end of the recording, and export it as either a GIF or a video.
The generated GIFs are excellent quality, but the file sizes are quite large unless you use a very low framerate.
The Game of Life animation at 10 frames per second is 2.0 MB even though gifine
uses gifsicile
under the hood for compression.
This actually isn’t too terrible compared to some of the other approaches, particularly considering that the fonts are anti-aliased, but it’s not exactly small either.
Luckily, it turns out that the video output option produces comparable quality recordings that take up much less space.
I was relatively happy with the default outputs, but I found that I was able to improve the quality and compression ratio a bit by modifying some of the ffmpeg
conversion settings.
The command-line arguments for ffmpeg
are defined in ~.luarocks/share/lua/5.3/gifine/commands.lua
and applying the following diff results in a slightly better output.
203c203
< "-",
---
> "pipe:.png",
205c205,209
< "h264",
---
> "libx264",
> "-crf",
> "18",
> "-preset",
> "veryslow",
These settings resulted in an h264 video file at 25 frames per second that is only 656 KB and can be played in any modern browser (see above). It’s possible to get the GIF size comparable by reducing the framerate, but overall I personally prefer the smoother playback. The videos also allow someone to seek to different positions in the recording. That’s pretty convenient for longer recordings.
recordit (macOS)
Just to be clear: the “X” in xmacros
doesn’t for OS X.
We had to get a little creative to test this one… we played back the macro of X events on a Linux computer and used the Chrome Remote Desktop extension to pipe these through to a computer running macOS.
The keypresses were effectively played back into a terminal on that computer and the terminal was then recorded using recordit.
To avoid “decision fatigue,” the developers of recordit
set the frame rate to a nominal value of 4 and they don’t let you change it.
The actual framerate appears to be closer to 2 frames per second, I’m not sure why there’s a discrepancy there.
This low framerate results in a noticeably jerky playback, but the image is crisp and the output file is a relatively slim 482 KB (which is comparable to gifine
GIFs with similarly low framerates).
It’s also worth mentioning that QuickTime supports recording portions of your screen as video.
This might be a good alternative option if you prefer to use a video file like I did with gifine
.
Record-Then-Render Tools
There are several popular programs for recording and playing back terminal sessions inside of a terminal, such as script and ttyrec. These guys have both been around for well over a decade and they really weren’t initially designed with web publications or animated GIFs in mind. Despite that, there are a number of projects which have sprung up and use these non-graphical tools as a backend for recording terminal sessions.
These are kind of a mixed bag: some actually playback the recordings in a terminal and record the window, while others attempt to render each frame programmatically.
One thing that they all share is that they inherit the limitations of their recording tools.
For example, ttyrec
fails to properly record any terminal size other than 80x24 and it has issues with glitching.
Any program that uses ttyrec
recordings internally therefore suffers from these same issues (though not all of the tools use ttyrec
).
ttystudio
Ttystudio is unlike the others in this category in that it uses the excellent blessed, term.js, and pty.js libraries instead of the more outdated ttyrec
/script
tools.
In my experience, the recording works flawlessly; there is no glitching and any terminal size is supported.
Additionally, ttystudio
supports both APNG and GIF outputs, does automatic frame offset optimization, and produces extremely lean and high quality animations (the one above is only 297 KB).
It can be installed very simply via npm
npm install -g ttystudio
and then run with either
ttystudio output.gif
ttystudio frames.json
ttystudio frames.json output.gif
if you would like to preserve the intermidate format.
Overall, the interface feels very polished and the GIF outputs are the most efficiently encoded of any of the tools here. This is probably my favorite tool to work with out of any of the ones on this list, but I have a few very minor nits to pick. Namely, that the font support is extremely limited and there’s no built-in support for changing the color schemes.
There is nominally support for changing the fonts, but only BDF fonts are supported. You can technically convert fonts to a BDF format using ttf2bdf (though the results are absolutely horrendous based on my experience). In practice, you’re pretty much stuck with either the bundled Terminus font or another bitmapped font. I’m not personally a huge fan of Terminus–and it has limited unicode support–so this is an issue for me.
There is no built-in support for changing the color scheme, but it’s actually relatively easy to hack in.
The rendering takes place in lib/writer.js where it’s hardwired to use the default terminal colors defined in blessed.colors.vcolors
.
I wrote a little bash script to extract my terminal’s color palette
#!/bin/bash
for i in {0..255}; do
exec < /dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
echo -en "\033]4;${i};?\033\\" >/dev/tty
if IFS=';' read -r -d '\' color ; then
result=$(echo $color | sed 's/^.*\;//;s/[^rgb:0-9a-f/]//g;s/rgb://')
fi
stty $oldstty
echo ${i}:${result}
done
based loosely on this really interesting StackOverflow answer.
I then executed the script from writer.js
and parsed my terminal’s colors to be used instead of the default ones.
I produced another animation with these colors and a font called Tamzen which has been patched to include powerline symbols.
I guess that I like this a little better, but I wish I kould find a better BPF font to use. If anybody knows one with full unicode support, then please leave a comment!
ttygif/ttyrec
Ttygif is one of the programs that uses ttrec
as a recording backend.
Yep, that means that it only supports terminals sized as 80x24 and that it tends to glitch with ncurses applications (as you can see above).
It’s simple enough to use… you just record with ttyrec
ttyrec game-of-life.rec
and then playback/create the GIF with ttygif
ttygif game-oflife.rec
to produce your GIF.
For the Game of Life animation, the memory usage ballooned up to an unbelievable 5 GB and the output file was 1.3 MB.
The output itself isn’t terrible–aside from the ttyrec
issues–but the memory usage is disqualifying for me… and I can’t see why you would use this over a generic screen recording application like gifine
.
ttygif
This one is also called ttygif, but it’s a separate project from the previous one.
It also uses ttyrec
as a backend… so only 80x24 and ncurses will glitch.
To install it, you need to setup a go environment and then run
go get github.com/sugyan/ttygif
followed by
~/go/bin/ttygif -in game-of-life.rec -out ttygif.gif
to convert the recording to a GIF.
This produces a relatively large 3.3 MB file with a very low framerate. It also takes 5 full minutes of processing to produce the GIF. That wouldn’t be a huge deal if it produced a better output, but… it doesn’t.
seq2gif
Seq2gif is another one that converts ttyrec
recordings to GIFs.
It does this without taking screenshots, instead using portable built-in terminal emulation engine originated from yaft.
It’s written in C and is fairly easy to compile
cd /tmp
git clone git@github.com:saitoha/seq2gif.git
cd seq2gif
./configure
make
and to run
/tmp/seq2gif/seq2gif -i game-of-life.rec -o seq2gif.gif
Used with the Game of Life recording, it produced an output file of 3.2 MB.
This is very large considering that this is using a bitmap font.
If you don’t mind using a bitmapped font with a default colorscheme, then you’re better off using ttystudio
… not seq2gif
.
ttyrec2gif
Ttyrec2gif is yet another one based on ttyrec
recordings.
It’s written by the same person as ttygif
, it’s also written in Go, and it has basically the same installation/running instructions.
go get github.com/sugyan/ttyrec2gif
~/go/bin/ttyrec2gif -in game-of-life.rec -out ttyrec2gif.gif
It produces an even larger 4.6 MB output file with terrible artifacting. Don’t use it.
recterm
Recterm is another… blah blah blah… based on ttyrec
… not worth using.
It’s actually the base project from which seq2gif
is forked, but seq2gif
apparently added some improvements because recterm
causes ncurses applications to crash.
That’s a flat out deal-breaker for me, I want a tool that is going to work consistently.
tty2gif
Guess what?
Tty2gif is based on ttyrec
… but don’t worry, I’ll be brief.
tty2gif game-of-life.rec ttygif.gif
*** buffer overflow detected ***: tty2gif terminated
======= Backtrace: =========
/usr/lib/libc.so.6(+0x7254c)[0x7f800f6c254c]
/usr/lib/libc.so.6(__fortify_fail+0x37)[0x7f800f74e377]
/usr/lib/libc.so.6(+0xfc2a0)[0x7f800f74c2a0]
/usr/lib/libc.so.6(+0xfc72d)[0x7f800f74c72d]
tty2gif(+0x275b)[0x55e39758475b]
tty2gif(+0x202d)[0x55e39758402d]
/usr/lib/libc.so.6(__libc_start_main+0xea)[0x7f800f67043a]
tty2gif(+0x22ca)[0x55e3975842ca]
That’s all I have to say about tty2gif
.
HTML/JavaScript Terminal Recorders
Alright, new section!
There will be no more ttyrec
… just kidding, there totally will be.
This section consists of recorders and playback tools that are designed specifically for embedding and rendering directly in a web browser. As we all know, web browsers are known for their strict adherence to web standards and uniform rendering behavior, so what could go wrong with this approach?
Asciinema
Asciinema is the clear star of the show when it comes to JavaScript playback. They sort of strive to be a YouTube of terminal recordings where everything is uploaded to their site and you can embed a player on your own. After uploading, you can adjust various settings like whether the recording is private and which color scheme to use during playback (e.g. Solarized Light/Dark, Monokai, Tango). You can’t adjust the font, but it’s reasonably good looking and it has solid unicode coverage. The tool is very polished, they use magic links instead of accounts, and everything pretty much just works out of the box.
It’s a popular tool, so it’s likely to be in the repositories for any major operating system, making installation very easy.
# Arch Linux Installation
sudo pacman -S asciinema
# macOS Installation
brew install asciinema
You can then record a terminal session into a JSON file.
asciinema rec -t 'Game of Life' asciicast.json
This launches $SHELL
in the same terminal by default, but you can optionally specify any other command.
The recording will finish whenever the command exits (so ctrl-D
to exit from a shell).
You’ll need to authorize your computer one time before uploading any recordings. This is done using magic URLs and I didn’t encounter any hiccups linking to my account.
asciinema auth
Open the following URL in a browser to register your API token and assign any recorded asciicasts to your profile:
https://asciinema.org/connect/1d90abbc-22fc-19a0-b1a1-a3b69cfa0d0a
After than initial authorization, you can upload your recordings and they’ll become available on the Asciinema.org site.
asciinema upload asciicast.json
https://asciinema.org/a/09yuoLlbWWOA00HzlDguQi5hZ
You can then visit the link directly to either watch the playback or modify the settings if you’re the owner.
The player can also be embedded on your site by including a single script
tag (as was done above).
<script type="text/javascript" src="https://asciinema.org/a/143553.js" id="asciicast-143553" async></script>
This injects an iframe
which works well on desktop, but it would take a lot of effort to make it adaptive and it doesn’t look very good on mobile.
I’m also not crazy about depending on an external source for content that’s crucial to an article.
There’s a non-negligible chance that the Asciinema.org site won’t be around forever, and a lot of sites will break if it goes offline.
The player itself is open source, so it is possible to embed it on your site without external resources. That said, they don’t include any build outputs and you’ll need to set up ClojureScript and LESS build environment just to produce them. It seems kind of not-so-subtle that they put a lot more effort into the instructions and tooling which involve putting tracking scripts and backlinks on your site than the ones that would allow you to self-host. That’s perfectly fine if that’s their perogative, but–for a tool that’s so otherwise polished–a lot of work is required to self-host and integrate a GIF fallback for users browsing without JavaScript.
ShowTerm
ShowTerm, like Asciinema, is another open source but heavily branded recorder that wants you to embed via an iframe
.
It has pretty much all of the downsides of Asciinema–like relying on an external site to stick around, being non-responsive, not preserving fonts or colors, and having no non-JS fallback–while having none of the polish or reliability.
I don’t want to be harsh here, but I really can’t see any reason at all why you would want to use ShowTerm over Asciinema.
The default installation method is to use Ruby Gems, but the native extensions fail to compile.
$ gem install showterm
Fetching: showterm-0.5.0.gem (100%)
Building native extensions. This could take a while...
ERROR: Error installing showterm:
ERROR: Failed to build gem native extension.
Luckily, they have a backup method that uses the industry standard best-practice of piping a curl
request directly into bash
.
bash <(curl record.showterm.io)
At least, it works though, right?
Uploading...
http://showterm.io/0ad098215d19edc64c78ecurl: (6) Could not resolve host:
Uploading failed, but don't worry! Your work is safe. You can try uploading again with:
curl showterm.io/scripts --data-urlencode cols=80 --data-urlencode lines=24 --data-urlencode scriptfile@/tmp/XhaLU.script --data-urlencode timingfile@/tmp/OE8ms.timing secret@/home/sangaline/.showterm
Depsite the error message, it actually did upload. If you manually separate the “curl” from the URL, you can extract the actual link to the recording. It mostly works, but there’s clear glitching when launching the Game of Life. There is also no customizability for the text styles and not even a half-hearted attempt at supporting self-hosting.
Overall, it’s far from the worst of the recorder/playback tools, but I really can’t see any reason why you would choose it over Asciinema.
TermRecord
This one kind of straddles the boundary between HTML/JavaScript and the RecTerm/Screen based categories because it uses ttyrec
as a backend, but outputs to HTML.
TermRecord is also the first contender in the HTML/JavaScript category that is geared more towards self-hosting.
As I’ve maybe more than hinted, I consider this to be a pretty big check in the PRO column, but not if it’s at the expense of functioning well.
That, unfortunately, is the case for TermRecord which suffers from serious glitching and resizing of the output space.
Echoing our experiences with ShowTerm, it appears that the author of TermRecord
is well-versed in software installation best practices.
# the official instructions for installing TermRecord
sudo pip install TermRecord
If you value the integrity of your system directories, then you might prefer my alternative instructions.
# install locally in ~/
pip install --user TermRecord
# add this to ~/.bashrc or equivalent if you haven't in the past
export PYTHONPATH=$(python -c "import site; print(site.USER_SITE)"):$PYTHONPATH
export PATH=$PATH:~/.local/bin
Recording is then pretty simple, you just run
TermRecord -o term-record.html
which will embed everything in term-record.html. It’s a nice enough interface, but it has serious glitching issues and it only seems to support an 80x24 terminal size. Those are both deal-breakers for me.
Terminal-Recorder
Terminal-Recorder is similar to TermRecord
in that it is intended to output HTML, CSS, and JavaScript for self-hosting.
It’s available as a node.js package, so installation is pretty simple.
npm install -g terminal-recorder
You then specify an entire output directory when you run it, this is meant to be populated with separate files for the different assets.
terminal-recorder -o terminal-recorder-output
The major problem is that the recording mechanism appears to cause issues with programs that use ncurses
(and the smaller problem is that it only supports an 80x24 terminal size).
Traceback (most recent call last):
File "./gol.py", line 346, in <module>
main()
File "./gol.py", line 335, in main
game = gol(parser.parse_args())
File "./gol.py", line 34, in __init__
self.initCurses()
File "./gol.py", line 88, in initCurses
curses.curs_set(0)
_curses.error: curs_set() returned ERR
Exception AttributeError: "'gol' object has no attribute 'win'" in <bound method gol.__del__ of <__main__.gol object at 0x7fcf0e740f90>> ignored
It seems to work OK otherwise, but I need a recording tool that isn’t going to randomly break curses applications. This is another flop as far as my personal use-cases are concerned.
Shelr
You can still find Shelr on github, but Shelr.TV is no more. I’m including this primarily as a foreboding tale of what can happen to these websites that encourage you to host your terminal recordings externally. If you check out their Twitter, you can see that they were looking for maintainers, but eventually gave up and let the site die. What’s to stop this from happening to Asciinema or ShowTerm?
Conclusion
I can summarize my thoughts pretty simply here:
- If you care about fonts and color schemes, then use a generic screen recorder like gifine. You can use a GIF, but you’ll likely get better compression with video and people will be able to seek during long recordings.
- If you don’t mind using the Terminus font, then use ttystudio. It’s going to faithfully reproduce your recordings and produces the most optimized GIFs of any of the tools.
- If you want a web-based player–and you don’t care about mobile users or relying on an external service– then use Asciinema.
I’ll add that I think the best solution would be a tool on top of Asciinema that outputs a self-hosted version of the player and adds responsive scaling and a GIF fallback for users without JavaScript.
Unfortunately, that tool does not seem to exist.
If you ever find yourself thinking, “you know, I should really build a terminal recorder that uses ttyrec
behind the scenes,” then please–for the love of God–build this instead.
Oh yeah, and please pay us to do web scraping for you. We’re really, really good at it.
Comments