asciinema & asciicast2gif
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:
sudo dnf install asciinema
Record sessions with asciinema rec
, play sessions with asciinema play
, cool.
Update: asciicast2gif is no longer maintained and agg appears to be the successor, and is much faster than asciicast2gif
. Once all this was installed, generating GIFs was easy enough. Stitching both tools togeter:
$ asciinema rec demo.cast timeout 5 asciiquarium ^D $ agg demo.cast demo.gif
Et voilà:
For posterity, let's leave these old notes in place:
$ 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 ] }