From 1b991a54cc834e8ef9ccc8bb15dce7ff70cdf8a3 Mon Sep 17 00:00:00 2001 From: Sadeep Madurange Date: Wed, 24 Dec 2025 16:29:32 +0800 Subject: Matrix post. --- _log/_site/matrix-digital-rain.html | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 _log/_site/matrix-digital-rain.html (limited to '_log/_site/matrix-digital-rain.html') diff --git a/_log/_site/matrix-digital-rain.html b/_log/_site/matrix-digital-rain.html new file mode 100644 index 0000000..85bac5d --- /dev/null +++ b/_log/_site/matrix-digital-rain.html @@ -0,0 +1,103 @@ +

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

+ +

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.

+ +

Phosphor decay

+ +

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

-- cgit v1.2.3