fdraw

reinhard@finalmedia.de cafeface
Mon May 25 01:42:41 PM CEST 2026
Public Domain

  • readme.txt
  • fdraw.c Quellcode 0c214caebd7ac6af2de7eb82cf2ffd14
  • fdraw (amd64 elf64 binary) 035a8d09c3561f311d5f8f29dc46c383
  • fdrawterm.c Quellcode 8d10abc3d3cd1cbc329b89543f8a674a
  • fdrawterm (amd64 elf64 binary) 7f6166ee939190d1cb76eb9ad1975921090add065e35c2cc8c0e03a079b40877
  • jsplay.tgz Demo Tarball mit Javascript Code als Framebuffer Video Display. Nutzbar im Browser (2D Canvas,WebGL,WebGPU). Stellt komprimierte gestreamte fdraw frames dar. streamed in einer unix pipe via websocketd aus der shell mittels awk und fdraw und pigz zum komprimieren live an jeden modernen browser als display. (Setzt im backend dann die fdraw binary voraus, z.B. unter /usr/bin/fdraw. natürlich auch die selbst compilierte). Anmerkung: Die Variante fdraw befehle zeilenweise an Javascript durchzugeben (statt bereits die fertige rgba Frames) wurde ebenso evaluiert, ist jedoch nicht performant genug in standard Javaascript beim zeilenweisen lesen und parsen der strings. Zudem ist dann das Datenaufkommen für einen webstream auch sehr hoch. Diese Variante streamed sehr sparsam.
  • fdrawterm.tgz liest fdraw befehle von stdin mit sehr schneller Ausgabe von ANSI Sequenzen im Terminal auf stdout, statt rgba frames. Sehr praktisch zur Vorschau im Terminal.
  • fvoxel Weiteres Tool, um ein Voxelterrain zu erzeugen und zu durchfliegen. Steuerbar und animierbar. Auch das Voxelterrain ist zeilenbasiert animierbar. Liest separate fvoxel Befehle von stdin und erzeugt fdraw Befehle auf stdout.

    Referenz

    Befehle für fdraw sind linebased ascii char. dezimal werte.
    fdraw erwartet diese Form der Eingabe auf stdin.
    
    Das erste Zeichen einer Zeile ist immer der befehl.
    Dann folgen die Argumente
    
    
    cmd     argumente       name    description
    -----------------------------------------------------------------------------------
    c       r g b           color   Setzt die Zeichenfarbe nach R G B
    m       x y             move    Bewegt den Zeichencursor zu gewünschten Koordinaten
    r       dx dy           rect    Zeichnet ein Rechteck
    p       x y r g b       pixel   Zeichnet exakt einen Pixel an x y in Farbe r g b
    s       n               sleep   Wartet angegebene Anzahl an Microsekunden
    o       ox oy           offset  Definiert globalen Offset für Zeichenoperationen
    
    Befehle und Parameter/Argumente werden durch ein Leerzeichen /space) getrennt.
    Alle Argumente MÜSSEN postive Ganzzahlen (integer) sein.
    

    Warum der Umweg über die fdraw Befehle?

    Das Prinzip der Unix-Philosophie besagt, dass Datenströme zeilenbasiert und in ASCII (menschenlesbar) übertragen werden sollten, weil Text die universellste und flexibelste Schnittstelle zwischen Programmen ist.

    Durch den zeilenbasierten Austausch über reine ASCII Texte gewinnt man diese Vorteile:

  • Universelle Kompatibilität: Jedes Werkzeug kann Text ohne spezielle Bibliotheken lesen und schreiben.
  • Menschenlesbarkeit: Fehler in Datenströmen lassen sich direkt im Terminal ohne Hex-Editor aufspüren.
  • Einfache Werkzeuge: Programme wie grep, awk oder sed arbeiten nativ mit Zeilenumbrüchen.
  • Zukunftssicherheit: Textformate überdauern proprietäre oder hardwareabhängige Binärformate.
  • Nicht zu vergessen: Das Prinzip wurde weltberühmt durch Doug McIlroy, den Erfinder der UNIX-Pipe.

    Er formulierte das Konzept 1978 im Bell System Technical Journal:

        "Expect the output of every program to become the input to another, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input."
    

    Die wichtigste Ergänzung dazu fasste Peter H. Salus 1994 als Philosophie in seinem Buch A Quarter Century of UNIX zusammen. Er formulierte die goldenen Regel:

        "Write programs to handle text streams, because that is a universal interface." (Schreibe Programme so, dass sie Textströme verarbeiten, denn das ist eine universelle Schnittstelle.)
    

    Die Intention dahinter ist recht einfach nachvollziehbar. Die Entwickler wollten Modularität statt monolithischer Software.

    Wenn ein Programm seine Daten in einem komplexen Binärformat ausgibt, bricht die Kette. Ein binäres Format zwingt das nachfolgende Programm, die genaue Datenstruktur des Vorgängers zu kennen. Standardisierter ASCII-Text entkoppelt hingegen die Programme einer Pipe.

    Das nächste Tool muss lediglich wissen, wie man Zeilen trennt. Alles bleibt menschlesbar und die Kette kann an jeder Stelle auseinandergenommen und geprüft werden. Zudem ist es dann ein Leichtes, auch zeilenbasierte Filter zu schreiben, um Ausgaben umzustruktieren, zu wandeln, zu extrahieren, wegzulassen oder aufzutrennen. Wenn du einmal die Magie dahinter verstanden hast, lässt sich das nicht mehr los. Du stellst dir dann eher die umgekehrte Frage: Warum sollte man das nicht so tun?

    fdraw Beispielbefehle

    Hier zum Verständnis

    Beispiele:
    
    c 140 20 0              Wechselt zur Farbe rot
    m 20 80                 Wechselt zu den Koordinaten x=20 und y=80
    r 10 10                 Zeichnet ein Quadrat 10x10 px
    r 10 40                 Zeichnet ein Rechteck 10px breit und 40px hoch
    s 50000                 Wartet 50 Millisekunden (Animationen)
    p 23 42 164 163 255     Zeichnet einen hellblauen Pixel an x=23 y=42
    o 50 100                Definiert einen globalen Offset von x=50 y=100
    
    
    Gerade das Definieren eines globalen offsets ermöglicht das verschieben
    eine frames, ohne dessen eigene steuersequenzen verändern zu müssen.
    
    Siehe daher auch die Tools charforce, intrange etc. auf https://finalmedia.de/code/
    die wunderbar in diese Kette passen.
    

    Hinweis: Für die nachfolgenden awk Demos wird ein awk mit math modul benötigt, z.B. gawk. einfaches busybox awk reicht nicht, solange nicht math support aktiviert ist.

    Ob in deinem awk diese mathematischen Funktionen (wie sqrt, sin, log) verfügbar sind, hängtdavon ab, wie die jeweilige Linux-Distribution die BusyBox-Binärdatei kompiliert hat. Die Option muss aktiviert sein: Option CONFIG_FEATURE_AWK_MATH, und dann wird auch die libm mitgeladen. Mit Math-Support ist die binary geringfügig größer, bietet dafür aber alle POSIX-Standard-Mathefunktionen.

    Animated Demos

  • barnsley.awk
  • barnsley.sh
  • Animated Torus Rotation

  • torus.awk
  • torus.sh
  • Example Pipeline to mp4 file

     export SCREEN_FPS=30
     export SCREEN_HEIGHT=240
     export SCREEN_WIDTH=320
     gawk -v width=320 -v height=240 -f torus.awk | \
     fdraw 2>/dev/null | \
     ffmpeg -f rawvideo -pixel_format rgba -video_size 320x240 -framerate 30 -i - -frames 300 -y torus.mp4 2>/dev/null
    

    Starfield Animation

  • starfield.awk
  • Raytracer Demo

  • raytracer.awk
  • rayconst.awk
  • raytracer.sh

  •  export SCREEN_FPS=30
     export SCREEN_HEIGHT=240
     export SCREEN_WIDTH=320
     gawk -v width=320 -v height=240 -f raytracer.awk | fdraw 2>/dev/null | ffmpeg -f rawvideo -pixel_format rgba -video_size 320x240 -framerate 60 -i - -y raytracer%03d.png
     gawk -v width=320 -v height=240 -f raytracer.awk | fdraw 2>/dev/null | ffmpeg -f rawvideo -pixel_format rgba -video_size 320x240 -framerate 60 -i - -y raytracer.mp4
    

    Voxel Renderer Animation

  • voxel.awk
  • zendless.awk
  •  export SCREEN_FPS=30
     export SCREEN_HEIGHT=240
     export SCREEN_WIDTH=320
     gawk -v width=320 -v height=240 -f voxel.awk | gzip -9 > voxel.precomputed
     zcat voxel.precomputed | fdraw 2>/dev/null | ffmpeg -f rawvideo -pixel_format rgba -video_size 320x240 -i - -vf "setpts=0.1*PTS,fps=30" -y voxel.mp4
    

    Fallout Mushroom Cloud

  • fallout.awk
  •  export SCREEN_FPS=30
     export SCREEN_HEIGHT=240
     export SCREEN_WIDTH=320
     gawk -f fallout.awk | fdraw | ffplay -f rawvideo -pixel_format rgba -video_size 320x240 -i -
    

    fdrawterm

    Zur Ausgabe aus einem Terminal in Halfblock Pixel, steht das Tool fdrawterm bereit. Es kann auch genutzt werden, um grafiken als ANSI ESC Sequenzen auf der Konsole zu betrachten.

     export SCREEN_WIDTH=80
     export SCREEN_HEIGHT=50
     convert example.png -resize 80x50 -compress none txt:- | awk -F'[(,):]' 'NR>1 {print "p", $1, $2, int($4), int($5), int($6)}' | fdrawterm
    
    
     convert example.png -resize 80x50 -compress none txt:- | awk -F'[(,):]' 'NR>1 {print "p", $1, $2, int($4), int($5), int($6)}' > example.txt
     (cat example.txt; echo s 40000) | fdrawterm > example.term
     cat example.term
    
    

    Weitere Beispiele und Experimente zum besseren Verständnis der Vorteile einer pipe

    
     export SCREEN_WIDTH=120 SCREEN_HEIGHT=80
    
     curl -Ls finalmedia.de/code/fdraw/torus.awk | gawk -f-  | fdrawterm
    
     curl -Ls finalmedia.de/code/fdraw/torus.awk | gawk -f-  | tr -dc "0-9a-z \n" | grep p | fdrawterm
    
     curl -Ls finalmedia.de/code/fdraw/torus.awk | gawk -f-  | grep -v p
    
     curl -Ls finalmedia.de/code/fdraw/torus.awk | sed "s/g = brightness/g = int(brightness * 0.6)/g" | gawk -f-  | fdrawterm
    
    

    Für derartige Spielereien empfehle ich zudem das coolretroterm als Terminal, aber auch allacritty oder xterm arbeitet damit.

     export SCREEN_WIDTH=120 SCREEN_HEIGHT=80
     curl -Ls finalmedia.de/code/fdraw/fallout.awk | gawk -f-  | fdrawterm
    

    Dabei lässt sich auch live die Anzahl der maximalen Partikel reduzieren

     export SCREEN_WIDTH=120 SCREEN_HEIGHT=80
     curl -Ls finalmedia.de/code/fdraw/fallout.awk | sed "s/1200;120;/g" | gawk -f- | fdrawterm
    

    Full Stack Demo x86_64 (Binarys)

    wget https://finalmedia.de/code/fdraw/fdraw
    wget https://finalmedia.de/code/fdraw/fdrawterm
    chmod +x fdrawterm
    chmod +x fdraw
    
    wget https://finalmedia.de/code/fvoxel/fvoxel
    chmod +x fvoxel
    curl https://finalmedia.de/code/fvoxel/map.txt.gz | zcat > map.txt
    wget https://finalmedia.de/code/fvoxel/rundflug.txt
    
    export SCREEN_WIDTH=100 SCREEN_HEIGHT=80
    
    # voxel rundflug
    cat map.txt rundflug.txt | ./fvoxel | ./fdrawterm
    
    # weitere spielereien
    wget https://finalmedia.de/code/fdraw/barnsley.awk
    timeout 30 gawk -f barnsley.awk | ./fdrawterm
    
    wget https://finalmedia.de/code/fdraw/torus.awk
    timeout 30 gawk -f torus.awk | ./fdrawterm
    
    wget https://finalmedia.de/code/fdraw/starfield.awk
    timeout 30 gawk -f starfield.awk | ./fdrawterm
    
    wget https://finalmedia.de/code/fdraw/plasma.awk
    timeout 30 gawk -f plasma.awk | ./fdrawterm
    
    wget https://finalmedia.de/code/fdraw/rayconst.awk
    gawk -f rayconst.awk | ./fdrawterm
    
    wget https://finalmedia.de/code/fdraw/fallout.awk
    timeout 30 gawk -f fallout.awk | ./fdrawterm
    
    timeout 30 od -t u1 /dev/urandom | tr -s " " | cut -d " " -f 2-6 | sed "s/^/p /g" | ./fdrawterm
    
    timeout 30 cat /dev/urandom | od -t u1 | tr -s " " | cut -d " " -f 2-4 | sed "s/^/p /g;s/$/ 12 30/g" | ./fdrawterm
    
    find / 2>/dev/null | od -t u1 | tr -s " " | cut -d " " -f 2-6 | sed "s/^/p /g" | ./fdrawterm
    
    curl -Ls https://finalmedia.de/code/fdraw/orb.gz | zcat