summaryrefslogtreecommitdiffstats
path: root/_log/matrix-digital-rain.md
blob: 0a3d30d41137b221fbfb3d5008d375470e1b41a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
---
title: Recreating the Matrix rain with ANSI escape sequences
date: 2022-08-22
layout: post
project: true
thumbnail: thumb_sm.png
---

Over the weekend, I came across Domsson's <a
href="https://github.com/domsson/fakesteak" class="external" target="_blank"
rel="noopener noreferrer">Fakesteak</a>: a beautifully lean rendition of the
Matrix rain in raw C using ANSI escape sequences—zero dependencies, not even
ncurses.

The project's README noted that it didn't support Japanese characters and that
it used 8-bit color mode. The latter meant that the ghosting effect has to rely
on different foreground colors rather than shades of the same color.

As a tip of the hat to Domsson's impressive work, I decided to add Unicode and
24-bit truecolor support to it, aiming to faithfully recreate the original
Matrix from the first movie during Neo and Cypher's conversation:

<video style="max-width:100%;" controls="" poster="poster.png">
  <source src="matrix.mp4" type="video/mp4">
</video>

Adding Unicode support via `wchar_t` and `wprintf()` was easy enough.
Implementing the ghosting effect with truecolor support, however, turned out
harder than expected. To achieve the ghosting effect, I treated phosphor decay
as a multiplier, allowing me to emulate a dim afterglow by gradually
transitioning each raindrop's RGB values towards the background color:

```
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;
}
```

Looking back at the implementation, there are still a few improvements to be
made. Instead of using a dedicated buffer, I should have bit-packed the
phosphor decay into the RGB data buffer to save memory. I'm not entirely
satisfied with the Unicode support as it's restricted to contiguous code
points. The glitch effect, which I implemented with characters unexpectedly
changing, really should flash white as well.

Nonetheless, the rain closely resembles the original, is highly customizable,
and is gentle on the CPU. Not too bad for a weekend project. I tested the
program 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](source.tar.gz)