2026-05-25-shadows-geb-mode-design.md

docs/superpowers/specs/2026-05-25-shadows-geb-mode-design.md

shadows.html — GEB-Modus (Design)

Datum: 2026-05-25 Status: Design, vor Implementierung Datei: shadows.html (0nefinity)

Ziel

shadows.html um einen GEB-Modus ergänzen (Gödel · Escher · Bach / Hofstadter-Cover). Heute zeigt die Seite eine Kugel, die per Raymarching drei Schatten wirft: 0 (Seite), 1 (Rückwand), (Boden). Der GEB-Modus erweitert das auf zwei Kugeln und fünf Lampen, analog zu den zwei Trip-Lets auf dem GEB-Cover.

Der bestehende Ein-Kugel-Modus bleibt unverändert der Default. GEB ist additiv (Toggle).

Hintergrund: das Cover-Prinzip

Auf dem echten GEB-Cover stehen zwei Holz-Trip-Lets in einem Raum, gestapelt auf der senkrechten Achse. Eine einzige Lampe von oben durchleuchtet beide und erzeugt einen geteilten Schatten unten (B) — möglich, weil der Querschnitt beider Klötze entlang dieser Achse identisch ist. Die beiden seitlichen Buchstaben (G, E) sind zwischen den Klötzen vertauscht und brauchen je eigene Lampen.

Übertragen:

Cover shadows
B (geteilt, unten, 1 Lampe) (geteilt, Boden, 1 Lampe)
G / E (pro Klotz getauscht) 0 / 1 (pro Kugel getauscht)
Klotz 1: G·E·B Kugel 1: 0·1·∞
Klotz 2: E·G·B Kugel 2: 1·0·∞

Daraus: 5 Lampen = 1× ∞-Boden (geteilt) + 2× Wand für Kugel 1 + 2× Wand für Kugel 2.

Aktuelle Architektur (Ist)

Design (Modus A: Toggle in shadows.html)

Kugel 2: fest an Kugel 1 gekoppelt

Entscheidung: Kugel 2 ist kein frei konfigurierbares Objekt, sondern spiegelt Kugel 1:

Vorteile: weniger UI, ∞-Teilung garantiert, Cover-treu, wenige Fehlerquellen. Bei dualMode=off existiert Kugel 2 nicht.

Lampen (5 im GEB-Modus)

Lampe Achse Zweck wirkt auf
∞-Boden (geteilt) Y, von oben wirft beide Kugeln (breiter Kegel → ein geteilter Schatten)
Kugel1-back Z wirft 1 (Kugel1.right) nur Kugel 1
Kugel1-side X wirft 0 (Kugel1.left) nur Kugel 1
Kugel2-back Z wirft 0 (Kugel2.right) nur Kugel 2
Kugel2-side X wirft 1 (Kugel2.left) nur Kugel 2

Im Ein-Kugel-Modus bleibt es bei 3 Lampen (floor/back/side), unverändert.

Datenstruktur-Änderungen

Schatten-Isolation pro Kugel

Damit Kugel-1-1 und Kugel-2-0 auf der Rückwand nicht überlappen/mischen:

  1. Primär (geometrisch): Kugeln vertikal versetzt + enge Spot-Kegel (15°) je auf das Kugel-Zentrum gezielt → Schatten landen in verschiedenen Wandhöhen, getrennt. Genau wie die einzeln gezielten Cover-Lampen.
  2. Fallback (falls Streuschatten): THREE.js-Layers — Kugel 1 + ihre 2 Wand-Lampen auf Layer A, Kugel 2 + ihre 2 auf Layer B, ∞-Lampe auf beide. Im Plan-Stadium prüfen, ob THREE.js Shadow-Mapping Layers respektiert; sonst per Spot-Range/Frustum eng begrenzen.

Entscheid im Plan-Stadium nach visuellem Test.

UI

URL-State

Verifikation (Self-Test-Plan)

Nach Implementierung via Playwright gegen dev.0nefinity.love/shadows (Test-User vorhanden):

  1. Default-Load: 1 Kugel, 3 Lampen, Schatten 0·1·∞ — wie vorher (Regression).
  2. GEB-Toggle an: 2 Kugeln sichtbar, gestapelt; 5 Lampen-Toggles da.
  3. Boden zeigt einen -Schatten (nicht zwei, nicht überlappend).
  4. Rückwand/Seitenwand: Kugel1 1/0, Kugel2 0/1 — getrennt lesbar, kein Mischmasch.
  5. Einzelne Lampen-Toggles schalten den jeweiligen Schatten korrekt.
  6. URL ?geb=1... reproduziert den Zustand (Reload + Back-Button).
  7. Mobile-Check: Tap-Targets ≥40px, Panel-Overflow, kein Hover-only.
  8. Dark-Mode korrekt (meta.css-Variablen).
  9. Performance auf schwacher Hardware: 5 Shadow-Maps + 2 raymarching-Kugeln — FPS prüfen, ggf. shadowMapSize defaulten.

Kein „fertig"-Claim ohne Playwright-Verify + Screenshot/DOM/Network.

Außerhalb Scope (YAGNI)

Offene Punkte fürs Plan-Stadium