summaryrefslogtreecommitdiffstats
path: root/_log/matrix-digital-rain.md
blob: d8e2543b0e879e8c52c53f913303a8aaf0d9e476 (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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
---
title: The Matrix digital rain
date: 2022-08-22
layout: post
project: true
thumbnail: thumb_sm.png
---

"All I see is blonde, brunette, red head." The iconic digital rain from The
Matrix in C, with zero dependencies—not even ncurses.

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

## Overview

This is my fork of Domsson's beautiful <a
href="https://github.com/domsson/fakesteak" class="external" target="_blank"
rel="noopener noreferrer">Fakesteak</a>. While going through his code, I
wondered what it would take to faithfully recreate the original Matrix from the
first movie without sacrificing its minimalism.

My implementation supports:

 - Unicode characters.
 - 24-bit RGB colors (truecolor).
 - Glitches in the matrix.
 - Ghosting effect of old monochrome CRT displays.
 - Closely resembles the Matrix seen in the background during Neo and Cypher's
   conversation.

With no dependencies, compilation is trivial:

```
$ cc -O3 main.c -o matrix
$ ./matrix
```

## How does it work?

The program tracks the state of the terminal, such as code points, background
and foreground colors, and cursor position, using multiple internal data
buffers. On each frame, it updates these buffers and repaints the screen using
ANSI escape codes:

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

The ghosting effect is achieved by carefully scaling the RGB
channels before mixing them:

```
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 ghosting function emulates the dim after glow by gradually transitioning
each raindrop's color towards the background color. This approach provides two
key benefits: straightforward color configuration that integrates naturally
with (Unix) ricing and high-fidelity recreation of the Matrix aesthetic.

## Customization

While you can alter almost every aspect, including speed, glitch frequency, and
rain density, the most common customizations are the color scheme and character
set.

There are three color settings: head, tail, and background. You can configure
them using `COLOR_*_RED`, `COLOR_*_GRN`, and `COLOR_*_BLU` definitions found in
main.c. 

The `UNICODE_MIN` and `UNICODE_MAX` values control the Unicode block used. For
example, setting them to `0x30A1` and `0x30F6` rains Katakana, if a font that
supports Katakana is present on the system:

<img style="width: 100%;" src="katakana.png" />

Files: [source.tar.gz](source.tar.gz)