{"findings":[{"area":"Extremwerte / tapete","criterion":"Extreme Parameterwerte duerfen nicht crashen (Instanz-Budget muss greifen)","severity":"kritisch","finding":"tapete zeilen=1e6 oder spalten=1e6 (getippt) friert den Tab >15s ein und crasht den Renderer-Prozess. instances() materialisiert die volle Zellliste (2*zeilen)*(2*spalten) plus 6 Matrizen pro Zelle und sortiert sie, BEVOR das Instanz-Budget (INSTANCE_CAP=1500) kappen kann — bei 1e6 Zeilen sind das 8 Mio. Zellen / 48 Mio. Matrix-Arrays -> OOM.","evidence":"Playwright-Repro: 'PRESS FAIL 15003ms' -> 'Target crashed', identisch fuer zeilen und spalten. blocks-muster.js:92-117 (Zellschleife + 6 Matrizen vor jeder Budget-Pruefung, Sort :104). Gegenprobe zellgroesse=1e6: 60,7 fps mit Limit-Pille. [Auditor: robustheit]","suggestion":"Zellzahl VOR der Schleife deckeln: benoetigte Instanzen = (2r)*(2c)*(spiegeln?6:3); wenn > INSTANCE_CAP, rows/cols proportional reduzieren (Zentrum-zuerst) und instanceLimitHit setzen."},{"area":"?s= URL-State / Persistenz","criterion":"Share-/Reload-URL muss bei realistischen Szenen funktionieren","severity":"kritisch","finding":"Realistische Szene (15 Bloecke + 3 Pfade a 200 Punkte) erzeugt eine ?s=-URL von 13.833 Zeichen; der Server akzeptiert nur ~8k (9k -> HTTP 414, ~13k -> Connection Closed). Da die URL per replaceState auch in die Adresszeile geschrieben wird, zerstoert schon ein F5 die Szene komplett (Seite laedt gar nicht mehr) — stiller Datenverlust ohne jeden Hinweis beim Teilen.","evidence":"urlLen=13833, page.goto(shareUrl) -> net::ERR_CONNECTION_CLOSED; curl-Grenzprobe: 8100 Zeichen 200, 9000 -> 414, 13000 -> exit 56. state.js:46-62 buildUrl()/writeNow() ohne Laengenpruefung. [Auditor: robustheit]","suggestion":"URL-Laenge in writeNow()/shareUrl() gegen ~7,5k-Budget pruefen: Pfad-Punkte staerker quantisieren/Delta-encoden, localStorage-Fallback, deutscher Warnhinweis beim Teilen; server-seitig large_client_header_buffers anheben."},{"area":"Meta-Tool / Undo","criterion":"Grundfunktion Undo/Redo; irreversibles Loeschen","severity":"mittel","finding":"Kein Undo/Redo im gesamten Tool. Der Loeschen-Button entfernt Bloecke (inkl. muehsam gezeichneter Pfade) sofort und unwiederbringlich; da der URL-State per replaceState (nie pushState) geschrieben wird, rettet auch Browser-Back nichts — Back verlaesst die Seite sofort. Die Spec-Milderung 'URL-State mildert' greift beim Loeschen gerade nicht.","evidence":"ui.js:206-210 (del -> scene.remove direkt); grep undo/redo = 0 Treffer; state.js:56 replaceState, kein pushState (grep); Back-Test: historyLen=2, afterBack='about:blank'. [Auditoren: vollstaendigkeit, robustheit]","suggestion":"Minimal unter V1-Budget: Ein-Schritt-Undo fuer 'Baustein geloescht' (Toast 'Rueckgaengig'); mittelfristig Ctrl+Z fuer Param-/Stack-Aenderungen."},{"area":"Ranges / Render-Clamps","criterion":"Soft-Range: getippte Werte muessen wirken; 0 ueberall gueltig; negative Werte wo mathematisch sinnvoll","severity":"mittel","finding":"Systemisches Muster ueber fast alle Bloecke: emit() clampt Parameter hart, sodass getippte Soft-Range-Werte still wirkungslos bleiben — das Eingabefeld zeigt den Wert, das Bild ignoriert ihn. Betroffen u.a.: Linienstaerke (0.5..12, 0 unmoeglich obwohl Original whitelines 0.01 als Look hat), Gluehen (0..40), Gitter-Dichte (2..80), Symbol-Groesse, Kurven-morph (0..100 — negatives inverses Herz des Originals unmoeglich, morph=-80 rendert pixelidentisch zu 0), Ringschrift-Radius (Math.max(1) verschluckt negatives Invertieren), Fourier (Kreise 2..400 statt 9999, Tempo 0..20, Freq-Skala ±8, Versatz ±100, Text max 12 Zeichen), Kaleidoskop-Segmente (min 1). Dazu viele Slider-Min > 0.","evidence":"Live-Beweise: width=0 getippt -> Kurve bleibt sichtbar (2418 lit px); morph=-80 == morph=0 (toDataURL identisch); radius=-300 == radius=0; circles=1000 -> effektiv 365. Code: blocks-dinge.js:95,127,258,262-263,301,358,514; blocks-fourier.js:342,355-358,435-436,481; blocks-muster.js:238,316-317,392,397; blocks-leben.js:214,283,301,383,397-425,591; blocks-kraefte.js:83,95,258,268. Projektregel CLAUDE.md '0 immer gueltig / Ranges unbegrenzt'. [Auditoren: konzept, vollstaendigkeit, robustheit, uiux]","suggestion":"Reine Geschmacks-Caps (glow, width, size, density, morph, Freq-Offset) entfernen oder auf Soft-Range koppeln; 0 als neutralen Aus-Zustand interpretieren (segmente 0 = keine Spiegelung, width 0 = unsichtbar); nur echte Perf-Budgets behalten und deren Greifen im UI zeigen (analog Instanz-Limit-Pille)."},{"area":"Ranges / Positions-Kaefig","criterion":"Unendlicher Raum (0=1=inf); Ranges so unbegrenzt wie moeglich","severity":"mittel","finding":"Alle Positionen (x/y bzw. cx/cy) werden in emit() UND beim Canvas-Drag hart auf ±2000 geclampt — obwohl die Kamera unendlich pannt, kleben Objekte an einer unsichtbaren Wand. Ein frei getippter Wert x=5000 wird gespeichert, rendert aber identisch zu x=2000 und springt beim kleinsten Drag auf 2000 zurueck. pfad-Drag ist unbegrenzt und beweist, dass der Clamp unnoetig ist. Das ist zugleich der spuerbarste Widerspruch zur eigenen Unendlichkeits-These: wer das ∞-Symbol auf x=3000 schiebt, erlebt eine unsichtbare Wand.","evidence":"Pixel-Beweis: x=2000 vs x=5000 rendert identisch (531==531 lit px); Drag-Test: x=5000 -> nach 10px-Drag params.x==2000. Clamp-Stellen: blocks-dinge.js:266-267,321-322,356-357,376-377; blocks-muster.js:241-242,277-279,318-319,353-354,400-401,465-466; blocks-leben.js:327-329,450-451; blocks-fourier.js:428-429,542-543; blocks-kraefte.js:150-152,235-236; pfad ohne Clamp (blocks-dinge.js:571-573). [Auditoren: konzept, interaktion, vollstaendigkeit]","suggestion":"±2000-Clamps aus emit() und allen drag()-Implementierungen entfernen (Schema-min/max nur als Slider-Default); gleichzeitig der wirksamste Philosophie-Fix."},{"area":"Kamera / Szene","criterion":"Verlorene Szene wiederfinden; Reset-Moeglichkeit","severity":"mittel","finding":"Es gibt weder Ansicht-Reset noch Szenen-Reset: Wer die Kamera weit wegbewegt, sieht eine leere Leinwand ohne Hinweis — und die verlorene Kameraposition wird via ?s= sogar ueber Reload und Share-Link persistiert. Zurueck zur Default-Szene kommt man nur durch manuelles Entfernen des Query-Params oder N-faches Loeschen; fast alle Alt-Tools haben einen Reset-Button.","evidence":"camera (999999,999999) -> 0 sichtbare Pixel, kein Button matcht /reset|zentr|home|fokus/ (DOM-Scan), nach reload immer noch x=999999; dblclick tut nichts (ui.js:1133-1147). Kein 'Neu'-Button (0necanvas.html:129-144); Referenz circleheart.html:566-572 addResetButton. [Auditoren: interaktion, vollstaendigkeit]","suggestion":"Zoom-to-fit-Button (oder Doppelklick auf leere Flaeche = Szene einpassen) + Hinweis-Pill wenn kein Block im Viewport; 'Neu'-Button in der Topbar (Szene leeren + Default + URL-Param entfernen)."},{"area":"Performance / Kraft-Multiplikation","criterion":"Performance auf schwacher Hardware; dunkle Aesthetik unter Komposition","severity":"mittel","finding":"Das Instanz-Budget zaehlt Instanzen, nicht Prims — akkumulierende Erzeuger (strahlen bis 2000 Einzel-Polylines) multipliziert mit Kraeften druecken 20 gemischte Bloecke in 3 Minuten monoton von 32,6 auf 3,3 fps (kein Leak, Heap stabil). Gleichzeitig kippt die additive Glow-Ueberlagerung ohne Deckkraft-Normalisierung die dunkle Grundstimmung ins Weisse: strahlen+tapete saettigt 99,98% aller Pixel, fraktal+tapete ueber gitter 100% — schon mit Defaults Weiss-Rauschen statt Muster.","evidence":"t6: t=2s 32,6 fps -> t=170s 3,3 fps, Heap ~36 MB stabil; Screenshot load20-end fast weiss. Coverage: strahlen 12,2% -> +tapete 99,98%, spirale 6,1% -> 98,1%. LOD-Controller (dynCap) begrenzt nur Instanzzahl. [Auditoren: robustheit, matrix]","suggestion":"Prim-/Stroke-Kosten ins dynCap-Budget einrechnen; pro-Instanz-Alpha bei tapete/kaleidoskop mit Instanzzahl skalieren (z.B. alpha ~ 1/sqrt(n))."},{"area":"Extremwerte / Feedback","criterion":"Sehr grosse Werte: sinnvolles Verhalten statt Quasi-Freeze; Feedback an den User","severity":"mittel","finding":"Mehrere 1e6-Faelle druecken die Framerate in den Quasi-Freeze-Bereich ohne jedes UI-Signal: verzerren radius=1e6 -> 5,0 fps, ringschrift size=1e6 -> 7,1 fps, zellautomat radius=1e6 -> 14,7 fps (Limit-Pille jeweils aus); kaleidoskop segmente=1e6 zeigt die Pille, bleibt aber bei 3,4 fps. Der LOD-Controller begrenzt Instanzen, nicht Prim-/Pixel-Kosten innerhalb eines Blocks.","evidence":"t2-results.json (42 Faelle, frische Seite, rAF-Zaehler): fps 3,4-15,2 bei limitPill false in 4 von 5 Faellen; Screenshots audit-robust-extreme-*.png. [Auditor: robustheit]","suggestion":"Vorhandenes frameEma (engine.js) nutzen: bei anhaltend >80ms Frames Segment-/Radius-Arbeit degradieren und die Pille generischer als 'Leistungs-Limit' einblenden."},{"area":"Spawner Template-Select","criterion":"Erzeuger x Dinge: alle registrierten Ding-Typen als Template waehlbar (Spec Abschnitt 2)","severity":"mittel","finding":"Das Select 'Was wird erzeugt' bietet nur 5 Optionen; textpunkte, linienschar, spirale und ringschrift fehlen. Ursache: templateOptions() wird beim Laden von blocks-erzeuger.js einmalig ausgewertet, aber blocks-muster.js/blocks-leben.js laden erst danach — die Auswahlliste ist eingefroren, obwohl zur Laufzeit alle 8 Templates einwandfrei funktionieren (via params/URL bewiesen).","evidence":"Live-DOM: 5 Optionen [symbols, gitter, kurve, symbol, pfad]; Runtime template='spirale' lit=3192 px / 'ringschrift' 1738 px. blocks-erzeuger.js:285 (Ladezeit-Auswertung), 0necanvas.html:76-79 (Script-Reihenfolge). [Auditoren: matrix, uiux]","suggestion":"Optionen beim Panel-Aufbau dynamisch aus der Registry ziehen (options als Funktion in ui.js erlauben) oder blocks-erzeuger.js als letztes Block-Script laden."},{"area":"Erzeuger Positionierbarkeit","criterion":"Konsistenz: jeder Block mindestens X/Y-verschiebbar","severity":"mittel","finding":"spawner, zellautomat und textbaum haben keine X/Y-Params, kein hit/drag und kein bewegliches Zentrum — fest am Weltursprung verankert; Drag darueber pannt die Kamera. Zwei Spawner an verschiedenen Orten oder GoL neben einer Kurve gehen nur ueber Kraft-Tricks. Auch kaleidoskop hat kein Zentrum-Param (fraktal hat cx/cy) und gitter ist als einziges Ding komplett unberuehrbar (fuer ein unendliches Gitter vertretbar, aber Konsistenzbruch 7/8).","evidence":"Playwright-Drag: spawner/zellautomat/textbaum/gitter moved:false, panned:true (Camera-Delta exakt -100/-70); strahlen/fourier dagegen moved:true. blocks-erzeuger.js:380 'no hit/drag: spawner has no position params in V1'; Schemata blocks-leben.js:184-194, 561-571 und blocks-kraefte.js:257-261 ohne x/y bzw. cx/cy. [Auditoren: interaktion, vollstaendigkeit]","suggestion":"x/y + hit/drag nach dem strahlen-Muster ergaenzen (Muster existiert im selben File); der Streuradius-Kreis des Spawners waere der natuerliche Griff."},{"area":"Selektions-Overlay","criterion":"Jeder Block-Typ zeigt ein Selektions-Overlay auf der Leinwand","severity":"mittel","finding":"8 von 17 Typen zeigen bei Selektion KEINERLEI Overlay auf der Leinwand: gitter, kurve, symbol, textpunkte, linienschar, spirale (6 der 8 Dinge!), kaleidoskop und tapete; bei strahlen ist es nur ein ~3px-Punkt. Der selektierte Zustand ist fuer diese Typen nur im Panel erkennbar.","evidence":"Accent-Pixel-Zaehlung (#a8b8e8) vor/nach select(): 8 Typen = 0 Pixel Zuwachs, strahlen 32 px; dagegen pfad 1564, verzerren 2622, spawner 1792. Screenshot audit-interaktion-sel-kurve.png (selektierte Kurve ohne Overlay); overlay() fehlt in den betroffenen Defs. [Auditor: interaktion]","suggestion":"Dezentes Akzent-Overlay je Typ (gestrichelte Bounding-Kontur oder Zentrum-Punkt wie bei fraktal); bei kaleidoskop/tapete mindestens Ursprungs-/Zellgitter-Andeutung."},{"area":"Glyphen unter non-affinen Kraeften","criterion":"Jede Kraft veraendert jeden Inhalts-Block (Kernversprechen)","severity":"mittel","finding":"Doppelte Ursache macht Kraefte auf das Symbol-Ding mit Defaults wirkungslos: (a) Glyph-Prims werden bei non-affinen Maps nur am Anker transformiert (dokumentierte V1-Vereinfachung — nie verformt oder mitrotiert), (b) die Default-Position (0,0) ist exakt das Kraft-Zentrum und damit Fixpunkt. Ergebnis: 'Raum verzerren' auf 'symbol' hat bei allen drei Arten NULL Pixel Wirkung (auch bei Staerke 80), 'Fraktal' stapelt 8 Kopien unsichtbar am selben Anker — der Nutzer schiebt den Slider und nichts passiert, ohne Feedback. Das sind die einzigen 2 Ausfaelle der 52er-Matrix.","evidence":"Pixel-Diff (dt=0-Toggle): symbol+verzerren diffForce=0 fuer twist/welle/sog auch bei staerke=80; off-center (200,100) wirkt es (diff 4068 px); symbol+fraktal diff nur 85 px. engine.js:312 'glyph/dot: transform the anchor only (V1)', engine.js:326-339 (rot/size unveraendert); blocks-kraefte.js:109/120 (Fixpunkt bei d<1e-6). Screenshots audit-matrix-NOEFFECT-*.png. [Auditor: matrix]","suggestion":"Lokale Rotation numerisch schaetzen (map an Anker + kleinem Offset) und auf glyph.rot addieren — billig, macht Twist auf Text sofort glaubwuerdig; zusaetzlich Fraktal-Default-Zentrum leicht versetzen (z.B. cx=60), damit die Wiederholung sofort sichtbar ist."},{"area":"Onboarding / Hilfe","criterion":"Hilfe & Dokumentation (Nielsen #10), Erlernbarkeit","severity":"mittel","finding":"Keinerlei Erklaerung des Kern-Modells: was Dinge/Erzeuger/Kraefte sind, dass Kraefte auf alles DARUNTER wirken, dass 'Zeichnen' Pfad-Bloecke erzeugt und 'Verzerren' das Kraft-Zentrum zieht, Doppelklick-Loeschen von Pfadpunkten — nichts davon steht irgendwo. Bibliothek-Kacheln haben nur Icon+Name, einziger Text auf der Buehne ist '0 ≡ 1 ≡ ∞'.","evidence":"DOM-Scan aller [title]: nur Werkzeug-/Button-Namen; grep hilfe/help/onboard = 0 Treffer; Screenshot audit-uiux-02-lib-tab1.png (Kacheln ohne Beschreibung). [Auditoren: uiux, vollstaendigkeit]","suggestion":"Ein-Satz-Beschreibung pro Tab-Kopf ('Kraefte — wirken auf alles darunter') + dismissbarer Erstbesuch-Hinweis (3 Zeilen) oder ?-Overlay."},{"area":"Leerzustaende","criterion":"Sichtbarkeit des Systemstatus / Empty States (Nielsen #1)","severity":"mittel","finding":"Leere Szene: Stapel-Liste komplett leer ohne Hinweis, Canvas rein schwarz. Kritischer: Kraft als einziger/unterster Block rendert nichts — Neuling fuegt Kaleidoskop hinzu, 'nichts passiert', kein Hinweis dass Kraefte nur auf Bausteine darunter wirken.","evidence":"stackHTML='' nach Entfernen aller Bloecke; Kaleidoskop solo: lit=0 von 179520 Sample-Pixeln, kein Hinweistext im DOM. Screenshot audit-uiux-03-empty-scene.png. [Auditor: uiux]","suggestion":"Empty-State-Zeile im Stapel + dezenter Canvas-Hinweis wenn eine Kraft nichts unter sich hat ('Kraft wirkt auf Bausteine darunter — fuege ein Ding hinzu')."},{"area":"Mobile Bottom-Sheet","criterion":"Affordanz / Flexibilitaet (Nielsen #7)","severity":"mittel","finding":"Der Griff-Balken oben am Bottom-Sheet ist reine Dekoration: Ziehen tut nichts, das Sheet ist weder kollabier- noch resizebar und belegt dauerhaft ~46% des Screens (388 von 844px) — die Leinwand ist mobil nie groesser als 404px sichtbar, fuer ein Kunst-Tool viel.","evidence":"CSS .mobile-tabs::before nur Griff-Optik (0necanvas.css:754ff), kein Sheet-Drag-Handler in ui.js; Pointer-Drag gemessen: sheet.top 455.77 -> 455.77 unveraendert; canvasVisiblePx=403.8. [Auditor: uiux]","suggestion":"Sheet per Drag auf Peek-Hoehe kollabierbar machen (nur Tab-Zeile sichtbar); mindestens den taeuschenden Griff entfernen."},{"area":"Stapel-Panel Drag-Reorder","criterion":"Drag-Reorder ueber lange Listen (12+ Bloecke)","severity":"mittel","finding":"Beim Drag-Reorder scrollt die Stapel-Liste nicht automatisch: bei 26 Bloecken (1204px Inhalt, 588px sichtbar) sind Ziel-Zeilen ausserhalb des Viewports ohne vorheriges manuelles Scrollen unerreichbar.","evidence":"Handle 1,5s am unteren Listenrand gehalten -> scrollTop bleibt 0. startRowDrag (ui.js:236-311) ohne Auto-Scroll-Logik. Screenshot audit-interaktion-stack26.png. [Auditor: interaktion]","suggestion":"Standard-Pattern: scrollTop += v pro Frame solange der Pointer in der Randzone ist."},{"area":"Icons / Wiedererkennbarkeit","criterion":"Konsistenz & Wiedererkennbarkeit (Nielsen #4)","severity":"mittel","finding":"Icon-Doppelungen: alle 3 Kern-Kraefte (verzerren, fraktal, kaleidoskop) teilen dasselbe Icon ✦ — im Kraefte-Tab stehen 3 identische Kacheln nebeneinander; Symbol (Ding) und Spawner (Erzeuger) teilen ✧ und sind im Stapel nur ueber das Kind-Label unterscheidbar.","evidence":"Registry-Dump: verzerren/fraktal/kaleidoskop icon='✦', symbol+spawner icon='✧'; Screenshot audit-uiux-02-lib-tab3.png. [Auditor: uiux]","suggestion":"Eigene Icons je Kraft (Wirbel fuer Verzerren, geschachtelte Quadrate fuer Fraktal, Segmentkreis fuer Kaleidoskop); Spawner-Icon von Symbol trennen."},{"area":"zellautomat vs game0f1ife","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Die komplette Pattern-Bibliothek des Originals fehlt: 22 LifeWiki-Patterns (Glider, Pulsar, Glider Gun, Acorn, ...) mit Klick-Platzierung und Rotation — der Zellautomat kann nur mit Zufalls-Suppe starten, das praegendste GoL-Erlebnis (Glider setzen) fehlt. Ausserdem kein Zellen-Setzen per Klick, kein Neu-Saeen (nur Block loeschen+neu), kein Death-Symbol, keine Stats.","evidence":"game0f1ife.html:72-171 (patterns), :713-765 (Reset, Death-Symbole), :808-819 (Stats); blocks-leben.js:184-248: Schema nur tick/birth/surv/spont/death/symbol/cellSize/radius, init() nur golSeedSoup, kein hit/drag/doubleClick. [Auditor: vollstaendigkeit]","suggestion":"Select-Param 'Startmuster' (Suppe + Pattern-Liste) + 'Neu saeen'-Button; Death-Symbol-Param analog 'symbol'; Klick-Platzierung spaeter."},{"area":"fourier vs fourieous","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Vier der sechs Frequenz-Manipulationen fehlen (Freq-Potenz, Freq-Modulo, Invertieren, Nur-positiv) — nur Skala und Versatz sind portiert, im Code explizit als Reduktion vermerkt. Dazu fehlen: Sortierungen 'Frequenz absteigend' und 'Phase', Zufall wuerfelt nicht neu (fester Seed 42), Startpunkt-Slider, abschaltbares 'Luecken auslassen', Font-Auswahl, getrennte Stil-Regler.","evidence":"fourieous.html:1461-1492 (freqPower/Modulo/Invert/Absolute), :1397-1428, :1332-1348, :1494-1530; blocks-fourier.js:16-17 ('3 strongest manipulations'), :292-294 (manipFreq = freq*scale+offset), :37 SEED=42, :31 FONT konstant. [Auditor: vollstaendigkeit]","suggestion":"manipFreq um die vier Operationen erweitern (reine Formel-Arbeit, sigC ergaenzen); Sortierungen + startPhase sind billig; Stil-Feinregler optional."},{"area":"kurve vs circleheart","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Obere und untere Verformung sind nicht getrennt steuerbar (Original: deformationTop/Bottom mit eigenen Drag-Punkten; Port kennt nur symmetrischen morph = ausschliesslich 'both'). Dazu fehlen 3 von 8 Rundungsmodi (spiky/stepped/wave), der Snap/'Magnet' auf die didaktischen Punkte 0/r/-r und ein Deckkraft-Regler; Wellen-Params sind enger und Ganzzahl-gerundet.","evidence":"circleheart.html:33-35, :101-108, :254-259, :324-335, :596-631; blocks-dinge.js:210-216 (ein def, Kommentar 'defTop == defBottom'), :189-205 (5 Modi), :262-263, :302 (col hartkodiert). [Auditor: vollstaendigkeit]","suggestion":"Zweiter Slider 'Morph unten' (Default gekoppelt); die 3 Bend-Funktionen sind 1-Zeiler; Snap als Toggle, Deckkraft wie beim symbol-Block."},{"area":"fraktal-Kraft vs circleheart Fraktalmodus","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Der circleheart-Fraktalmodus ist deutlich maechtiger: innere UND aeussere Fraktale getrennt schaltbar, 'Fraktal-Verzoegerung' (jede Kopie zeigt einen aelteren Zustand via History-Ringpuffer — der charakteristische Zeitversatz-Effekt) fehlt komplett, Scale inkl. negativ und Count bis 1000 vs. 0..60 im Port.","evidence":"circleheart.html:648-689 + :66-98 (Toggles, fractalDelay, getHistoricalState, Ranges); blocks-kraefte.js:178-222 (anzahl 0..60, skalierung 0.5..1.5, eine Potenzreihe, kein Delay). [Auditor: vollstaendigkeit]","suggestion":"Schema-max anheben; Inner/Outer als Toggle-Paar (zweite Instanzliste mit 1/s); Delay als Engine-Feature (History pro Block) fuer Phase 2."},{"area":"ringschrift vs kreisausdingen","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Die 'echte' Pi-Faehigkeit fehlt: das Original berechnet Pi per Chudnovsky (Decimal.js) beliebig weit und haengt echte naechste Ziffern an; der Port hat 200 hartkodierte Stellen und kappt bei 600 Zeichen. Ausserdem fehlt der Anzahl-Parameter (Original wiederholt die Sequenz 0..20000-mal um den Kreis — im Port muss man Zeichen buchstaeblich n-mal tippen).","evidence":"kreisausdingen.html:260-344 (calculatePi, Cache, getNextPiDigit), :231 (count 0..20000); blocks-leben.js:349-364 (PI_TEXT 200 Stellen, RING_MAX_CHARS=600). [Auditor: vollstaendigkeit]","suggestion":"'Wiederholungen'-Slider (Sequenz x n); Decimal.js liegt bereits unter tools/tools/ — Pi-Modul als Worker anbinden, mindestens PI_TEXT verlaengern."},{"area":"verzerren vs co0rdinates","criterion":"Vollstaendigkeit vs Alt-Tool (Kernel-Vielfalt)","severity":"mittel","finding":"Von den co0rdinates-Freiheitsgraden fehlen Chirp (quadratische Phasen-Drift), Kernel-Breite/-Form (Port hat nur festen Smoothstep-Falloff) und vor allem das freie Grid-Punkt-Ziehen als Verzerrquelle — der Kern des Originals. Der Port bietet genau 3 feste Kernel (Twist/Welle/Sog) mit je einem Staerke-Wert.","evidence":"co0rdinates.html:127-131, :1007-1046, :2182-2229; blocks-kraefte.js:77-81 (nur 3 Arten), :39-46 (Falloff fix). [Auditor: vollstaendigkeit]","suggestion":"Chirp als vierte Art oder Zusatz-Param; 'Falloff'-Select (weich/linear/hart) waere billig; freies Ziehen = groesseres Feature (Punktliste als hidden param wie beim pfad)."},{"area":"tapete vs 1kaleidosk0p","criterion":"Vollstaendigkeit vs Alt-Tool","severity":"mittel","finding":"Das Dreieck-Clipping fehlt: das Original clippt jede Zelle aufs Dreieck und kachelt die Ebene nahtlos (echter Kaleidoskop-Tapeten-Look); der Port wendet nur die 6 Transformationen an — Kopien ueberlappen frei und laufen ueber Zellgrenzen. Zudem max. 16x16 Zellen gegenueber 80x80 im Original.","evidence":"1kaleidosk0p.html:59-65 (clipPath), :44-48 (40x40); blocks-muster.js:63-65 (max 8); grep 'clip' in engine.js = 0 Treffer. [Auditor: vollstaendigkeit]","suggestion":"ctx.clip mit Dreieckspfad vor dem Instanz-Stroke als Engine-Feature — oder als bewusste Stil-Abweichung dokumentieren."},{"area":"Stapel: Duplizieren + Umbenennen","criterion":"Grundfunktionen Block-Verwaltung","severity":"klein","finding":"Bloecke koennen weder dupliziert noch umbenannt werden: muehsam getunte Parameter-Sets muessen von Hand nachgebaut werden (gerade Moire braucht ZWEI fast identische Linienscharen), und mehrere Bloecke gleichen Typs heissen identisch ('Pfad'/'Pfad' — jeder Zeichnen-Strich einer mehr). block.name existiert im Datenmodell und wird serialisiert, hat aber keine UI.","evidence":"DOM-Scan Buttons auf dupli/kopie/copy und Rename-Code-grep: leer; Doppelklick auf .row-name erzeugt kein Input; engine.js:509/533 serialisiert/laedt name; Screenshot audit-uiux-m06-stack.png (zwei Zeilen 'DING Pfad'). [Auditoren: interaktion, vollstaendigkeit, uiux]","suggestion":"Duplizieren als drittes Row-Icon (deepClone params, Name + ' Kopie'); Umbenennen per Doppelklick auf den Namen; alternativ laufende Nummer ('Pfad 2')."},{"area":"Meta-Tool PNG-Export","criterion":"Grundfunktion Werk sichern","severity":"klein","finding":"Kein PNG-Export — das fertige Kunstwerk ist nur per Browser-Screenshot sicherbar (Spec: 'Screenshot reicht vorerst'), aber mobile Screenshots enthalten immer die UI, und mit einem Canvas ist toBlob+Download nahezu gratis.","evidence":"grep toDataURL/toBlob/export/download in ui.js/engine.js/0necanvas.html = 0 Treffer; Topbar nur Teilen+Vollbild (0necanvas.html:27-31). [Auditor: vollstaendigkeit]","suggestion":"Download-Button neben Teilen: einen Frame ohne Overlays in ein Offscreen-Canvas rendern."},{"area":"?s= Laden / Param-Validierung","criterion":"Robuste Fallbacks bei feindlichen Werten im JSON","severity":"klein","finding":"sc.load() uebernimmt Parameterwerte typungeprueft (nur Key-Whitelist): {size:'NaN', freq:null, x:1e9} laedt ohne Fehler, der Block ist aber unsichtbar und das Panel zeigt unbrauchbare Werte — kein Crash, aber stiller Kaputt-Zustand ohne Hinweis (die Kamera wird dagegen validiert).","evidence":"t4 'hostileValues': Szene laedt, lit=0, 0 Fehler; engine.js:538-543 (Copy ohne Typpruefung) vs. :548-553 (Kamera-Validierung). [Auditor: robustheit]","suggestion":"Je Schema-Eintrag Number()+isFinite coercen mit Schema-Default als Fallback, analog Kamera-Validierung."},{"area":"Zeichnen: Muell-Bloecke","criterion":"Sehr kurzer Tap/Wackler","severity":"klein","finding":"Ein Mini-Wackler von ~5px im Zeichnen-Modus hinterlaesst einen bleibenden, praktisch unsichtbaren 2-Punkte-Pfad als Muell-Block im Stapel (ein sauberer Tap ohne Bewegung wird korrekt verworfen).","evidence":"Drag (4,3)px -> Block bleibt {type:'pfad', pts:[-2,-2,2,1]}; finishDraw verwirft nur raw.length<6, keine Mindest-Ausdehnung (ui.js:850). [Auditor: interaktion]","suggestion":"Zusaetzlich Bounding-Box des Strichs pruefen (< 6 css-px in beiden Achsen -> verwerfen)."},{"area":"Verzerren-Werkzeug Blockwahl","criterion":"Mehrere verzerren-Bloecke: nachvollziehbare Drag-Zuweisung","severity":"klein","finding":"Die Blockwahl des Warp-Werkzeugs ignoriert die Klickposition: ohne Selektion bekommt immer der oberste verzerren-Block den Drag — auch bei Klick exakt auf das sichtbare Zentrum eines anderen; dessen Zentrum teleportiert dann zum Klickpunkt. Nachvollziehbar nur, weil der Gewinner sofort selektiert wird.","evidence":"Test: Klick bei Welt(0,-100) nahe A(cx-300) -> B(cx300) teleportiert auf (40,-100); resolveWarpBlock (ui.js:890-901) prueft nur selectedId, sonst obersten. [Auditor: interaktion]","suggestion":"Bei mehreren verzerren-Bloecken den mit dem naechsten Zentrum zum Klickpunkt waehlen; Selektion als Override behalten."},{"area":"Stapel-Logik: Einfuege-Position","criterion":"Mentales Modell (Nielsen #2)","severity":"klein","finding":"Neue Bausteine landen immer ganz oben — also UEBER allen Kraeften. Wer in der Default-Szene (mit 'Raum verzerren' oben) ein neues Ding hinzufuegt, bekommt ein unverzerrtes Ding und versteht ohne Ebenen-Vorwissen nicht warum.","evidence":"Nach Hinzufuegen von Ring-Schrift: Reihenfolge gitter,symbol,kurve,spawner,verzerren,ringschrift (Ring-Schrift oberhalb der Kraft, gemessen; Screenshot audit-uiux-04-after-add.png). [Auditor: uiux]","suggestion":"Neue Dinge/Erzeuger unterhalb der obersten zusammenhaengenden Kraft-Gruppe einfuegen — oder bewusst so lassen und im Kachel-Text erklaeren."},{"area":"Default-Optik spawner/textbaum","criterion":"Aesthetik der Defaults (jeder Block solo)","severity":"klein","finding":"2 von 17 Defaults wirken beim Solo-Hinzufuegen wie 'kaputt/leer': Spawner ist nach 2,5s fast unsichtbar (0,5% lit-Pixel — Rate 4/s, Groesse 10-28, Gluehen 6), Text-Baum sehr blass/duenn (0,45%). Vergleich: kurve 5,5%, strahlen 14,9%.","evidence":"Pixel-Sampling nach 2,5s: spawner 900/179520, textbaum 818 vs kurve 9841, strahlen 26709; Screenshots audit-uiux-def-spawner/-textbaum.png. [Auditor: uiux]","suggestion":"Spawner-Default auf 8/s, Groesse 16-48, Gluehen 10; Text-Baum-Startchance hoeher, damit in 2s ein sichtbarer Baum steht."},{"area":"Benennung / Einheiten","criterion":"Konsistenz der Benennung + Performance-Unabhaengigkeit (Nielsen #4)","severity":"klein","finding":"Gleiche Konzepte, verschiedene Namen: 'Strichstaerke' vs 'Linienstaerke', 'Zeichengroesse' vs 'Schriftgroesse'; Fourier hat ZWEI Groessen-Regler ohne erkennbaren Label-Unterschied. Gravierender: Strahlenbueschel-Rate ist 'Linien pro Frame' ohne dt — auf einem 30fps-Geraet wachsen Strahlen halb so schnell wie bei 60fps (Spawner '/s' und Zellautomat 'Schritte/s' sind korrekt zeitbasiert).","evidence":"schema-dump.json: strahlen.width='Strichstaerke' vs 5x 'Linienstaerke'; fourier fontSize+size (blocks-fourier.js:407-408); blocks-leben.js:283 'st.acc += clamp(p.perFrame,0,200)' ohne dt vs :214 'tick * dt'. [Auditor: uiux]","suggestion":"Einheitlich 'Linienstaerke'/'Zeichengroesse', Fourier-zweiter Regler = 'Skalierung'; strahlen auf 'Linien pro Sekunde' umstellen (st.acc += perSecond * dt)."},{"area":"Mobile Werkzeugleiste","criterion":"Wiedererkennung statt Erinnern (Nielsen #6)","severity":"klein","finding":"Auf Mobile sind die drei Werkzeuge nur kryptische Icons (✥ ≈ ✎) ohne Label; title-Tooltips sind auf Touch nutzlos — Bewegen/Verzerren/Zeichnen sind ohne Ausprobieren nicht unterscheidbar.","evidence":"Screenshot audit-uiux-m01-load.png; .tb-label wird <900px ausgeblendet (CSS). [Auditor: uiux]","suggestion":"Beim Werkzeugwechsel kurz einen Toast/Chip mit dem Namen einblenden ('Zeichnen'), wie es 'Link kopiert' schon vormacht."},{"area":"Sprache / Jargon","criterion":"Sprache der Nutzer (Nielsen #2), UI durchgaengig Deutsch","severity":"klein","finding":"Vereinzelter unerklaerter Jargon: 'TrueArc' ist der einzige englische UI-String (neben 'Geometrisch'/'Kreisbogen'), die Rundungs-Optionen 'Cos/Sin²' sind fuer Nicht-Mathematiker Rauschen, die Pille 'Instanz-Limit erreicht' erklaert weder Ursache noch Abhilfe, 'Spawner' ist englisch. Sonst ist die UI durchgehend deutsch.","evidence":"blocks-dinge.js:235 label 'TrueArc'; 0necanvas.html:28 Pill-Text; Registry-Label 'Spawner'; vollstaendiger grep ueber label/title/aria-label sonst deutsch. [Auditoren: uiux, konzept]","suggestion":"'TrueArc' -> 'Echter Bogen' (value-Key bleibt englisch); Pille z.B. 'Zu viele Kopien — Anzeige gekappt'."},{"area":"Kleinere Portierungs-Luecken (textpunkte, linienschar, textbaum, strahlen)","criterion":"Vollstaendigkeit vs Alt-Tools","severity":"klein","finding":"Restluecken je Block: textpunkte ohne Punktgroesse-Regler und Font-Auswahl, Dichte gilt fuers Gesamtwort statt pro Zeichen; linienschar hat endliche Segmente statt der unendlichen, viewport-fuellenden Linien des Originals (Moire endet sichtbar beim Zoomen); textbaum ohne 'Text rotieren'-Toggle (praegt den wuchernden Look) und ohne Metriken; strahlen-Cap 2000 vs unbegrenzt.","evidence":"musterpointfont.html:114-130 vs blocks-muster.js:216-247; moire...html:174-215 vs blocks-muster.js:298-316; n0thingsp1s.html:394-423 vs blocks-leben.js:561-617 (rot:0 fest); blocks-leben.js:255 RAY_MAX=2000. [Auditor: vollstaendigkeit]","suggestion":"Punktgroesse als Multiplikator (3 Zeilen), 'Unendlich'-Option fuer linienschar (Laenge aus view wie beim gitter), rot-Toggle beim textbaum (2 Zeilen)."},{"area":"Kamera-Zoom","criterion":"Ranges so unbegrenzt wie moeglich / 0=1=inf","severity":"idee","finding":"Zoom ist hart auf 0.05x-50x geklemmt (sauber und ohne Springen) — der Raum ist erfahrbar endlich, was in Spannung zum Unendlichkeits-Anspruch steht; zudem gibt es keinen tippbaren Zoom-Wert. Praktisch durch Float-Praezision/Performance motiviert, kein Defekt.","evidence":"ui.js:22-23 (ZOOM_MIN/MAX), :938; Test: 80x Wheel-In -> scale exakt 50, 160x Out -> exakt 0.05. [Auditoren: interaktion, konzept]","suggestion":"Grenzen um 1-2 Groessenordnungen weiten oder Zoom-Anzeige mit tippbarem Wert; Renormalisierung der Weltkoordinaten als Phase-2-Gedanke."},{"area":"Zeichnen-Feinheiten","criterion":"Einzelpunkt / Kontrollpunkt-Praezision","severity":"idee","finding":"Zwei Kleinigkeiten: (a) ein bewusster Tap im Zeichnen-Modus erzeugt nichts — ein Einzelpunkt als kleinstes gestisches Element ist nur ueber Bibliothek->Symbol erreichbar (zugleich verstaendlich als Muell-Schutz); (b) liegen zwei Kontrollpunkte naeher als ~22 Screen-px, verdecken die Punkt-Handles die Midpoint-Handles — Einfuegen auf dem Segment geht erst nach Hineinzoomen.","evidence":"ui.js:850 (raw.length<6 verworfen, Blockzahl unveraendert); blocks-dinge.js:536-554 (pt-Handles r=11 vor Midpoints r=9); auf frischem Pfad fuegt der Klick exakt den Mittelpunkt ein. [Auditor: interaktion]","suggestion":"Optional: Tap setzt kleinen Punkt-Pfad; Midpoints nur anzeigen/hitten wenn Segment lang genug."},{"area":"Phase-2-Luecken","criterion":"Nordstern-Vollstaendigkeit (in Spec explizit vertagt)","severity":"idee","finding":"Bewusst vertagte Luecken, gesammelt dokumentiert: Preset-Galerie (nur kuratierte ?s=-URLs — fast gratis) und Video-Export (Spec Phase 2), Spiralen-Physik (Slinky-Feder-Simulation des Originals, Spec 'Physik sprinky' Phase 2) sowie eine Trail-/Nachleuchten-Kraft (der Look vieler nicht-clearender Alt-Tools; im Pipeline-Modell schwer, pragmatisch als globaler Szenen-Param moeglich).","evidence":"grep preset/galerie/record/MediaRecorder/trail = 0 Treffer ausser fourier-internem trail-Param; Spec 2026-07-06-...design.md:108-111; blocks-muster.js:7-8 'helix geometry only, no physics'. [Auditor: vollstaendigkeit]","suggestion":"Presets zuerst (kuratierte ?s=-URLs); Nachleuchten als globaler Szenen-Param (fillRect mit Alpha statt clear)."},{"area":"Code-Hygiene","criterion":"Keine Duplikate/Reste im Auslieferungs-Code","severity":"idee","finding":"Sammelposten ohne Nutzer-Wirkung: clamp 6x, num 2x, PRNG 3 Varianten und Punkt-Decimation 2x ueber die Block-Dateien dupliziert (~100 Zeilen Sparpotential, IIFE-Isolation evtl. bewusst); Akzent-/Ink-Farbe als rgba-Strings in 6 Dateien wiederholt statt zentralisiert; Selbsttest-Block inkl. console.log lebt in der Prod-HTML (sauber hinter ?selftest=1 gated); vermutlich fehlendes Favicon als Quelle eines einmaligen, nicht reproduzierten 404.","evidence":"clamp: ui.js:39 + 5 Block-Files; mulberry32Next 2x + seededRandom; decimatePts/decimateTplPts; rgba(168,184,232,..) mehrfach (blocks-leben.js:38-41 u.a.); 0necanvas.html:94-120 (_selftest, console.log :119), kein <link rel=icon>. [Auditor: konzept]","suggestion":"Kleines Helper-Objekt auf window.OneCanvas (clamp, num, prng) + Farb-Konstanten in engine.js; Favicon-Link same-origin ergaenzen."},{"area":"Mobile Tastatur","criterion":"Eingabe-Ergonomie (headless nicht final verifizierbar)","severity":"idee","finding":"Wert-Eingaben und Zahnrad-Popover liegen im unteren Screen-Drittel (Input y=550-590, Popover bis y=793 von 844) — eine echte Soft-Tastatur (~300px) ueberdeckt genau diese Zone; ob der Browser das Feld zuverlaessig ins sichtbare visualViewport scrollt, ist headless nicht pruefbar. Tippen selbst funktioniert.","evidence":"BoundingRects M_VALUE_INPUT_POS {top:550, bottom:590, vh:844}, M_POPOVER {top:596, bottom:793}; Wert 333 uebernommen. [Auditor: uiux]","suggestion":"Einmal auf echtem Handy testen; falls verdeckt: Sheet bei Fokus nach oben schieben (visualViewport.resize-Listener)."},{"area":"Ring-Schrift Default-Text","criterion":"Editierbarkeit von Defaults","severity":"idee","finding":"Der Default-Text der Ring-Schrift ist eine 200-Zeichen-Pi-Sequenz in einem einzeiligen schmalen Textfeld — huebsch auf der Leinwand, im Panel praktisch nicht editierbar/ueberblickbar.","evidence":"schema-dump.json ringschrift.text (200 Zeichen); Screenshot audit-uiux-04-after-add.png (ueberlaufendes Eingabefeld). [Auditor: uiux]","suggestion":"Mehrzeiliges Textarea fuer lange Sequenzen oder Kurz-Presets (Pi, Fibonacci, eigener Text)."}],"passes":["Kern-Matrix sitzt: 50 von 52 Kraft-x-Inhalt-Zellen nachweislich kausal wirksam (Pixel-Diff mit Kontrolle, diffForce 3.611-660.688 px), Kraft-Reihenfolge zaehlt, Kraefte doppelt stapelbar, Toggle hinterlaesst keinen State-Muell, Instanz-Budget verhindert Explosionen im Normalfall — 0 Console-Errors ueber den gesamten Lauf [matrix].","Interaktion praezise: 12 von 17 Typen exakt draggable (Param-Delta == Pixel-Delta), Selektion/Panel-Sprung bei allen 17 Typen korrekt, Zeichnen mit RDP-Dezimation auch waehrend laufender Animation, Pfad-Kontrollpunkte ziehen/einfuegen/loeschen exakt, Stapel-Reorder/Auge/Loeschen korrekt, Zoom-Clamps ruhig ohne Springen [interaktion].","Robustheit stark: 0-Regel bei allen 109 Slidern einzeln UND gleichzeitig bestanden (kein Error, kein NaN, 60 fps), 40 von 42 Extremwert-Faellen crashfrei, Text-Params robust gegen Emoji/ZWJ/RTL/500 Zeichen, ?s=-Fallbacks bei kaputtem base64/JSON/unbekannten Typen sauber, kein Memory-Leak in 3 min Volllast, Resize/DPR pixelexakt [robustheit].","Portierungs-Kerne mathematisch exakt: Herz-Morph 1:1 aus circleheart, DFT/Epizykel inkl. Moore-Konturverfolgung aus fourieous, GoL-Regelwerk vollstaendig inkl. survMin=0-Sonderfall, Tapeten-Transformsatz und Hex-Gitter exakt wie 1kaleidosk0p, textpunkte-Pixel-Scan und textbaum-Mechanik komplett; strahlen und spawner uebertreffen ihre Originale teils (Position, Templates, Budget-Drossel) [vollstaendigkeit].","Konzept-Treue an der Oberflaeche: Stage-Hint '0 ≡ 1 ≡ ∞', Defaults tragen die Theorie (∞/0/1 in Symbol, Spawner, Textbaum, Fourier), Default-Szene deckungsgleich mit abgenommenem Mockup, Spawner-Vereinigung Ding=Erzeugnis im Code angekommen, alle 17 Blocktypen registriert und stimmig kategorisiert [konzept, uiux, vollstaendigkeit].","Konventionen eingehalten: UI durchgaengig Deutsch (einzige Ausnahme 'TrueArc' als Finding), exakt eine Akzentfarbe #a8b8e8 auf monochromem Dunkel, keine Emojis, strikt same-origin (kein einziges externes http(s)/url()/font), Serialisierungs-Vertrag sauber (v:1, kein block.state in der URL, unbekannte Typen mit warn uebersprungen) [konzept, uiux].","Mobile-Grundlagen solide: 390x844 ohne horizontalen Overflow, ALLE Tap-Targets >=40px, Touch-Zeichnen/-Tippen/-Popover funktionieren, Doppel-Tap-Punktloeschen in Chromium-Touch verifiziert; Soft-Range-Mechanik beim Tippen voll wirksam (0, 5000, -300 akzeptiert, Slider streckt sich) [uiux, konzept].","Feedback-Momente da, wo vorhanden, gut: Teilen-Toast + korrekte Clipboard-URL, Instanz-Limit-Pille zuverlaessig und dezent, neue Bloecke erscheinen zentriert und vorselektiert, Leer-Selektion sagt 'Kein Baustein angewaehlt', 15 von 17 Solo-Defaults sehen sofort gut aus [uiux, matrix]."]}
