A bit frustrated by the relatively simple effect for Mana Fortress last year, I wanted to create a more complex scene, mixing sprites and generated graphics.
After Revision 2025 ended, I found a better way of directly generating a layout without creating an explicit grid, hopefully freeing enough space for something more interesting.
~
Since Revision happened early April this year, I also wanted to avoid the rush of previous year by starting earlier.
In the end I worked on the intro for about a month, and made the last decisions hours before the deadline, as usual :)
Also as usual, since I’m largely on the sorcerer side of programming, I ended up with something completely different from what I had in mind initially :)
The intro is available here and on pouët.net, it needs DOSBox with “cycles=60000” configured (and a CPU powerful enough) to run it properly.
Inspiration
My original idea was to create a scene combining one or two simple sprites with procedurally generated effects.
I found some great pixel art depicting quartz and rocks floating into space :
- https://www.deviantart.com/pauriva/art/Pixeltober-10-2020-858135260
- https://www.deviantart.com/pauriva/art/Pixeltober-9-2020-857596080
- https://www.deviantart.com/314erry/art/Neutron-Star-849886516
I figured the complex shapes could be rendered using sprites, leaving the background to be generated with code.
~
I also studied scenes from Secret of Mana and Chrono Trigger (did I mention I liked SNES RPGs ?), notably the ground, quartz, and waterfalls :
Kingdom of Zeal, from Chrono Trigger
Ice Country, from Secret of Mana
Water Palace, from Secret of Mana
I noticed the waterfall pattern in SoM was relatively simple (only two 16x16 sprites) and created a nice flow effect when moving :
So at this point my plan was to :
- try implementing a better tiling system,
- create a waterfall-like effect like the above in code (instead of using a sprite),
- see if I could add some sprites to complement it.
Tiling system (again)
Unlike my previous intro, I wanted to try a different tiling system that does not rely on computing a “tile ID” and a 0-255 pixel index for each 16x16 tile.
For some reason I directly tried using the Rrrola’s trick, which computes an approximated X and Y screen coordinates from the screen pointer DI :
mov ax, 0xCCCD ; rrrola constant
mul di ; multiply this constant with screen pointer
; DL : approximated X screen coordinate (0-255)
; DH : Y screen coordinate (0-200)
This code uses only 5 bytes (3 for the constant, 2 for the MUL), compared to the classical way, which uses 9 bytes :
mov ax, di
mov bx, 320
xor dx, dx
div bx ; divide the screen pointer stored in AX by 320
; DX : X screen coordinate (0 - 320)
; AX : Y screen coordinate (0 - 200)
There is only a ‘small’ problem : since X is approximated, it would be impossible to render a sprite without artifacts/duplicated pixels. But I didn’t figured that at the time :)
Tile coordinates
To compute the X/Y coordinates local to each tile, we can use a logical AND instead of a DIV, as long as the tiles size are a power of two :
; example here with 8x8 pixels tiles
mov al, dl ; get screen X coordinate into AL
and al, 0x07 ; compute X % 8 : x = [0-7]
; y % 8 : [0 - 7]
mov bl, dh ; get screen Y coordinate into BL
and bl, 0x07 ; compute Y % 8 : y = [0-7]
shl bl, 3 ; bit-shift left 3 times (i.e. multiply Y by 8), since each row is 8 pixels
add al, bl ; add Y to X
; generates a 8x8 tile with colors ranging from 0 to 63
This saved a lot of bytes compared to my previous intro, which used far too many DIV to compute modulos.
Waterfall ?
With the tiling system done, I tried to create a waterfall-like effect, and failed :
Pseudo-random noise looked ugly, and I failed computing a gradient that looked convincing enough. I didn’t want to use a sprite, since it would consume too much size to add something interesting after that.
Dark portal
I tried playing with the coordinates, different colors, adding symetry, until I stumbled upon this :
Not quite a water-like effect, but I tried reverting it to create a portal-like effect in the top-half part of the screen :
At this point I thought I could add something like a bridge to the bottom half to complement the effect, inspired by The Maw of Darkness by Julian Faylona.
I couldn’t find anything good enough to add, and the top/bottom transition was too harsh, so I kept trying other variations.
Bubbles
I tried computing a distance function from the center (i.e. creating a circle using the FPU), and adding it to the color computation for each pixel in a tile.
This ended up creating some interesting gradients :
Then bubble-like patterns :
Then finally this with less eye-popping colors :
… which kinda looked like a waterfall, I guess ? :’)
More experiments
Since the previous effect only had a screen-wide horizontal symmetry, I tried adding a vertical one and testing all gradient variations :
At this point I noted that my current palette had an off-by-one bug (same bug I did during Hellgate) :
The intended gradient should have used 8 colors, since the tiles were 8x16 pixels, so the black should have been in the 8th position instead of the 7th. And much like Hellgate, this created interesting patterns, so I kept it that way.
~
At this point, the effect was relatively interesting, and the intro used ~150 bytes, so plenty of bytes left ; I thought I could use this effect as a background, then add a sprite on top of it.
Searching through all variations, I thought about using this one, which created a nice organic effect :
At this point I realized that the Rrrola’s trick approximation created this “strange” discontinuity in the middle of the screen, and this would likely prevent me to display a sprite correctly by ruining the pixel computations.
I tried swapping the code with the exact division instead, but it ended up ruining the effect altogether.
Tweaking the palette
Realizing I could probably not use sprites, I tried creating a more interesting background by using a full 256 colors palette with gradients, instead of the 16-colors teal version.
This could be used to apply different 16-colors patterns depending on the position of the screen, and time.
I spent some time trying to come up with interesting colors, and many experimentations/headaches later ended up on this :
mov dx, 0x3c8 ;
xor ax, ax ; start at color 0
out dx, al ; select palette colors
inc dx
mov ax, 58 ; starts AX at 58
push ax ; save AX value for later
xor bp, bp ; use BP as the iteration counter (0 -> 255)
color:
out dx, al ; output red
mov bx, bp ; copy BP into BX
shr bx, 4 ; divide BX by 16
shl bx, 2 ; multiply BX by 4
sub ax, bx ; subtract AX with BX
; if AX is now negative : set G & B to 0 instead of overflowing
jns next2
xor ax, ax ; AX = 0
next2:
out dx, al ; output green
out dx, al ; output blue
pop ax
sub ax, 9 ; remove 9 from AX
and ax, 0x3F ; compute AX modulo 63, since 63 is the maximum possible color index
push ax ; save AX for next iteration
inc bp
loop color ; loop until all 255 colors are set
This starts with a white to black gradient, which progress into a red to black one later in the palette :
Blood wave
I replaced the teal palette in the original effect by this new one, applying the gradient by adding a palette offset depending on elapsed time and the position of the screen.
The resulting effect is probably one of my favorite visual so far :)
I just loved the combination of the quasi-organic effect of the bubbles, the ‘boiling’ effect created by the animation, and the red/white transition creating a nice contrast accross the screen.
As each pixel has a different color, this is an example where running the intro on DOSBox will display much better graphics, since the video compression tends to blur all these little details.
I tried to find other variations by changing the symmetry or the palette computation :
First test, with an horizontal scrolling :
Diagonal scrolling from top/left to bottom/right corner (my favorite) :
Same with inverted colors, I also liked it but would probably look better with a slower speed, or a static scene :
Changing a single multiplication by 8 to 4 :
This last one created a cool horror-like organic effect, but I did not have any idea how to improve upon it, so I kept my favorite.
Underwater scene
Of course I also tried to tweak the palette itself, by removing the red component and using the gradient for the green + blue components instead :
This created a nice water-like wave effect, at least for me ; others have seen a more vegetal/alien thing :
To improve the underwater effect, I added a blue layer by XOR-ing the blue component, leading to this palette :
color:
xor ax, ax ; => set the red to 0 in all cases
out dx, al ; output red
pop ax ; => get back the gradient value earlier than the white/red palette
mov bx, bp
sub ax, bx ; => subtract the iteration counter
jns next2
xor ax, ax ; => prevent green & blue from underflowing
next2:
out dx, al ; output green
xor al, 0x09 ; => XOR the green with some constant
out dx, al ; output blue
pop ax
sub ax, 9
and ax, 0x3F
push ax
push ax
inc bp
loop color
Resulting in this :
Combining the two scenes
The first scene (the white/red one) used 171 bytes after an optimization pass, but I also liked the second scene, so I searched a way to combine the two scenes with a smooth color transition between both.
There was multiple issues though :
- the second scene used a different palette (obviously),
- the ‘wave’ diagonal bar moved in the inverse direction : bottom-right corner to the top-left one,
- the ‘bubbles’ creating the water effect also moved in the inverse direction,
- the ‘wave’ diagonal bar was thinner than the other scene.
I knew I could not fix all these : abruptly changing the direction of the wave would create a weird effect.
Instead I focused on the colors, put the two palettes code side-by-side and tried to switch between them gradually.
After many trials and errors, I finally found a way :
The transition created new interesting color combinations, I adjusted its speed to be a little longer (the maximum time for the 256b intros at Revision is ~1 minute).
Then I adjusted the ‘wave’ bar so the green/blue starts thinner, then widen over time :
I could not find a way to alternate the scrolling from bottom->top to top->bottom, so I kept the top->bottom one, that looked better for the white/red palette. So the blue/green effect at the start scrolls “backwards” compared to the stand-alone version.
At ~215 bytes, the graphics were good enough, but again there was one problem left … no sound :)
Music
Adding sound was as painful as the previous year : not much time left, not much bytes left for an interesting pattern.
However I had a nice visual transition this time, so I decided to try and reuse the same registers both for the graphics and the sound.
Much like my previous intro, I stored the MIDI commands at the end of the code :
music:
db 0x3F ; UART mode
db 0x99 ; NOTE ON on channel 9
db 44 ; note
db 80 ; volume
db 0xc0 ; change instrument on channel 0
db 101 ; instrument
db 0x90 ; NOTE ON on channel 0
db 35 ; note
db 80 ; volume
Then updated the overall volume every frame, with the same register used to progressively change the colors (i.e. adding more red) :
; progressively augment drum volume
mov [si - 6], byte bl
; brighten red
mov al, 64
sub al, bl ; 64 -> 0
mov [brighten_red + 2], byte al
; go_green:
shr bl, 5 ; brighten every 256 frames
mov [darken_green + 2], byte bl
mov cx, 0xFF ; reset loop for palette generation
jmp palette
- SI points somewhere in the MIDI commands (SI +/- an offset can be cheaper than using a label)
- “brighten_red” and “darken_green” are two labels in the palette code, controlling the amount of colors added,
- I use the time counter BL both for increasing the volume, and progressively changing the colors.
Also notice how I need to regenerate the whole palette on each frame, which obviously consume a fair amount of CPU.
Finding notes
I used two MIDI channels, as usual Channel 0 and Channel 9 (drums).
I couldn’t quickly find a satisfying bass pattern, so instead I used a MIDI “sound effect” instrument that looped indefinitely once triggered.
Since a note is played on both channels every frame, the trick was simply to mute the volume on channel 0 on first frame to avoid ‘stacking’ the notes.
For drums, I already had a progressively increasing volume, all I needed was an interesting pattern.
This time I experimented with logical ANDs base on the frame counter to trigger either a snare, a kick, or silence :
mov bx, bp ; copy the time counter stored in BP into BX
cmp bx, 256
jle kick ; don't start the snare until the 256th frame
mov al, 40 ; snare is MIDI note 40
test bx, 0x0F ; play snare every time the frame counter AND 0x0F = 0, i.e. every 16 frames
jz drum_note
kick:
mov al, 36 ; kick is MIDI note 36
test bx, 0x03 ; play kick every 4 frames
jz drum_note
salc ; xor al, al : set the MIDI note to 0 (silence)
drum_note:
mov [si + 1], al ; update drum note directly in the MIDI commands
; play all 8 MIDI commands
mov cl, 8
rep outsb
I tried probably close to a hundred snare/kick patterns by hand, until finally deciding on the final one hours before the deadline.
Still, this technique was simpler and cheaper than the multiple branchs approach I tried with Mana Fortress, just more tedious to code with no better way than listening to each pattern one by one.
I noticed the MIDI notes sounded far better on the Revision compo machine than on my PCs, which was a nice bonus. These drums definitely screams metal and the original Doom to me :)
Last minute optimization
As usual the intro still weighted more than 256 bytes hours before the deadline ; this time one of the tricks that saved it was related to the symmetry computations :
mov cl, 7
sub cl, bl
mov bl, cl ; bl = 7 - bl
This code is similar to the one used in Mana Fortress : we want to replace a series of indices like this : 0 1 2 3 4 5 6 7 to the symmetric : 7 6 5 4 3 2 1 0
Turns out we can use NEG instead, saving one whole byte and a register :
neg bl
add bl, 7
This effectively compute -X + 7, instead of 7 - X.
This NEG use, combined with other tricks reduced the intro size to a satisfying 253 bytes.
I named it “Inner Rage” since I liked the contrast between the calm waves at the start transitionnig to the “boiling blood” effect.
Last minute glitch
After merging the two scenes, I found out the XOR’ed blue of the first scene created some subtle yellow accents once in the white/red scene, so I decided to keep it.
What I didn’t found out until the end of development was that after about 50 seconds of running time, the palette computation overflowed, the white/red gradient disappeared, replaced by completely glitched colors that kept changing until being stuck on the old teal version (?).
I didn’t have time to investigate exactly why that happened, and the effect was cool and unexpected, so of course I kept it that way and pretended it was planned.
The final result :
Conclusion
Overall I’m much more satisfied with how this intro turned out compared to last year, even though I had some luck finding the ‘bubble’ pattern early on and didn’t follow the original plan.
I really liked the visuals this time, and could not find huge inefficiencies in the code, so I consider it my best intro yet at least for the technical side.
Greetings
Greetings to :
- Gub for helping with intro-saving optimizations at the last minute (as usual)
- Coralune for seeing way too many drafts of these effects without being bored to death
- all the people that didn’t quite know what’s a “256 bytes intro”, but still gave useful feedback and support anyway
- sizecoding.org and all its helpful tricks when it’s past midnight and you still can’t see how to save those pesky bytes
- Ctrl-Alt-Test, Razor1911, all sizecoders











