rss feed of the blog sol stuff discord follow sol email sol Sol::Breakdown

Litterae Finis Breakdown Writeup

I promised myself that I'd write a breakdown of Litterae Finis if it won, so I suppose I should keep my promise.


Back in 1996, I was IRCing from my summer intern place, and talked about text mode demos with a bunch of folks. Basically, we agreed that they are cool, and there should be more of them. There's two ways of getting more text mode demos: make them yourself, or make other people make them. So TMDC, or Text Mode Demo Compo (or Contest, or Competition) was born.

I organized TMDC for over a decade, writing invitation demos most of the time, but never entered, because being an organizer and a competitor at the same time doesn't quite mix.

After TMDCX I figured that's it, compiled a CD image of all TMDC demos to date and said it's over in rather many words. To my surprise, the Northern Dragons took over TMDC, with my blessing (and bunch of my resources) and freed me to actually enter the contest!

So I did. With a bunch of new tricks. I might write a breakdown of "Turing Machines" if there's interest...

Now then, a few years later, I figured I should do "just one more" text mode demo, and called the project Litterae Finis.

You can watch the demo on YouTube.

Base Technology

After "Turing Machines", which was written with TextFX6, I got inspired to improve the filters and wrote TextFX7.

The basic idea of graphics to text mode conversion filters is to take a bunch of pixels and try to figure out which glyph matches the pixels best. This is part science, part art, part luck; you can read more about my approach here, but the basic idea is, render to 160x100 and convert to ascii.

So apologies to those disillusioned by this, but yes, everything in the demo goes through TextFX7. (In one case, twice).

In "Turing Machines" I had used some software 3d engine, but using it felt so flaky and error-prone that I didn't feel like touching it again. Instead, I found TinyGL, a software-based subset of OpenGL which already did most of the tricky things I wanted: perspective corrected texture mapping and z-buffer, plus an OpenGL 1.x instruction subset.

I spent a lot of time reworking the code, including little features like non-square textures, mipmaps (with miplevel selection per 4 pixels or so), direct pointer access to texture data, to mention a few. I dubbed this reworked version TextGL, and may clean it up and release it one day if there's interest.


I had lots of ideas I'd love to do in text mode, including some old-school effects, color toning, bloom, cool transition effects, etc, most of which didn't make it mostly because I didn't have the energy to build the tech ahead of time.

After getting music from !Cube I first listened through it with my eyes closed and let my imagination flow - spacey things, fast things, moody things.

Then I loaded the tune up in audacity and wrote down the principal timings, and ended up with 13 values, with some kind of idea of the structure; basically we have an intro, couple separate uplifting bits and then a longish darker ending sequence with a fadeout ending.

Then I started filling the demo out. The parts weren't written exactly in the order presented; for instance, fades were added later on, and all of the parts went through several tweaking passes before their current state.


Watching the demo before reading this will probably make more sense, as the screenshots are no substitute to the animation when trying to make out what's going on the screen =).

The intro tile fades were a relatively late addition. I already had the audio sync for the pulses, but starting off from a star field didn't feel right, even if it was pulsing to the rythm, so I figured a more or less random tile thing would work. The tiles blink a bit longer than absolutely necessary, fading out towards the end.

The background star field here is actually a strange attractor fractal. In addition there's a star field effect flying past the camera where the stars are recycled when they are far enough. Since we're always looking at the origin of the world, it looks like stuff is flying to the center of the world. This was mostly so because starfields always seem to fly towards the camera for some reason..

The font used in the credits is Hershey 'scripts'; I had written a Hershey to vertex data converter earlier. Since the vector data follows the writing direction (and having a line primitive in TinyGL and TextGL), having it scribble on the screen was relatively simple, as was getting the text to fly out towards the world origin.

Here the whole attractor is on screen. I divided the whole bunch into eight segments (simply (+x/-x), (+y/-y), (+z/-z)) and pulsed these in rythm to the music. I considered different kinds of subdivisions, but simple things tend to work well.

The attractor changes into hand I modelled in blender (whee, my first blender modelling in at least four years - the new interface is much easier to work with). I actually considered this a placeholder and made a few replacements from 3d-scans of skeletons and actual human hands, but those looked uglier, so I reverted to this.

I also wrote a few different morphing codes, but they all looked too distracting, so a simple horizontal sweep was used instead.

I briefly considered separating the fingers and pulsing them instead of the eight segments, but didn't try it out.

The demo name is drawn again with the Hershey font.

Funny thing about the Hershey fonts is that they are basically public domain, unless in some specific format; Hershey wrote in the documents that nobody should use the format because it's so wasteful. So here I am, with the data converted into tables for easy OpenGL usage, growing the data size by a couple orders of magnitude..

Now we zoom into the world origin, with a ball of bazillion points and a few lines extruding from it. Certain percentage of the lines is replaced with new ones on each tune pulse, and the lines get longer, and so on and so forth.. lots of things you don't actually notice (or have any way of seeing due to the low resolution).

Or well, you used to be able to see the bazillion points, but the fade to white masks them. Note that the fade is not a direct fade to white but a circular one; all of the fades in the demo are some way tweaked to be nicer than your typical linear fade..

One of the effects I wanted to do was one of those infinite zoom-into-picture-pixels ones where each pixel is another image.. with each pixel being a character. Zooming into @ or space would have been rather boring, so this is what you got instead.

The plasma is run through TextFX and then rendered as a bitmap, scaled, and cross-faded with the plasma at certain zoom levels. I find it sort of funny reminder that yes, this is text mode.

While the "quine-zoomer" (as Gargaj named it on Pouet) is neat and all, I had to add another effect on top of it or it would have been too boring for at least some of the potential viewers. One of the old-school effects I wanted to do was these fun effects where you render a few frames in off screen buffers and then use previous frames for rubber effect.

Yes, the logo twister is actually pre-rendered at start-up; 180 frames, from which vertical slices are rendered on screen. Add a simple shadow effect, some transitions and we're done.

Remember all that talk about perspective-correct texture mapping etc? This is the only use of mipmapped textures in the whole demo. And it's a pre-render. So life goes.

The next part of the music required something with speed. Personally, this is the weakest part of the demo; I made a tunnel effect out of random triangles, and then tried to make it feel more dynamic by adding rotations and so on, but it didn't quite.. flow. I also hate to admit that yes, I used greetings as content here.

As a whole, it kinda works.

A little implementation detail: the background stars rotate at a different pace than the tunnel; when I had their rotations locked, it just looked bad.

Something like a flyby of a city on the sphere (see below) might have worked better. Maybe. And might also have added more implementation complexity, and if it hadn't worked, well..

We're back to the strange attractor star field. Syncing the rotations with the tunnel was somewhat painful, as the whole demo just uses random sin/cos movements for camera =)

Hey look, I found a use for my sphere mesh generation code! The texture on the sphere is made of total placeholder colors, but it worked so well, I left it there. Since we're dealing with vector graphics, I wanted a "WE HAVE A HUGE THING HERE" moment in, and here it is.

As we move away from the sphere, the cubes that orbit the sphere come into view. There's actually three step orbitals (sphere is orbited by 8 cubes that are orbited by cubes that are orbited by cubes). The cubes are more or less direct from my TextGL test scene, with a plasma on a few sides and last frame as texture on others for a "mirror tunnel" effect.

All of the textures in the cubes are generated each frame, and there are no miplevels.

Getting the orbits to work so that we see a cube or two close by (but don't fly through one) took a lot of trial and error. To avoid these issues, the cubes don't actually get rendered before we're far away enough from the sphere.

The shock wave particles are a very late addition. I started to be totally sick and tired of all the tweaking by then, and had less than 24 hours to the deadline.

!Cube demanded I add something to those sound cues, and he was totally right; the demo would not be nearly as good without them.

Another picture of the shock wave. The wave is actually a flattened random dot sphere that is scaled up at an exponential rate.

At this point lots of dots are flying to our sphere, and the sphere is clearly no longer a sphere. Random triangles are also flying in, and the orbiting cubes are flying in too. My basic vision here was that everything we've seen so far is flying in.

Making it look like the end of the world had the negative side of making it a bit too chaotic in some viewer's eyes.

At a specific sound cue, the sphere is scaled up and down to show that it's going through some rather heavy deformations. These are actually random sin/cos deformations along vertex normals, and they are steadily increased through the part until and over the threshold where the sphere is partially inside out. This might probably look pretty bad if you could tell what the heck was going on, but text mode masks it pretty well.

As a little thing, I could have made it so that everything inside the grown area disappears, but that would have been quite bit of work due to the chaotic way the whole demo works, and probably would have been rather unnoticeable.

Finally, we start cranking the deformation up so that the sphere takes the whole screen. The sphere is not scaled up - just the deformations are.

No more orbitals, no more random triangles flying in..

Even the strange attractor star field is sucked in near the end, but drawn so that the sphere is rendered on top of the stars, as otherwise it just looked bad.

Another fade to white..

and a fun "sliding" fade to the end screen.

And we're done.

No Color?

As I mentioned, I had thought about color grading - one of the problems with text mode "graphics" is the very limited color space, so rendering in 32bit color and tuning the colors with a 3d look-up table would be a nice way of doing it.

I could go and say something high-brow about artistic direction and the purity of ASCII art, but like many other design decisions, this one was at least partially accidental. I hate the way TextFX7's color ASCII art works (or doesn't), and fixing it would have required plenty of research (which I may or may not end up doing). The pure ASCII art filter was also much, much faster, at least in debug mode.

So I started without color, figuring I could add it later. And never did. The end result is still pretty nice, even though several people (including the TMDC judges) wondered about the lack of color.

End of Letters

So there you have it. I hope you enjoyed this little tour behind the scenes.

Will this be the last textmode demo I make?

I guess time will tell.


As always, comments are appreciated.

Site design & Copyright © 2022 Jari Komppa
Possibly modified around: January 28 2015