UK | EN |
LIVE
Світ 🇺🇸 США

Wake up! 16b

Hacker News MaximilianEmel 2 переглядів 5 хв читання

Released at the Outline Demoparty in May 2026, Ommen, NL
An exploration of algorithmic density in 16 bytes of x86 assembly.

Watch Video Demozoo Entry

Hey everyone. I learned programming as a kid on an old IBM PC with a monochrome green monitor over 30 years ago and always wanted to create a program for this system. I created well over 100 tiny intros in the last 15 years. Recently I was not too active but the fantastic "Rainbow Surf" from Plex in just 16 bytes motivated me to dig up some old dusty sketches again and get to work.

The creation of this program happened with the usual tinkering around. I was messing with cellular automaton for graphics and sounds and discovering sizecoding tricks. Actually: a) polymorphic asm instructions, like add [bx+si],al which is 0x0000 b) jumping into the middle of instructions to save bytes and reuse opcodes. In hundreds of tiny experiments, this one stuck out, just by the sound of it.

When I unfolded what's left and removed "the rest", I had a hard time to grasp what's really going on. I was scratching my head looking at the simple formula that remained after golfing many bytes away. I myself didn't expect that the explanation would go this deep for just these few bytes xD.

My original "M8trix" from 2014 already did smear pseudorandom letters across the screen (in 8 bytes, then in 7) and I always wondered how I could make it "sound good". But chronologically in the development of "wakeup", the sound was first. Since you "see what you hear" it doesn't really matter, but "16 bytes that turn Sierpinski sound into Matrix rain" would be a good subtitle =)

So, here are the 16 bytes of x86 real-mode DOS assembly. When you run it, it uses the video memory as a calculation space to draw an infinite Sierpinski fractal, and at the same time bangs the speaker with that geometry.

int 10h ; 2 bytes mov bh, 0xb8 ; 2 bytes mov ds, bx ; 2 bytes L: lodsb ; 1 byte sub si, byte 57 ; 3 bytes xor [si], al ; 2 bytes out 61h, al ; 2 bytes jmp short L ; 2 bytes

1. The Canvas: A Primed Void

The code starts with a standard BIOS interrupt: int 10h. This sets up video mode 0, giving a 40x25 text mode grid. Then the data segment (ds) is pointed to 0xb800, the memory address of the VGA/CGA text buffer.

When the BIOS clears the screen, it doesn't fill memory with absolute zeroes. Every character space is two bytes: the ASCII character and the color attribute. All 2,000 slots are set to 0x20 (space) and 0x07 (light gray on black). So the screen looks empty, but the memory is already filled with a uniform pattern.

The intertwine, the synesthesia goes far beyond what I found so far in other tiny intros. I would even go so far as to say it's revealing more mathematical secrets and relations than using iterated function systems for the "chaos game" without an RNG. Anyway, this time I want you to fundamentally understand the mathematics of what you hear. Not just "you do some operation here and then it sounds interesting".

To strip it down to pure math: assume a zeroed state instead of 0x20, use add instead of xor, and step forward 16 bytes at a time. Assume the accumulator al starts at 2.

A DOS segment is exactly 65,536 bytes. Moving 16 bytes per step means exactly 4,096 steps to traverse the segment (\( 65536 / 16 = 4096 \)). Then si wraps cleanly back to 0x0000.

Adding up values between cells creates partial sums. Because 4,096 is a multiple of 256 (the 8-bit register size), the carryover aligns perfectly when the segment wraps, cleanly resetting al to 2 at the start of each sweep.

The value follows a binomial sequence, scaled by 2:

Here is how the first 16 steps accumulate row by row:

Now, back to combinatorics. By special laws, when doing modulo two, the Sierpinski triangle emerges. This specific bit is what gets banged into the speaker, while the other bits are ignored.

To separate the bitplanes, the fact that carry-free addition of bits is XOR is why it is there instead of add.

Since the code starts with 2 (binary 00000010), only bit 1 is toggled between 0x00 and 0x02. This perfectly maps to rule 60 in elementary cellular automata:

Lucas's theorem guarantees this matches bit 1 from the addition table. See for yourself ('2' means bit 1 is set):

Port 61h interfaces with the PC speaker. Bit 1 pushes the speaker cone out (1) and pulls it in (0). The code computes the fractal via XOR, writes it to memory, and shoves that byte straight into the speaker port.

The 1s and 0s from the fractal create square waves that shift naturally in pulse width and frequency:

When played linewise, this creates self-similar, almost tempo-invariant bytebeats.

But it gets better: not only the text is output to the speaker but also the remaining bytes of the 64 kilobyte segment, which in this case also contains shadowed video ROM BIOS code, which is the secret ingredient to the punky and gritty sound that differs quite a bit from the expected Sierpinski line based overlayed rectangle wave bytebeat.

To recreate the M8trix effect, the cells themselves have to be splat across the screen in a way that the sound buffer is not too large and the screen is nicely sparsely filled with chars.

So the code doesn't step by 16. sub si, byte 57 plus lodsb means it moves -56 bytes per iteration - going backwards.

56 doesn't divide 65,536 evenly. The code only hits offsets that are multiples of 8, taking 8,192 steps and wrapping 7 times before resetting. This doubles the cycle length, halving the fundamental frequency. The sound drops one octave.

Moving back 56 bytes on an 80-byte wide screen is like moving forward 24 bytes (12 columns). Only 10 distinct columns are visited. The fractal isn't drawn as a solid image; it shears diagonally into 10 pillars of characters moving up the screen.

The scener miragept did a capture with this motivation:

Emulators and different BIOS versions leave slightly different artifacts in RAM. Since the code XORs against whatever is there, the output is highly sensitive to the environment. Clearing the memory first would give a perfectly uniform output, but that costs precious bytes. Embracing the hardware's natural state is just part of the charm of sizecoding. Thanks for reading.

Поділитися

Схожі новини