1. Executive Summary
Pikchr ist eine kleine, bemerkenswert dichte, C-basierte DSL zur Erzeugung von Diagrammen als SVG. Sie ist PIC-inspiriert, aber klar auf das moderne Web ausgerichtet: Markdown-Einbettung, sicheres SVG, begrenzte Komplexität, keine Shell-Escapes, kein Copy-File-Missbrauch, keine Schleifen, keine Ifs.
Die zentrale Überraschung des Projekts ist nicht nur, dass es Diagramme erzeugt, sondern dass es sehr nah an einer allgemeinen semantischen Bild- und Layoutsprache operiert. Mit Boxen, Linien, Splines, relativer Positionierung, Text, Farben, Auto-Fit und Makros kann Pikchr weit mehr als „nur Kästchendiagramme“.
Für privacy-first interne Live-Dashboards empfehle ich nicht den exotischen No-JS-HTTP-Stream als Produktionslösung, sondern ein serverseitiges Modell: State serverseitig aggregieren → Pikchr serverseitig rendern → SVG per SSE an dünnen Client ausliefern. Der Browser bekommt dann nur die gerenderte Sicht auf den Zustand, nicht die Rohtelemetrie.
2. Was Pikchr ist
2.1 Grundidentität
- PIKCHR steht für eine PIC-ähnliche Diagramm-Markup-Sprache.
- Autor: D. Richard Hipp (auch SQLite).
- Lizenz: Zero-Clause BSD.
- Ausgabeformat: SVG.
- Einsatzidee: Diagramme in Markdown bzw. technischen Dokumentationen.
- Implementierung: C99 + Lemon Parsergenerator.
2.2 Designphilosophie
- Single-pass: Ein Objekt darf nur frühere Objekte referenzieren.
- Einfaches mentales Modell: Kein Constraint Solver, kein komplexes Layoutsystem.
- Sicherheit für Web-Kontext: Keine Shell-Kommandos, keine Dateiinserts, keine Schleifen.
- Relative statt absolute Beschreibung: Layout wird bevorzugt in Beziehungen statt in Koordinaten beschrieben.
2.3 Der eigentliche Reiz
Pikchr ist nicht nur interessant, weil es SVG erzeugt, sondern weil es visuelle Struktur semantisch beschreibt. Diese Eigenschaft macht es besonders spannend für Diagramme, aber auch für experimentelles Webdesign, statische Interfaces, live aktualisierte Systemdarstellungen und deklarative Visualisierungssysteme.
3. Projektstruktur
| Datei / Ordner | Bedeutung |
|---|---|
pikchr.y |
Zentrale Quelldatei: Lemon-Grammatik + sehr großer C-Implementierungsblock. |
pikchr.h.in |
Öffentliche Header-Vorlage. |
Makefile |
Generiert pikchr.c und baut CLI / Testartefakte. |
lemon.c |
Lemon Parsergenerator. |
lempar.c |
Lemon Parser-Template. |
doc/ |
Offizielle Doku in vielen kleinen Markdown-Dateien. |
examples/ |
Kleine Beispielskripte. |
tests/ |
Über 100 Testfälle in Pikchr. |
fiddle/ |
WASM-basierte Browser-Fiddle-App. |
homepage.md |
Projektnahe Start-/Infoseite. |
3.1 Bemerkenswerte Beobachtung
Der Großteil der eigentlichen Logik sitzt in einer einzigen Datei (pikchr.y). Das macht das Projekt einerseits konzentriert und elegant, andererseits auch ungewöhnlich dicht. Wer es wirklich verstehen will, kommt mit einer intensiven Lektüre dieser Datei erstaunlich weit.
4. Build- und Laufmodell
4.1 Klassischer Build
make clean
make pikchr
Dabei passiert im Kern:
lemonwird gebaut.mkversionerzeugtVERSION.h.lemon pikchr.yerzeugtpikchr.c.- Dann wird die CLI mit
-DPIKCHR_SHELLkompiliert.
4.2 CLI-Modi
./pikchr file.pikchr→ HTML-Hülle mit SVG und Source-View../pikchr --svg-only file.pikchr→ reines SVG../pikchr --dark-mode ...→ invertierte Farbwelt../pikchr --dont-stop ...→ mehrere Dateien trotz Fehlern weiter verarbeiten.
4.3 Weitere Build-Ziele
pikchrfuzzfür Fuzzer-Build.piktclfür Tcl-Bindings.- WASM-Fiddle über
fiddle/GNUmakefileundemcc.
5. Öffentliche API
char *pikchr(
const char *zText,
const char *zClass,
unsigned int mFlags,
int *pnWidth,
int *pnHeight
);
const char *pikchr_version(void);
5.1 Verhalten
- Input: nullterminierter Pikchr-Quelltext.
- Output: per
malloc()allokierter String. - Bei Erfolg: SVG.
- Bei Fehler: Fehlermeldungstext statt SVG.
*pnWidth/*pnHeightgeben SVG-Größe zurück oder negative Werte bei Fehlern.
5.2 Flags
| Flag | Bedeutung |
|---|---|
PIKCHR_PLAINTEXT_ERRORS |
Fehlertext als Plaintext statt HTML. |
PIKCHR_DARK_MODE |
Farben auf Dark-Mode-Nutzung hin transformieren. |
6. Sprachmodell
6.1 Grundstruktur
Ein Pikchr-Dokument ist eine Folge von Statements, getrennt durch Newlines oder Semikolons.
line; box "Hello"; arrow
6.2 Statement-Typen
- Objektdefinitionen
- Richtungswechsel:
right,down,left,up - Variablenzuweisungen
- Makrodefinitionen
print-Statementsassert-Statements- Labels für Objekte oder freie Positionen
6.3 Single-pass
Das Layout ist absichtlich einfach: ein Objekt kennt nur die Objekte, die vorher schon definiert wurden. Das macht Pikchr sehr vorhersehbar und erklärt auch, warum relative Referenzen so wichtig sind.
7. Objekte, Attribute, Positionierung
7.1 Objektklassen
| Klasse | Typ | Bemerkung |
|---|---|---|
box |
Block | Rechteck, optional mit Radius. |
circle |
Block | Kreis mit gekoppelten Width/Height/Radius-Regeln. |
ellipse |
Block | Ellipse. |
oval |
Block | Racetrack-/Kapsel-artig. |
diamond |
Block | Diamantform. |
cylinder |
Block | Zylinder mit Endkappenradius. |
file |
Block | Dokument-/Datei-Symbol. |
dot |
Block | Kleiner Punkt. |
text |
Block | Textobjekt, auch implizit via String als Statement. |
line |
Linie | Pfad ohne Pfeilspitze. |
arrow |
Linie | Wie line, aber mit Pfeil an Standardseite. |
spline |
Linie | Mehrsegmentiger Pfad mit Rundungen. |
arc |
Linie | Tatsächlich als Quadratic Bezier angenähert, nicht als echter Arc. |
move |
Linie | Unsichtbarer Abstandshalter. |
7.2 Ankerpunkte
Blockobjekte besitzen übliche Anker:
.n .ne .e .se .s .sw .w .nw .c .start .end
7.3 Attribute
Form / Größe
width/widheight/htradius/raddiameterfit
Stil
colorfillthicknessthin,thick,soliddashed,dotted,invis
Pfad / Linie
from,to,then,goheadingchopclose<-,->,<->cw,ccw
Position
atwith .edge at ...- relative Positionen:
left/right/above/below - Zwischenpositionen:
1/2<A,B>bzw.between
8. Ausdrücke, Variablen, Funktionen
8.1 Arithmetik
expr + expr
expr - expr
expr * expr
expr / expr
(expr)
+expr
-expr
8.2 Built-in Funktionen
abs()cos()sin()int()min(),max()sqrt()dist(position, position)
8.3 Variablen
Wichtige built-ins u. a.:
| Variable | Bedeutung |
|---|---|
boxwid, boxht, boxrad |
Default-Boxmaße. |
circlerad |
Default-Kreisradius. |
linewid, lineht |
Default-Linienmaße. |
thickness |
Default-Linienstärke. |
color, fill |
Default-Farben. |
charwid, charht |
Textgrößenheuristik. |
fontscale |
Skalierung der Textdarstellung. |
margin, leftmargin, ... |
Außenabstand um das Diagramm. |
scale |
Skaliert die SVG-Ausgabegröße. |
8.4 Maßeinheiten
Numerische Literale können Suffixe haben:
cm mm in px pt pc
9. Text, Auto-Fit und Bounding Boxes
9.1 Warum das bemerkenswert ist
Pikchr kennt die echte Browser-Fontmetrik nicht. Trotzdem schafft es das Projekt, Text oft überraschend gut zu schätzen und daraus brauchbare Boxgrößen abzuleiten. Genau diese Stelle ist eine der beeindruckendsten technischen Leistungen des Codes.
9.2 Wie Text intern geschätzt wird
- ASCII-Zeichen haben eine interne Breitentabelle (
awChar[]). - Monospace wird separat behandelt.
- Entities und UTF-8 werden berücksichtigt.
charwidundcharhtskalieren die Heuristik.big,small,bold,italic,monobeeinflussen die Schätzung.
9.3 Auto-Fit-Algorithmus
Wichtige Funktionen:
pik_text_length()– Breitenabschätzung.pik_append_txt(..., pBox)– berechnet Text-Bounding-Boxen ohne tatsächlich zu rendern.pik_size_to_fit()– errechnet aus Textgröße passende Objektmaße.xFit()je Objektklasse – wendet Fit-Regeln objektspezifisch an.
9.4 Wichtige Erkenntnis
9.5 Warum trotzdem Padding sichtbar ist
Selbst wenn nur Text gerendert wird, fügt Pikchr am Ende noch Diagrammränder hinzu (margin plus einen Sicherheitsabstand basierend auf thickness). Deshalb ist ein „text only“-SVG nicht vollkommen auf die Glyph-Bounds zugeschnitten.
10. Rendering-Pipeline intern
10.1 Objektmodell
Jedes Statement erzeugt ein PObj. Alle Objekte landen in einer PList. Eine globale Struktur Pik hält den Zustand des laufenden Renderings.
10.2 Typische Phasen
- Tokenisierung des Input-Strings.
- Parsing via Lemon.
- Objekte werden sukzessiv erzeugt und positioniert.
- Nach Statementende werden Attribute fixiert und Bounding Boxes berechnet.
- Nach komplettem Parse wird eine Gesamt-Bounding-Box aufgebaut.
- Dann wird SVG generiert.
10.3 Wichtige interne Strukturen
| Typ | Rolle |
|---|---|
Pik |
Gesamtkontext eines Renderlaufs. |
PObj |
Ein Diagrammobjekt. |
PList |
Liste von Diagrammobjekten. |
PClass |
Vtable-artige Klassendefinition pro Objekttyp. |
PToken |
Tokenizer-/Parser-Token. |
PVar |
Variableinträge. |
PMacro |
Makrodefinitionen. |
10.4 Schöne Designentscheidung
Jede Objektklasse implementiert Verhalten über ein kleines Methodenset (xInit, xFit, xRender, xCheck, xOffset, xChop). Dadurch ist das System überraschend erweiterbar, obwohl alles in einer Datei lebt.
11. Tokenizer, Parser und Makros
11.1 Tokenizer
Der Tokenizer erkennt unter anderem:
- Strings in Anführungszeichen
- Zahlen mit Units
- Hex-Farbwerte
- Kommentare:
#,//,/* ... */ - Unicode-/HTML-Aliasse für Pfeile
- Uppercase-Labels vs lowercase-Variablen
11.2 Parser
Lemon erzeugt einen LALR(1)-Parser. Die Grammatik sitzt in pikchr.y. Pikchr ist damit formal genug, um sehr klar lesbar zu bleiben, ohne in klassische Compiler-Komplexität abzudriften.
11.3 Makros
define NAME { ... }
Makros unterstützen bis zu 9 Parameter ($1 … $9).
- Rekursion wird verhindert.
- Makros zählen gegen das Token-Limit.
- Argumente werden kontextbewusst geparst.
12. Security- und Safety-Modell
12.1 Sehr bewusste Web-Sicherheit
Pikchr ist auffällig defensiv designt. Weggelassen wurden absichtlich:
sh– keine Shell-Kommandoscopy– keine Dateiinsertsfor,if– keine Kontrollflusskonstruktesprintf()– keine problematische Stringformatierung
12.2 Token-Limit
Es gibt ein bewusstes Token-Limit (PIKCHR_TOKEN_LIMIT, Default 100000), um DoS-artige Makroexplosionen zu begrenzen.
12.3 Fazit
Pikchr ist nicht „sicher, weil klein“, sondern weil es in vielen Punkten absichtlich so gebaut wurde, dass gewisse Klassen von Missbrauch gar nicht erst auftreten können.
13. Wichtige Unterschiede zu klassischem PIC
- Neue Objekttypen:
oval,diamond,cylinder,file,dot. - Units wie
cm,mm,pxusw. colorundfillals CSS-ähnliche Farbsprache.fitund Auto-Fit um Text.same as objectals Erweiterung.chopmit neuer Semantik.- Mehr Textattribute und modernere Textkontrolle.
- Variablen wie
fontscale,margin,charwid,charht. - Pikchr verschiebt den
.end-Anker des vorherigen Objekts bei Richtungswechseln anders als PIC. - Variablen in
[]-Containern werden nach Verlassen nicht restauriert.
14. Browser- und Playground-Experimente
14.1 WASM-Fiddle
Das Repository enthält eine Browser-Fiddle, die eigentlich per Emscripten/WASM funktioniert. In meinem Kontext war emcc nicht installiert, daher habe ich zunächst stattdessen eine Browser-Playground-Lösung mit nativer CLI-Render-Pipeline aufgebaut.
14.2 CLI + Browser Hybrid
Es wurde ein lokaler Playground umgesetzt:
- Browser-Editor als HTML
- lokaler Python-Server
- Server ruft native
./pikchr --svg-only -auf - Browser zeigt das SVG an
Das ist kein WASM, aber praktisch ein „browser UI over native CLI renderer“.
14.3 Fiddle Auto-Render Bug
Beim offiziellen Fiddle-Code wurde eine Schwäche im „render while typing“-Modus gefunden:
- zu stark auf
keydownstattinputgestützt - kleiner Fehler in der Tastencode-Filterlogik
- behoben durch Umstellung auf robustere Input-Event-Nutzung
15. Dashboards, SVG und Live-Interfaces
15.1 Wichtige Erkenntnis
Mit Pikchr lassen sich nicht nur Diagramme, sondern erstaunlich überzeugend auch Dashboard-Mockups, semantische Oberflächen und grafische Systemräume bauen. Es ist nicht dasselbe wie HTML/CSS-Layout, aber visuell kann man sehr weit kommen.
15.2 Gebaute Experimente
- Plausible-artiges Dashboard als Pikchr-SVG
- Light/Dark-Varianten
- „Pure“ SVG-Version mit minimaler HTML-Hülle
- Fake-live HTML-Demo mit SVG-Mutationen
- Control-room/System-flow Diagramm für privacy-first Architektur
15.3 Strategischer Wert
Pikchr ist besonders stark, wenn nicht DOM-Interaktivität und Accessibility das Hauptziel sind, sondern eine server-autorisierte, semantische, visuelle Zustandsdarstellung. Gerade für Architektur, Monitoring, Produkt-Analytics-Mockups und Systemkartierung ist das hochinteressant.
16. No-JS Streaming & Live-Update-Strategien
16.1 Fake-live im Browser
Ein erster Live-Effekt wurde über vorgerendertes SVG plus Browser-JS realisiert. Dabei wurden nur Zahlen, Bars, Linienpfade und Labels aktualisiert. Das zeigt: Schon ohne komplettes Neu-Rendern ist ein lebendiges Dashboard möglich.
16.2 No-JS HTTP/1.1 Stream
Danach wurde ein bewusst radikaler Prototyp gebaut: multipart/x-mixed-replace mit SVG-Frames über HTTP/1.1, ohne Frontend-JS für die Updates. Das ist architektonisch poetisch und experimentell reizvoll:
- Server rendert Zustände in Pikchr
- Server streamt komplette SVG-Frames
- Browser konsumiert Stream als Bildressource
16.3 Drei Laufzeitmodelle
| Modell | Beschreibung | Bewertung |
|---|---|---|
| Pre-render + SVG mutieren | Pikchr nur beim Build, Browser ändert Teile im DOM. | Gut für Demos und leichte Interaktivität. |
| Server-side render bei Zustandswechsel | Server rendert neues SVG pro Update. | Sehr sauber für privacy-first interne Dashboards. |
| WASM im Browser | Browser rendert Pikchr lokal. | Später interessant, aber komplexer. |
17. Production-Architektur-Empfehlung
17.1 Entscheidungskriterium: Privacy first
Unter dem Leitwert privacy first lautet meine Empfehlung klar:
17.2 Warum SSE zuerst und nicht WebSocket
- einfacher
- einseitig Server → Client, was für Dashboards oft reicht
- leichter hinter Reverse Proxies
- weniger moving parts
- leichter zu härten und zu debuggen
17.3 Empfohlener Datenfluss
- Events / Rohtelemetrie werden serverseitig ingestet.
- Ein Aggregator erzeugt einen renderbaren Zustand.
- Ein isolierter Renderer erzeugt daraus Pikchr bzw. SVG.
- Der Browser erhält nur Snapshot-SVG und Live-Updates.
- Rohdaten verlassen den Server nicht.
17.4 Iterationsstrategie
- Start: komplettes SVG per Update austauschen.
- Später: falls nötig Teilupdates und weichere Client-Animationen.
- Noch später: optional WASM oder Hybridmodell evaluieren.
18. Hardening-Checkliste
18.1 Rendering
- nicht direkt im Request-Thread rendern
- Worker-Prozess oder Sandbox
- Timeouts, Memory-Limits, CPU-Limits
18.2 Streaming
- max. gleichzeitige Clients
- Idle-Timeouts
- Slow-Client-Handling
- Backpressure beachten
18.3 Zugriff
- Auth vor Dashboard
- nicht öffentlich ohne Schutz deployen
- TLS/HTTPS erzwingen
18.4 Reverse Proxy
- Buffering für Streams deaktivieren
- Cache auf Live-Endpunkten deaktivieren
- saubere Timeouts definieren
18.5 Sicherheits-Header
Cache-Control: no-storeX-Content-Type-Options: nosniff- CSP
- Referrer-Policy
19. Im Verlauf erzeugte Artefakte
Während der Exploration wurden mehrere Dateien erzeugt:
| Datei | Zweck |
|---|---|
playground.html |
Browser-Playground-Oberfläche. |
playground_server.py |
Lokaler HTTP-Server, der native CLI aufruft. |
dashboard_mockup.pikchr |
Erster größerer Dashboard-Versuch. |
dashboard_mockup.html |
Gerenderter Dashboard-Mockup-Prototyp. |
dashboard_v1.pikchr |
Reduzierte Dashboard-Version. |
dashboard_v1_light.pikchr |
Light-Theme-Version. |
dashboard_v1.html |
Light/Dark HTML-Demo. |
dashboard_v1_pure.html |
Nahezu reines SVG-Frontend. |
dashboard_fake_live.html |
Fake-live Dashboard mit SVG-Mutationen. |
stream_dashboard_server.py |
No-JS HTTP/1.1 Streaming-Prototyp mit SVG-Frames. |
control_room_flow.pikchr |
Privacy-first System-/Control-Room-Architekturdiagramm. |
control_room_flow.html |
Gerenderte HTML-Fassung des Architekturdiagramms. |
20. Schlussfazit
Pikchr ist ein außergewöhnlich spannendes Projekt, weil es mehrere seltene Eigenschaften vereint:
- klein genug, um als Ganzes verstehbar zu bleiben
- formal genug, um ernsthaft einsetzbar zu sein
- sicher genug, um im Webkontext Sinn zu ergeben
- flexibel genug, um über klassische Diagramme hinauszuwachsen
Die wichtigste persönliche Erkenntnis aus dieser Analyse ist wahrscheinlich diese:
Genau deshalb taucht es plötzlich in Bereichen auf, die zunächst überraschend wirken: Dashboard-Mockups, technische Storytelling-Flächen, Architekturansichten, Monitoring, Live-Systembilder, experimentelles Webdesign und privacy-first Visualisierungssysteme.
Wenn man es produktiv ernst nimmt, würde ich Pikchr nicht als universellen HTML/CSS-Ersatz denken, sondern als server-authoritative rendering layer für klar definierte Zustandsbilder. In dieser Rolle ist es nicht nur interessant, sondern potenziell sehr stark.