4x smaller, 50x faster Published on 22 Nov 2021 by Marcin Kulik

It’s been a while since asciinema-player 2.6 was released and a lot has changed since. Version 3.0 is around the corner with so much good stuff, that even though it’s not released yet, I couldn’t wait any longer to share.

Long story short: asciinema-player has been reimplemented from scratch in JavaScript and Rust, resulting in 50x faster virtual terminal interpreter, while at the same time, reducing the size of the JS bundle 4x.

You may wonder what prompted the move from the previous ClojureScript implementation. As much as I love Clojure/ClojureScript there were several major and minor problems I couldn’t solve, mostly around these 3 areas:

Btw, special shout out to Ryan Carniato, the author of SolidJS, for focusing on speed and simplicity, while not compromising on usability. Thanks Ryan!

Now, on top of all the above, I had fun building terminal control sequence interpreter in Rust, using excellent resource for that - Paul Williams’ parser for ANSI-compatible video terminals. Special shout out to Paul Williams!

But back to speed. It used to be good enough, which is no longer good enough for me. The old player used to be sufficiently fast for probably 90% of the recordings people host on asciinema.org. It exercised many types of optimizations, like memoization (trading memory for CPU time) and run-ahead (which used a lot of memory by precomputing terminal contents for each future frame).

At first I planned to implement the terminal emulation part in Rust without any optimizations, just write idiomatic Rust code, then revisit the tricks from the old implementation. The initial benchmarks blew my mind though, showing that spending additional time on optimizing the emulation part is absolutely unnecessary.

The numbers show how many megabytes of text the terminal emulator can process in each player version (tested on Chrome 88):

recording v2.6 (MB/s) v3.0 (MB/s) ratio
https://asciinema.org/a/20055 0.61 35.41 58x
https://asciinema.org/a/153907 0.33 24.27 73x
https://asciinema.org/a/44648 0.51 26.81 52x
https://asciinema.org/a/117813 0.82 37.73 46x
https://asciinema.org/a/325730 0.58 26.13 45x

50 times faster on average!

Note that the above benchmark represents the speed of text stream parsing (including control sequences), as well as updating emulator’s internal, virtual screen buffer. This has been the bottleneck in the previous implementation of the player. The benchmark doesn’t measure rendering of the buffer to the actual screen (DOM), therefore the rendering speed improvements coming from React.js->SolidJS transition are not included here. However, SolidJS has been benchmarked against React.js and other libs many times already, so I didn’t bother proving it’s faster.

I still thought I may need to implement some form of terminal state snapshot/restore to support the “seeking” feature. This feature requires feeding the terminal emulator with the whole text stream between the current position and the desired position, or in the worst case when you’re seeking back, feeding the emulator with the whole text from the very beginning of the recording up to the desired position. Optimizing this could be done, for example, by keeping snapshot of the terminal emulator state at multiple time points, sort of like having key-frames every couple of seconds. In ClojureScript implementation this came for free, thanks to the immutable data structures. In the new JS+Rust implementation this would have required extra work, but it turned out, that’s not needed either - clicking on the progress bar in the new player, to jump to the desired time in the recording, results in instantaneous jump, even when it has to feed the emulator with megabytes of data to parse and interpret.

Other than the speed and size improvements, the new version of the player brings more nice things, like automatic scaling of the player to fill its container (as seen above), as well as WebSocket and custom “drivers”. More on these in the upcoming posts.


Did you like it? Feel free to send me an email with your feedback to . You can also reach me on Mastodon at @ku1ik@hachyderm.io. Thanks!