Sometimes a help text to convey
how to properly use a command, or a help text exists but people are not reading it. In these cases
a short video might do the trick. asciinema is quite popular and allows to record terminal
sessions and replay them later on. But to embed these recordings in a web page another tool was needed: asciicast2gif.
asciinema
was easy to install as its available in many distributions:
sudo dnf install asciinema
Record sessions with rec
, play sessions with play
, easy enough.
asciicast2gif
wasn't available in our distribution and needed to be installed
manually:
npm install asciicast2gif
sudo dnf install ImageMagick gifsicle
The first few tries failed with a horrid error message, so let's see what asciicast2gif
really does:
$ asciicast2gif /tmp/test.cast /tmp/test.gif
[...]
convert -loop 0 -delay 20 /tmp/tmp.UccOho2kuG/0.png \
-delay 420 /tmp/tmp.UccOho2kuG/1.png \
-delay 86 /tmp/tmp.UccOho2kuG/2.png \
[...] -layers Optimize gif:- | gifsicle -k 64 -O2 -Okeep-empty --lossy=80 -o /tmp/test.gif
And looking in dmesg revealed that convert
was running out of memory
while trying to convert a ~30 second screen cast recording! After granting the machine a bit more memory, the
command completed and we now had a working GIF to embed in
our web page:
$ asciicast2gif -t tango test.cast test.gif
==> Loading test.cast...
==> Spawning PhantomJS renderer...
==> Generating frame screenshots...
==> Combining 137 screenshots into GIF file...
gifsicle: warning: huge GIF, conserving memory (processing may take a while)
==> Done.
$ mv test.gif ~/www/
$ cat ~/www/test.md
[...]
[video](test.gif)
Note: limiting the memory usage
of the node
process did not seem to work, as can be seen in top
:
Tasks: 117 total, 2 running, 115 sleeping, 0 stopped, 0 zombie
%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 2.0 us, 0.0 sy, 0.0 ni, 74.0 id, 24.0 wa, 0.0 hi, 0.0 si, 0.0 st
GiB Mem : 7.8 total, 1.7 free, 6.0 used, 0.1 buff/cache
GiB Swap: 0.1 total, 0.0 free, 0.1 used. 1.6 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1971 dummy 20 0 5996.9m 5.8g 1.5m R 103.0 75.0 1:05.03 convert -loop 0 -delay 13 [...]
1958 dummy 20 0 635.3m 30.7m 0.2m S 0.0 0.4 0:05.03 node --max-old-space-size=512 [...]
The actual gif
quality varies depending on what's shown on the terminal. Movie like sequences look a bit
crappy though, but that's not what we wanted to do in the first place, hm? :-)

error message
gifsicle:<stdin>: empty file
at checkExecSyncError (child_process.js:630:11)
at Object.execSync (child_process.js:666:15)
at Dp (/home/dummy/node_modules/asciicast2gif/main.js:708:246)
at /home/dummy/node_modules/asciicast2gif/main.js:713:178
at Function.b [as h] (/home/dummy/node_modules/asciicast2gif/main.js:709:287)
at ep (/home/dummy/node_modules/asciicast2gif/main.js:697:48)
at /home/dummy/node_modules/asciicast2gif/main.js:697:193
at /home/dummy/node_modules/asciicast2gif/main.js:689:264
at Immediate.Po (/home/dummy/node_modules/asciicast2gif/main.js:685:331)
at processImmediate (internal/timers.js:456:21) {
status: 1,
signal: null,
output: [
null,
Buffer(0) [Uint8Array] [],
Buffer(29) [Uint8Array] [
103, 105, 102, 115, 105, 99, 108,
101, 58, 60, 115, 116, 100, 105,
110, 62, 58, 32, 101, 109, 112,
116, 121, 32, 102, 105, 108, 101,
10
]
],
pid: 2836,
stdout: Buffer(0) [Uint8Array] [],
stderr: Buffer(29) [Uint8Array] [
103, 105, 102, 115, 105, 99, 108,
101, 58, 60, 115, 116, 100, 105,
110, 62, 58, 32, 101, 109, 112,
116, 121, 32, 102, 105, 108, 101,
10
]
}