Werkstattbericht · Generative Reihe · Maierzone 2026

Generativ auf MZP Stack & Fallstricke

Was der Mzone Publisher für generative Arbeiten hergibt — und was beim Bauen von Morphogenesis, Dispersion Cathedral und Chladni tatsächlich wehgetan hat. Dreiundzwanzig Einträge, jeder aus einem konkreten Fehlschlag.

mzone-ui 1.8.0  ·  three 0.170.0  ·  23 Einträge  ·  Stand 2026-08-31

So liest sich ein Eintrag
F·08 error subject: CONSOLE_ERROR ⬡

Fehler abfangen, nicht loggen

Befund
Die Initialisierung wirft, der statische Ersatz greift korrekt — aber ein console.error im catch bleibt stehen.
Folge
Der Render-Check meldet CONSOLE_ERROR, obwohl die Seite genau das tut, was sie soll.
Kur
Init in try/catch, im Fehlerfall den Ersatz zeigen — und im catch schweigen.

Jeder Fallstrick trägt das Zeichen der Arbeit, an der er gelernt wurde. Die Kur-Zeile ist der Fix, nichts sonst.

§ 01

Was ausgeliefert wird

Fünf Bibliotheken liegen unter /lib/ — selbst gehostet, ohne CDN, ohne Netzabhängigkeit. Ein Bundle bindet sie mit relativen Pfaden ein und läuft offline im Prüf-Browser genauso wie im Netz.

BibliothekVersionEinstieg
three0.170.0/lib/three@0.170.0/three.module.js
d37.9.0/lib/d3@7.9.0/d3.min.js
chart.js4.4.6/lib/chart.js@4.4.6/chart.umd.js
mzone-ui1.8.0/lib/mzone-ui@1.8.0/mzone.css
fonts-maierzone1.0.0Space Grotesk · Inter · Instrument Serif · IBM Plex Mono
10 MB
entpackt, gesamt je Bundle
64
Dateien je Bundle
1
index.html, genau eine
5
Versionen je Slug (dann rollt es)
Schriften kommen aus fonts.css, nicht aus mzone.css. Dort liefert fonts.css vier selbst gehostete Familien (SIL OFL); in mzone.css ist --mz-font nur ein System-Stack. Für einen eigenen Auftritt beide Dateien verlinken — Instrument Serif trägt Werktexte spürbar besser als der System-Serif.
Die fx-Module sind für Reports gedacht. fluidField, glass, particleField, glyphRain, dither, asciify … — für eigenständige Arbeiten zu generisch. Eigenes GLSL schreiben; die Bibliothek nur fürs Gerüst (mz-body, mz-page) und die Barrierefreiheit (mz-sr-only) nutzen.
§ 02

Branding als Palette

Vier eigenständige Profile stehen bereit. nightline — Petrol #2F6F63, Gold #E8B34C, Surface #14181B — ist als Ops-Dashboard gedacht, trägt aber als Kunstpalette überraschend weit; alle drei bisherigen Arbeiten stehen darauf. Die Profile sind laut Publisher ausdrücklich eine Basis, kein Regelwerk: einzelne Tokens dürfen verstellt werden.

nightline ledger monoblock arcade
Diese Seite nimmt nightline und erweitert es um einen Rostton (#CE6B44) für die Stufe error — genau die Freiheit, die das Profil einräumt.
§ 03

Fallstricke

Dreiundzwanzig Einträge in fünf Gruppen. error heißt: der Render-Check schlägt an oder das Bild bleibt schwarz. warn heißt: es rendert, sieht aber falsch aus. info ist ein Kniff, der Arbeit spart.

APräzision & Farbe

F·01warnsubject: HALF_FLOAT⬡

Half-Float statt Float

Befund
Echtes FloatType mit linearer Filterung braucht eine Extension und ist nicht überall da.
Folge
Nahe 1.0 liegt die Auflösung von Half-Float bei rund 0.001; kleinere Simulationsschritte verschwinden ersatzlos.
Kur
Render-Targets als THREE.HalfFloatType. Schrittweiten nicht unter etwa 0.005.
F·02warnsubject: COLOR_SPACE⬡

Farbraum einmal festlegen

Befund
Ohne gesetzten Output-Farbraum wandelt three zweimal um.
Folge
Alles wird flau.
Kur
renderer.outputColorSpace = THREE.LinearSRGBColorSpace setzen und Farben direkt in sRGB schreiben.
F·03infosubject: FIREFLIES▲

Ausreißer klammern

Befund
Einzelne Pixel, deren gebrochener Strahl über eine Facettenkante genau die Lichtquelle trifft, blitzen als weiße Punkte.
Folge
Die Arbeit sieht verrauscht aus, obwohl sie es nicht ist.
Kur
min(farbe, vec3(6.0)) am Ende jedes Strahlengangs.
F·04warnsubject: SDF_RECT⬡

Rechteck-SDF vollständig schreiben

Befund
length(max(q, 0.0)) ist innerhalb des Rechtecks überall exakt null.
Folge
Eine Kantenmaske darauf färbt die ganze Fläche.
Kur
length(max(q,0.0)) + min(max(q.x,q.y), 0.0) — der Innenterm fehlt sonst.

BPublisher & Render-Check

F·05errorsubject: MZ_BODY⬡

mz-body ist Pflicht

Befund
Ohne class="mz-body" am <body> greifen Hintergrund und Grundschrift nicht.
Folge
Dunkel ist Default, hell kommt bei prefers-color-scheme: light — eine Arbeit braucht zwei Paletten.
Kur
mz-body setzen. Erzwingen mit <html data-theme="light">; darauf per MutationObserver reagieren.
F·06warnsubject: UNKNOWN_STYLE_CLASS⬡

Geratene CSS-Klassen fliegen auf

Befund
Der Publisher meldet unbekannte mz--Klassen beim Publish.
Folge
UNKNOWN_STYLE_CLASS statt gestyltem HTML.
Kur
Nur Klassen aus getCapabilities().styling. Eigene Klassen ohne mz--Präfix sind unproblematisch.
F·07behobensubject: VERBOSITY_COMPACT⬡

compact verschluckt den Render-Check nicht mehr

Befund
Bis zum Redeploy vom 2026-08-31 ließ verbosity: 'compact' Konsolenfehler und Erreichbarkeit weg — die erste Veröffentlichung lief blind.
Stand
Seither trägt compact den vollen render-Block (clean, consoleErrors, findings) und die reachability weiter; weg fallen nur Datei-Liste, libReferences und INFO-Hinweise.
Kur
Entfällt. verbosity: 'compact' darf jetzt von Anfang an gesetzt sein.
F·08errorsubject: CONSOLE_ERROR⬡

Fehler abfangen, nicht loggen

Befund
Der Ersatz greift korrekt, aber ein console.error im catch bleibt stehen.
Folge
Der Render-Check meldet CONSOLE_ERROR, obwohl die Seite sich korrekt verhält.
Kur
Init in try/catch, statischen Ersatz zeigen — und im catch nichts loggen.
F·09errorsubject: PAGE_EXCEPTION⬡

Kein throw auf Modulebene

Befund
Eine ungefangene Ausnahme im Top-Level eines ES-Moduls landet im Render-Check.
Folge
Der Render-Check meldet PAGE_EXCEPTION — auch wenn erst das 200. Bild betroffen ist.
Kur
Alles in boot(); try { boot(); } catch {}. Den Schleifenkörper ebenso.
F·10infosubject: PAYLOAD_BR⬡

Große Bündel komprimiert übergeben

Befund
publishArtifact nimmt je Datei contentBase64 mit encoding: 'br'.
Folge
40 kB Quelltext werden zu ~17 kB Base64.
Kur
br nutzen. Danach md5sum der ausgelieferten Datei gegen die lokale — die Übertragung läuft durch den Modellkontext.
F·11infosubject: MD5_HEAD_TAGS⬡

Beim md5-Abgleich die Meta-Tags einrechnen

Befund
Der Publisher schreibt canonical und die og:/twitter:-Tags beim Ausliefern in den <head>.
Folge
Die ausgelieferte index.html ist immer länger als die lokale.
Kur
Nur Begleitdateien direkt per md5sum vergleichen. Fürs HTML diff nehmen und sehen, dass nur der eingespritzte Block übrig bleibt. Oder die Injektion abschalten: metadataJson.socialPreview: false — dann ist die ausgelieferte index.html byte-gleich zur lokalen.
F·12infosubject: PATCH_MODE⬡

Zweite Datei per patch nachschieben

Befund
Ein Bundle muss nicht in einem Aufruf komplett sein.
Folge
Die MISSING_REFERENCE-Warnung des ersten Schritts ist dann erwartet.
Kur
Erst index.html publizieren, dann mit mode: 'patch' die app.js nachlegen — kein overwrite nötig, Unerwähntes bleibt.

CDer Raum, nicht das Objekt

F·13errorsubject: EMPTY_ENVIRONMENT▲

Brechung zeigt die Umgebung — nicht das Objekt

Befund
Ein Glaskörper vor schwarzem Grund sieht aus wie ein matter Stein. Völlig korrekt gerechnet, nur eben leer.
Folge
Der teuerste Irrtum bei Dispersion Cathedral — Stunden an Shader-Parametern, die nichts retten konnten.
Kur
Zuerst den Raum bauen: eine strukturierte Lichtquelle (ein Fenster mit Sprossen), deren Projektion auf dem Boden, ein zweites schwächeres Licht von der Gegenseite. Erst danach an Shader-Parametern drehen.

Was ein gebrochener Strahl beim Austritt findet, ist das Bild.

F·13 · EMPTY_ENVIRONMENT
F·14warnsubject: FLAT_AMBIENT▲

Grundlicht ist kein Ersatz für eine Quelle

Befund
Der erste Reflex ist, das Umgebungslicht hochzuziehen.
Folge
Der Raum wird flau und das Glas trotzdem nicht brillant.
Kur
Die Helligkeit muss aus einer kleinen, hellen, strukturierten Fläche kommen.

DAkkumulation & Zeit

F·15warnsubject: AUTOCLEAR◆ ●

autoClear abschalten, sobald akkumuliert wird

Befund
three löscht das Render-Target vor jedem render().
Folge
Ein Sandbett, ein Leuchtspur- oder Kaustik-Puffer über Bilder hinweg geht genau dort verloren.
Kur
renderer.autoClear = false; neue Puffer einmal von Hand mit renderer.clear(true, false, false) räumen.
F·16infosubject: DECAY_NO_PINGPONG●

Abklingen ohne Ping-Pong

Befund
Ein Puffer lässt sich in sich selbst dimmen, ohne zweites Target.
Folge
Spart die Hälfte des Speichers und einen Kopierschritt.
Kur
Vollbild-Quad mit blendSrc: ZeroFactor, blendDst: SrcColorFactor → Ergebnis dst × srcColor, je Kanal ein eigener Abklingfaktor.
F·17warnsubject: FRICTION_PER_FRAME●

Reibung je Sekunde, nicht je Bild

Befund
v *= 0.88 verhält sich auf 30 und 144 fps völlig verschieden.
Folge
Dieselbe Arbeit ist auf dem einen Rechner zäh, auf dem anderen zappelig.
Kur
v *= pow(damp, dt * 60.0) macht die Terminalgeschwindigkeit von der Bildrate unabhängig. Erst danach Parameter suchen.
F·18errorsubject: FADE_BY_FRAME⬡

Anlaufen nach Uhrzeit, nicht nach Bildern

Befund
Ein Einblendfaktor, der pro Bild wächst, bleibt auf einem langsamen Renderer bei null.
Folge
Das Bild ist schwarz, obwohl alles läuft. Eine halbe Stunde Fehlersuche.
Kur
(now - start) / ms nehmen.

Ein Faktor, der pro Bild wächst, misst die Hardware — nicht die Zeit.

F·18 · FADE_BY_FRAME
F·19warnsubject: QUALITY_RAMP⬡

Qualitätsstufen von unten anfahren

Befund
Startet man auf der höchsten Stufe, braucht das erste Bild auf schwacher Hardware Minuten.
Folge
Die Nachregelung greift nie.
Kur
Niedrig starten, nach ~12 Bildern messen, hocharbeiten. Ein ?q=0..3, das die Stufe festnagelt, macht die Prüfung im Headless-Browser überhaupt erst möglich.
F·20warnsubject: RESEED_ON_RESTEP●

Stufenwechsel darf das Bild nicht neu würfeln

Befund
Wird bei der Nachregelung die Partikelzahl geändert, ist Neu-Streuen die naheliegende — und sichtbar falsche — Lösung.
Folge
Auf schneller Hardware fliegt das Bild in den ersten Sekunden zwei-, dreimal auseinander.
Kur
Die alte Positionstextur mit einem Resample-Pass auf das neue Raster lesen (plus Mikro-Jitter). Das alte Target erst nach dem Pass dispose().

EHeadless

F·21warnsubject: NO_WEBGL2_HEADLESS⬡

Hardware-GL gibt es im Headless hier nicht

Befund
--use-gl=egl oder --use-angle=vulkan starten zwar, liefern aber kein WebGL 2.
Folge
Die Seite fällt auf den Ersatz zurück — leicht als Bug der Arbeit missdeutet.
Kur
Nur SwiftShader. Für teure Shader kleines Fenster (800×500), niedrige Stufe, großzügiges --virtual-time-budget; ein Bild kann eine Minute Realzeit brauchen.
F·22infosubject: VIRTUAL_TIME_FROZEN⬡

Headless friert performance.now() ein

Befund
Mit --virtual-time-budget laufen Dutzende Bilder, aber die Seitenzeit wächst kaum.
Folge
Alles Zeitgesteuerte — Auto-Wechsel, FPS-Anzeige, Einblendung — lässt sich nicht prüfen; prüfbar ist nur, dass gerendert wird.
Kur
Für jeden Zustand einen URL-Parameter vorsehen und die Zustände einzeln fotografieren.
F·23infosubject: STATE_VIA_URL⬡

Jeder Zustand über einen URL-Parameter

Befund
Zeitabläufe sind headless nicht beobachtbar (siehe F·22).
Folge
Ohne Schalter bleibt ein Großteil der Arbeit ungeprüft.
Kur
?q= Stufe, ?m= Startmotiv, ?t=light|dark — je ein fester Einstieg, einzeln zu fotografieren.
§ 04

Nachsehen, ob es läuft

HTTP 200 heißt nicht, dass WebGL etwas zeichnet. Für Konsolenfehler, unbehandelte Ausnahmen und tote Ressourcen reicht der Publisher selbst: validateArtifact lädt die Preview in einem echten Browser und meldet CONSOLE_ERROR / PAGE_EXCEPTION / RESOURCE_FAILED schon vor dem Publish — publishArtifact ebenso, danach.

Ein eigener Browser bleibt für den visuellen Blick auf einen teuren Shader nötig — und für wiki--Slugs, wo der Render-Check hinter der Zugriffssperre entfällt. Der chrome-devtools-MCP-Server sucht Chrome unter /opt/google/chrome/chrome und findet auf diesem Rechner nichts — installiert ist /usr/bin/chromium. Direkt aufrufen:

chromium --headless=new --no-sandbox --disable-dev-shm-usage \
  --hide-scrollbars --window-size=1280,800 \
  --use-gl=angle --use-angle=swiftshader --enable-unsafe-swiftshader \
  --enable-logging=stderr --v=0 \
  --virtual-time-budget=9000 \
  --screenshot=shot.png \
  "https://publisher.maierzone.de/published/<slug>/" 2>console.log

--use-angle=swiftshader plus --enable-unsafe-swiftshader gibt WebGL 2 in Software — langsam, aber es rendert; auf diesem Rechner der einzige Weg. --virtual-time-budget lässt die Simulation vor dem Screenshot ein paar Sekunden laufen, sonst fotografiert man das Startrauschen.

Im console.log ist fast alles Chrome-Eigenrauschen (PHONE_REGISTRATION_ERROR, D-Bus, sqlite_persistent_store, Sandbox-Warnungen). Danach filtern, sonst übersieht man den echten Fehler.
§ 05

Die Reihe

Jeder Fallstrick oben stammt aus einer dieser drei Arbeiten. Sie laufen live.

§ 06

Quellen

  • getCapabilities des Publishers — die maßgebliche Selbstbeschreibung
  • searchBranding — die vier Profile
  • Playground: /lib/mzone-ui@1.8.0/playground/ (öffentlich, ohne Auth)
  • Wyman, Sloan & Shirley, Simple Analytic Approximations to the CIE XYZ Color Matching Functions, JCGT 2 (2), 2013

Werkstattbericht zur generativen Reihe auf dem Mzone Publisher. Beschreibt den ausgelieferten Bibliotheks-Stack, die vier Branding-Profile und dreiundzwanzig konkrete Fallstricke beim Bauen von WebGL-Arbeiten — geordnet nach Präzision und Farbe, Publisher und Render-Check, Raum und Objekt, Akkumulation und Zeit sowie Headless-Prüfung.