--- title: The Matrix digital rain date: 2024-01-12 thumbnail: thumb.png layout: post --- "All I see is blonde, brunette, red head." The iconic digital rain from The Matrix, implemented in C, without dependencies (not even ncurses). This project is a fork of Domsson's beautiful Fakesteak. Use the following commands to compile and run the program: ``` $ cc -O3 main.c -o matrix $ ./matrix ``` While I loved Domsson's take on the digital rain, what blew my mind was the minimalistic elegance of his code. As I carefully examined it, I thought about what it might take to recreate the original digital rain from the first Matrix movie with it. The challenge is adding these features without destroying fakesteak's elegance. ## How does it work? The `matrix` struct makes use of three 2D arrays to encode the Matrix: the `code` array for 32-bit Unicode characters, the `rgb` array for 24-bit RGB values of the character (foreground color), and the `shade` array for the degree of transparency of the character to simulate the ghosting effect of old monochrome displays. The dimensions of these arrays depend on the size of the terminal screen. Each slot in the array corresponds to a cursor position on the screen. The ghosting effect, which is arguably the crowning feature of my version, is implemented by carefully scaling and mixing the RGB channels: ``` static void mat_shade(matrix *mat, size_t row, size_t col) { unsigned char *color; color = mat->rgb[mat_idx(mat, row, col)].color; color[R] = color[R] - (color[R] - COLOR_BG_RED) / 2; color[G] = color[G] - (color[G] - COLOR_BG_GRN) / 2; color[B] = color[B] - (color[B] - COLOR_BG_BLU) / 2; } ``` The above algorithm achieves transparency by iteratively bringing the foreground color closer to the background color with each pass of the rain. This approach offers multiple advantages, such as simpler and more natural color configuration (background, foreground, and the color of the first drop) that lends itself well to Unix ricing, and of course, recreates The Matrix rain with high fidelity. Rather than heavy-weight graphics tool kits, we use ANSI escape codes to control the terminal screen. It's the effective use of the ANSI escape codes that greatly contributes to the minimalism of the solution: ``` static void term_print(const matrix *mat, size_t row, size_t col) { size_t idx; idx = mat_idx(mat, row, col); wprintf(L"\x1b[%d;%dH\x1b[38;2;%d;%d;%dm%lc", row, col, mat->rgb[idx].color[R], mat->rgb[idx].color[G], mat->rgb[idx].color[B], mat->code[idx]); } ``` Finally, the glitch effect is controlled by the following code: ``` if (mat.row[i] > 0 && rand() % 6 == 0) { j = rand() % mat.row[i]; if (mat.code[mat_idx(&mat, j, mat.col[i])] != ' ') { mat_put_code(&mat, j, mat.col[i]); term_print(&mat, j, mat.col[i]); } } ``` The above code causes glitches in the Matrix with a probablity of 1/6. ## Customizing the rain While you can customize almost any aspect of the rain including its speed, glitch frequency, and the density of the rain, the most useful settings for ricing are the color scheme and the character set used for the rain. There are three color settings: the head, the tail, and the background. They are configured by setting the `COLOR_*_RED`, `COLOR_*_GRN`, and `COLOR_*_BLU` definitions in main.c. The `UNICODE_MIN` and `UNICODE_MAX` values control the Unicode block used. For instance, setting them to `0x30A1` and `0x30F6` rains Katakana code points: Files: [source.tar.gz](source.tar.gz)