Published: 15. 11. 2019   Category: GNU/Linux

Linux demoscene

Demoscene is a computer art subculture on the edge of creative art and underground, which is created and presented on computers.

A demo is a program, which demonstrates the abilities of computers and also a programmer. They are created for all possible platforms, which can display information and play sound. The demoscene has developed in the 80s when cracker was removing the copy protection of games and they were adding some of their creations to a pirate distribution, FILE_ID.DIZ and *.NFO files. They added small programs with logos, scrolling text with greetings and some music, FILE_ID.DIZ a *.NFO contained information decorated with rich ASCII art.


Video: DOS demo from 1993 Second Reality from Finnish group Future Crew is one of the most famous demos in the history of the demoscene.

By the time, the demoscene developed into an independent IT discipline where one person or several people (demogroup) creating a product to show to the community at demo-party/demo-compo. Criteria for rating are music, graphics, and code. There are several constraints for each category given by a platform or some rules, e.g. size of executable binary, number of colors in the palette and many others.

The best demos must be original, never seen before and must show something unbelievable or at least funny. A specialist is amazed: is it really possible to generate tunnel effect and music in 256 bytes!? How 8086 without co-processor can generate such a 3D effect!? Polyphone music generated by a reading head of floppy drive?! It is not possible!

And laic is asking: and what? Then he does not understand it and he cannot value demoscene products.

What demoscene categories are closely related to Linux?

ASCII Art

It is the oldest type of modern digital art (I have emphasized modern because if you will go deep in history, you will find power looms programmable via punch cards invented at the end of the 1800s century by J. M. Jacquard). ASCII Art was developed in times when users accessed computers by teletypes and videoterminals. Creations are assembled from alphanumeric characters of 7bit ASCII norm where the first 31 codes are control bytes like newline, tabulator or bell. And codes 32 (space) to 127 belong to the English alphabet, numbers, and symbols.

Generate all ASCII character in the shell:

for i in {32..127}; do printf \\$(printf "%o" $i); done

Very first ASCII Art was created during the 60s on teletype machines, but at that time it was called RTTY Art, from the abbreviation of Radio Teletype. This RTTY Art used a 5bit International Telegraphic Alphabet only displaying capital letters, digits and few symbols.

The scan bellow from a ham radio magazine shows miss Deborah in the column called RTTY Artwork of the month:



Fig.: Digital version (705  kB)

This artwork has 10452 symbols and with usual speed 45 Bauds of ham radio RTTY transmission, the image is transferred during almost 4 minutes.

There is no problem with viewing ASCII Art in GNU/Linux any problem, you will need commands likes cat, less and more in your favorite terminal emulator or console. The used font must be non-proportional where characters, including space, have the same width.

More reading

ANSI Art

Colorful text with a richer set of characters is an ANSI Art. Its greatest spread started in times of phone BBS and MS-DOS (you needed to waste a little bit of memory with ANSI.SYS driver). ANSI contains escape-sequences that define the color of character, background, blinking or underline and bold text and many more. The original charset on IBM PC uses an 8bit code, which uses ASCII code for the first 127 characters and the rest contains mathematical symbols and semigraphic symbols. The code is defined as CP-437.

Regular Linux terminal can ANSI sequences interpret without any problem, so thanks to it we have colors in our text user interface. The difficult part is good old CP-437 encoding, but it can be converted to Unicode to display it correctly.

CP-437 charset displayed as UTF-8. If you see a square instead of some character your font probably missing given symbol:

for i in {32..255};do ((i%16==0))&&echo;printf "\\$(printf "%o" $i) ";done|iconv -f cp437 -t utf8

You can also display ANSI Art which is already in UTF-8:

curl -L http://git.io/unix

This command will display Denis Ritchie and the ANSI was converted to HTML by ansi2html.sh.


                     ,_ ,_==▄▂
                  ,  ▂▃▄▄▅▅▅¾.            /    /
                   "»▓▓%\       / /   / /
                 ,7"     ´>▓▓%   /  / > / >/%
                        ,»▓▓¾´  /> %/%// /  /
                  ▅▅▅▃,,▅▅Æ\// ///>// />/   /
                 V«¼.;<«.,`=// />//%/% / /
               //╠<´ -²,)(▓~"-╝/¾/ %/>/ />
           / / / % -./▄▃▄, /7//;//% / /
           / ////` %zWv xX//&;% / /
       / / / %//%/¾½´▃▄▄▄▄▃▃\/& /
         </ /</%//`!%%WY<Y)y&/`\
     / / %/%//</%//\i7; ╠N>)VY>7;  \_    UNIX IS VERY SIMPLE IT JUST NEEDS A
  /   /</ //<///<_/%\  V%W%£)XY  _/%‾\_,   GENIUS TO UNDERSTAND ITS SIMPLICITY
   / / //%/_,=--^/%/%%\¾%%%}    /%%%%%%;\,
    %/< /_/ %%%%%;X%%\%%;,     _/%%%;,     \
   / / %%%%%%;,    \%%l%%;// _/%;, dmr
 /    %%%;,         <;\-=-/ /
     ;,                l

If you want to display ANSI graphics encoded in CP-437, use command iconv -f cp437 -t utf8 and if the graphics have more lines then your terminal window, send it via the pipe to less -r (the option -r tells less to leave the interpretation of escape sequences to a terminal). Beware, some of the old images expected that your terminal window has exactly 80 columns, so an author did not care about new line characters. Maybe you know that you can tell to your terminal how many columns to use by stty cols 80 and you also need to set a flag setterm --linewrap on, but this variant does not work as expected, so the best way is to resize a terminal window manually (or by command/escape-sequence echo -ne "\033[8;32;80t" for the 80×32 size).

Some BBS screen uses addressing of line/column position and because people were connected by slow speed 1200 Bd, they were able to see simple animation during screen data download.

Few examples:

curl -L http://artscene.textfiles.com/ansi/scene/am-ice.ans | iconv -f cp437 -t utf-8
curl -L http://artscene.textfiles.com/ansi/scene/bigtime3.ans | iconv -f cp437 -t utf-8
curl -L http://artscene.textfiles.com/ansi/scene/fconfigs.ans | iconv -f cp437 -t utf-8

More complex example with terminal window resize and with removing background attribute at the end of the line:

# Set size 80x32
echo -ne "\033[8;32;80t" 
# Download and save the file
curl -L http://web.archive.org/web/20140407205152/http://sixteencolors.net/pack/blocktronics_blockalypse/rad-LOVE.ANS/download > rad-LOVE.ANS
# Show the file and replace line ends with background reset:
iconv -f cp437 -t utf8 rad-LOVE.ANS | sed -e "s/$/\x1b[49m/"

The following script shows 7 basic colors displayed by „░▒▓█“ characters. These chars are usually used with different background colors to simulate more colors:

for i in {0..7}
do
        for j in 91 92 93 88
        do
                echo -en "\E[3${i}m\u25$j"
        done
done

There is also a very nasty sequence that will brake your terminal. It is printf "\e(0", it is turning on display of semigraphics. Maybe you already discovered it by accident, when you displayed a binary file on the terminal and there is a high probability for having sequence 0x1b 0x28 0x30 in a huge binary file. In this case use command reset or tput sgr0 to reset the terminal flags. Usually, you will need to type these commands by blind or find a reset terminal emulator function in GUI.

Example of ANSI Art Animation

ANIMATION=https://16colo.rs/pack/acdu1092/raw/BRTRACD2.ANS
SPEED='.005' # wait this time each line

# 1. Disable cursor
printf "\e[?25l"

# 2. Download, convert, display slow == play animation
curl --silent $ANIMATION | \
    iconv -f cp437 -t utf-8  | \
    awk "{system(\"sleep $SPEED\");print}"

# 3. Enable cursor settings
printf "\e[?12l\e[?25h"

More reading

Demos

The biggest web portal for demoscene is pouët.net and it has about 90 platforms listed (my favorites are ZX Spectrum, MS-DOS, Linux) and 36 categories. IMHO, most important are 256 Bytes, 1k, 4k, 64k, and demo. But the size of the executable starts at 32 Bytes.

GNU/Linux has a disadvantage in the size of the executable file. In MS-DOS you can just say to compiler put instruction from 100h offset and go on! The switching to a graphics mode is done by few instructions calling BIOS services and then you need just know a lot of tricks and instruction lengths.

Binary executable in Linux must be in ELF format (Executable and Linkable Format) and this format has many data, headers, architecture information, compiler identification and a lot of useless information :) Also operation system watches the access to its resources so the direct access to hardware or memory is provided by Linux kernel, so you need to run system calls or high-level libraries to execute some operation.

There is article A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux describing how author was reducing an executable of minimal C source code: int main(void) { return 42; }, it had 3998 Bytes after compilation and then he was trying several compiler parameters, rewrite code to assembler and then he created own ELF header with just minimal necessary data and final binary has only 45 Bytes!

There is a tutorial: 4k Intros in Linux with tricks for the creation of really small binary executables and there is a set of helping utilities ELFKickers (GitHub) for changing of ELF files and removing unnecessary data added by gcc and much more.

So there is no space for program instruction itself, so there are only 3 demos in 128 Bytes category on Pouët.

The situation in the 256B category is not better. Demo AT LEAST IT AIN'T NO XOR TEXTURE? (NoXor), is a shell script which will unpack binary data with a C source code, it will send them to the compiler and run the output ELF binary (this binary has about 8 kilobytes). In comparison with previous creation, it at least does not need any crazy research around :)

This is a quite common trick even in larger categories. The demo contains a C source which will use graphic mode provided by SDL or GLUT library. Almost all Linux distributions have these libraries and because demo making is a domain of programmers, they usually have also header files needed for compilation. Source code itself looks like an entry from The International Obfuscated C Code Contest and if the size must be reduced, it is done by self-extracting gzip or bzip2 script (a shell script which contains binary data from some position and it will unpack itself and send it to the compiler). Because nowadays interpreted languages are quite fast, there are several demos written in them.

It could be quite lame for old-school demo consumers and programs, but for them could be also lame using OpenGL functions, shaders or other library functions, which do a lot of work instead of programmer :)

Authors of production in bigger demo categories could add more graphics, music, complex 3D objects and textures. They can use multiplatform libraries and demos then run not only on Linux. But for smaller categories, programmers must use just clever algorithms, run-time texture generation and procedural generation of textures, object position and also a procedural generation of music and sound. A lot of these algorithms are not original anymore, e.g. XOR texture (my implementation in bash):


for((y=0;y<$[LINES-1];y++));do for((x=0;x<=$COLUMNS;x++));do 
printf "\e[${y};${x}f\e[38;5;$[232+(x^y)%24]m\u2588";done;done

This version uses additional ANSI sequences for 256 colors \e[38;5;XXXm and it must be copy&pasted to shell because $COLUMNS and $LINES are not set when the command is invoked from inside a script file.

Following version gets terminal size with tput command and only basic ANSI colors are used:


clear
W=$(tput cols)
H=$(tput lines)
for((i = 0;;i++)) do
  for((y = 0; y < $[H-1]; y++)) 
  do
    c=$[31+i%7]
    for((x = 0; x < $W; x++))
    do
      (( (x ^ y) == $i )) && echo -en "\e[${y};${x}f\e[${c}mX"
    done
  done
  sleep 0.25
done

More reading

Sound generation

Similar to procedural texture generation also music and sounds can be generated algorithmically. I am admitting that I did not think about music generated by algorithms, I thought that demo contains data samples, pushing them to a sound card and there is no necessity to build a big science around.

The following this C source code (song.c) from finnish demo programmer viznut proofs that I was completely wrong:

#include <math.h>
main(v,i,z,n,u,t){for(v=-1;;)for(n=pow(1.06,"`cW`g[`cgcg[eYcb^bV^eW^be^bVecb^"[++v&31]+(v&64)/21),
i=999;i;putchar(128+((8191&u)>i?0:i/8)-((8191&(z+=n))*i-->>16)))u+=v&8?t/2:(t=v&64?t:n/4);}

Download source code and compile it with command: gcc -lm -o song song.c. There are several options how to play the output, because sound card by default accepts unsigned 8bit (u8), 8000 Hz data.

How the source code above works is explained in a presentation called Making music with a C compiler (SIGINT13). The basic trick uses an infinite cycle of binary data 0, 1, 2,… 255 send to sound card input. It is a sawtooth signal with frequency 8000/256 = 31.25 Hz. If you do not want to compile C source codes from the presentation, use following one-liners for Python 3:

export PYTHONIOENCODING=latin1 
python3 -c "while 1:locals().setdefault('t',0);print(chr(t%255),end='');t+=1;" | aplay

I thought that I will do it in bash directly:

for((t=0;;t++)); do printf \\$(printf "%o" $[t % 255] ) ; done | aplay

But bash is not able to generate data to stdout in enough speed and smoothness, because data are stored in a buffer first and then they are sent to pipe and buffer must be filled again. There is also command stdbuf, but it does not help me to change a buffer size. If you want still to use bash, save data to the file first and then send the file to aplay.

I have used my second favorite programming language instead. But also, in this case, I have found that it will not be completely straightforward. I wanted to implement it as Python one-liner and a local variable must be defined with locals().setdefault('t',0). Then I was listening to the signal and realized that it does not sound in a similar way as from C's putchar. I have realized after data hex dump comparison that print() will convert 8bit byte from chr() from latin1 to utf-8, so there are a lot of unwanted data. You need to set an environment variable export PYTHONIOENCODING=latin1 to disable this default conversion.

Operations like t*2 or t<<1 doubles signal frequency or you can modulate signal by logical conjunction with 42 (101010) and combine binary operation AND, OR and shifts with mathematical operations.

python3 -c "while 1:locals().setdefault('t',0);print(chr(t & 42 ),end='');t+=1;" | aplay

The „melody“ is created by combination of binary operations and some of them sound quite nice, but with such retro-electronic flavour and also you need to listen quite long time sometimes to hear all surprising riffs discovered in later periods of signal generation :

export PYTHONIOENCODING=latin1

python3 -c "while 1:locals().setdefault('t',0);print(chr((t*((t>>12|t>>8)&63&t>>4))%255),end='');t+=1;"|aplay 

python3 -c "while 1:locals().setdefault('t',0);print(chr(((t*9&t>>4)|(t*5&t>>7)|(t*3&t//0x400)-1)%255),end='');t+=1;"|aplay

python3 -c "while 1:locals().setdefault('t',0);print(chr(((t//10**7*t*t+t)%127|t>>4|t>>5|t%127+(t>>16)|t)%255),end='');t+=1"|aplay 

python3 -c "while 1:locals().setdefault('t',0);print(chr(()%255),end='');t+=1"|aplay 

python3 -c "while 1:locals().setdefault('t',0);print(chr((((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t<<6))%255),end='');t+=1"|aplay

python3 -c "while 1:locals().setdefault('t',0);print(chr(((t*(t>>5|t>>8))>>(t>>16))&255),end='');t+=1"|aplay

python3 -c "while 1:locals().setdefault('t',0);print(chr(((((t>>12)^(t>>12)-2)%11*t//4)&255)),end='');t+=1"|aplay -r 44100

There is a byte array '36364689' changing melody's octaves, the sample rate here is 44.1 kHz):

python3 -c "while 1:locals().setdefault('t',0);print(chr((((t*((ord('36364689'[t>>13&7])&15)))
//12&128)+(((t>>12)^(t>>12)-2)%11*t//4|t>>13)&127)),end='');t+=1"|aplay -r 44100

Are you fascinated like me by procedural generation of music now? Browse through following links, you will find a little bit of theory, some already generated data (even with visualization) and a lot of other examples from several authors (I used these examples for Python3 one-liners above).

More information