The Matrix digital rain implemented in raw C using ANSI escape sequences with zero dependencies—not even ncurses.
This project began over three years ago as a fork of Domsson’s unique rendition of the Matrix rain: Fakesteak. I aimed to modify the algorithm to produce a rain that resembled the original with high visual fidelity.
Unicode support in the 2022 version lacked flexibility. The charset used in the
rain had to be a single contiguous block defined by UNICODE_MIN and
UNICODE_MAX settings:
#define UNICODE_MIN 0x0021
#define UNICODE_MAX 0x007E
static inline void insert_code(matrix *mat,
size_t row, size_t col)
{
mat->code[index(mat, row, col)] = rand()
% (UNICODE_MAX - UNICODE_MIN)
+ UNICODE_MIN;
}
There was no way, for instance, to use both ASCII and Katakana at the same
time. The user had to pick one. In the new version, the user can use any number
of Unicode blocks using glyphs array. In fact, the default rain now includes
both ASCII and half-width Katakana characters:
#define UNICODE(min, max) (((uint64_t)max << 32) | min)
static uint64_t glyphs[] = {
UNICODE(0x0021, 0x007E), /* ASCII */
UNICODE(0xFF65, 0xFF9F), /* Half-width Katakana */
};
static uint8_t glyphlen = (sizeof glyphs) / (sizeof glyphs[0]);
static inline void insert_code(matrix *mat,
size_t row, size_t col)
{
uint64_t block;
uint32_t unicode_min, unicode_max;
block = glyphs[(rand() % glyphlen)];
unicode_min = (uint32_t)block;
unicode_max = (uint32_t)(block >> 32);
mat->code[index(mat, row, col)] = rand()
% (unicode_max - unicode_min)
+ unicode_min;
}
Entries in the glyphs array are Unicode blocks bit-packed in an 8-byte
container: the four low bytes forms the first codepoint and the four high bytes
the last.
The dim afterglow of monochrome CRT displays is achieved by carefully scaling the RGB channels individually and mixing them:
#define DECAY_MPLIER 2
static inline void blend(matrix *mat,
size_t row, size_t col)
{
unsigned char *color;
color = mat->rgb[index(mat, row, col)].color;
color[R] = color[R] - (color[R] - RGB_BG_RED) / DECAY_MPLIER;
color[G] = color[G] - (color[G] - RGB_BG_GRN) / DECAY_MPLIER;
color[B] = color[B] - (color[B] - RGB_BG_BLU) / DECAY_MPLIER;
}
The blending function emulates the phosphor decay by gradually transitioning each raindrop’s color towards the background color. The multiplier is the number of passes over the rain track needed before the afterglow disappears.
Nonetheless, the rain resembles the original with high visual fidelity. It’s highly customizable and gentle on the CPU. On my 14” ThinkPad T490, which has a resolution of 1920x1080 and 4GHz CPU, it uses 2-3% of the CPU with occasional jumps of up to about 8%. Not too bad for a weekend project. The program has been tested with xterm and urxvt terminal emulators on OpenBSD and Arch Linux systems. Someone has managed to get it moving on a Raspberry Pi as well.
Lastly, to compile and run:
$ cc -O3 main.c -o matrix
$ ./matrix
“All I see is blonde, brunette, red head.”
Files: source.tar.gz