The final result, running on a random number generator as input

Having recently finished my undergraduate studies I’ve found myself with more free time than I’m used to. I figured it would be good to put at least part of this time towards learning the Rust language – a relatively new low-level language with excellent memory efficiency. I’ve always found it easier to learn new languages and libraries when I’m working towards a project (however silly the idea), so I got to work thinking of ideas.

The cmatrix program has always been a favourite of mine – what better way to show off your hacking prowess than having an entire terminal dedicated to scrolling matrix-style text? However, it bugged me a little that this program, however aesthetic, didn’t really have a practical use. Enter data-matrix! This program takes arbitrary data from STDIN (the standard input stream in unix-like systems) and prints it to the terminal cmatrix-style. This means you can run any script that generates output, pipe it to data-matrix, and view the output as it’s produced in a pretty matrix cascade!

You can read the source code and install it yourself by checking out the github repo.

Implementation

Here I’ll attempt to explain some of the ideas that went into building this project, although you’ll need to check out the source code for a full understanding of how it works.

The bulk of this project was built around the console_engine crate, which provided some useful bindings for drawing to the terminal in real-time – using this crate we can set a target framerate and draw to the terminal each frame by writing characters to specific locations. For each line of code we have an instance of the Line struct, which holds its text, position, and the length of the trail of random characters above each character. Every frame we move each line down one position, draw every line, and draw the trails above every line.

For the trails we maintain a 2D Vec of chars which contains a random character for each terminal co-ordinate. We query this array every time we need to draw a trail character, and each frame gives each character a small random chance of changing to a different random character. This mirrors the original cmatrix’s behaviour of having consistent trails which occasionally change.

One of the challenging parts of getting this to work was interfacing with STDIN – reads on STDIN when no input has been created block the thread, so we needed a way for reads to happen in the background while the main thread focused on drawing. If we didn’t do this, the script would freeze up while it waited for additional input! So we spawn a new thread which waits for input from STDIN and sends the result over a channel to the main thread. The main thread has a receive on this channel with a 1 millisecond timeout, allowing it to continue with drawing to the terminal if no inputs have been produced that frame.