managarten/apps/mukke/docs/VISUALIZER_CONCEPT.md
Till JS 2d7ca7e387 docs(mukke): add comprehensive visualizer system concept
Describes the architecture for an extensible music visualizer framework:
- Audio data layer with frequency, beat detection, and energy bands
- Registry system with discriminated union pattern for visualizer types
- 10+ planned built-in visualizations (spectrum, particles, 3D, etc.)
- Custom visualizer system with sandboxed code execution
- AI-powered code generation via mana-llm
- Fullscreen visualizer mode and community sharing
- Phased implementation roadmap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:34:42 +01:00

490 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Mukke Visualizer System Konzept
## Vision
Ein erweiterbares Visualisierungs-Framework, das:
- Viele eingebaute Visualisierungen mitbringt (Frequency Bars, Circular, Particles, Waveform, etc.)
- Nutzern ermöglicht, eigene Visualisierungen zu erstellen (Code-Editor oder KI-generiert)
- Visualisierungen als "Community Presets" teilbar macht
- Sich nahtlos in den bestehenden Player (FullPlayer, MiniPlayer, Fullscreen-Modus) integriert
---
## Architektur-Übersicht
```
┌─────────────────────────────────────────────────────────┐
│ Player UI │
│ FullPlayer │ MiniPlayer │ Fullscreen Visualizer │
└────────────────────────┬────────────────────────────────┘
┌────────────────────────▼────────────────────────────────┐
│ VisualizerRenderer.svelte │
│ Wählt den aktiven Renderer, übergibt AudioData + Config│
└────────────────────────┬────────────────────────────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Built-in │ │ Built-in │ │ Custom │
│ Renderer │ │ Renderer │ │ Renderer │
│ (Svelte) │ │ (Svelte) │ │ (Sandboxed)│
└────────────┘ └────────────┘ └────────────┘
│ │ │
└──────────────┼──────────────┘
┌─────────────────────────────────────────────────────────┐
│ Audio Analyzer (Singleton) │
│ AnalyserNode → frequencyData, timeDomainData, volume │
└─────────────────────────────────────────────────────────┘
```
---
## 1. Audio Data Layer
### Aktueller Stand
`src/lib/visualizer/analyzer.ts` liefert bereits:
- `getFrequencyData()``Uint8Array` (Frequenzbänder 0-255)
- `getFrequencyBinCount()` → Anzahl der Bins
### Erweiterung
```typescript
// src/lib/visualizer/audio-data.ts
export interface AudioData {
/** Frequency spectrum (0-255 per bin) */
frequency: Uint8Array;
/** Time domain waveform (-128 to 127, centered at 128) */
timeDomain: Uint8Array;
/** Number of frequency bins */
binCount: number;
/** Current RMS volume (0-1) */
volume: number;
/** Current bass energy (0-1) average of lowest 1/8 bins */
bass: number;
/** Current mid energy (0-1) */
mid: number;
/** Current high energy (0-1) */
high: number;
/** Beat detected this frame */
beat: boolean;
/** Current playback time in seconds */
currentTime: number;
/** Song duration in seconds */
duration: number;
/** Song BPM (if available) */
bpm: number | null;
}
```
Die `AudioData`-Struktur wird einmal pro Frame berechnet und an alle aktiven Renderer weitergegeben. So müssen Visualisierungen selbst keine Audio-Analyse machen.
---
## 2. Visualizer Registry
### Discriminated Union Pattern (wie Picture App Board Items)
```typescript
// src/lib/visualizer/types.ts
export interface VisualizerMeta {
id: string;
name: string;
description: string;
author: string;
thumbnail?: string; // Preview-Bild oder generiert
category: VisualizerCategory;
tags: string[];
version: string;
}
export type VisualizerCategory =
| 'spectrum' // Frequency-basiert (Bars, Circular)
| 'waveform' // Wellenform-basiert
| 'particles' // Partikel-Systeme
| 'geometric' // Geometrische Muster
| 'abstract' // Abstrakte/generative Kunst
| 'custom'; // User-created
export interface VisualizerConfig {
[key: string]: unknown;
}
export interface VisualizerDefinition<C extends VisualizerConfig = VisualizerConfig> {
meta: VisualizerMeta;
/** Default-Konfiguration */
defaultConfig: C;
/** JSON-Schema für die Config (für UI-Generierung) */
configSchema: ConfigSchema;
/** Rendering-Typ */
type: 'builtin' | 'custom-canvas' | 'custom-shader';
}
```
### Registry Store
```typescript
// src/lib/visualizer/registry.svelte.ts
function createVisualizerRegistry() {
let visualizers = $state<Map<string, VisualizerDefinition>>(new Map());
let activeId = $state<string>('frequency-bars');
let activeConfig = $state<VisualizerConfig>({});
return {
get all() { return [...visualizers.values()]; },
get active() { return visualizers.get(activeId) ?? null; },
get activeConfig() { return activeConfig; },
register(def: VisualizerDefinition) { ... },
unregister(id: string) { ... },
setActive(id: string) { ... },
updateConfig(patch: Partial<VisualizerConfig>) { ... },
getByCategory(cat: VisualizerCategory) { ... },
};
}
```
---
## 3. Built-in Visualisierungen
### Phase 1 (implementiert)
| ID | Name | Beschreibung |
|----|------|-------------|
| `frequency-bars` | Frequency Bars | Klassische EQ-Balken, konfigurierbar (Anzahl, Farbe, Mirror) |
### Phase 2 (Editor-Verbesserungen)
| ID | Name | Beschreibung |
|----|------|-------------|
| `beat-grid` | Beat Grid | BPM-basiertes Raster über Waveform |
| `energy-heatmap` | Energy Heatmap | Waveform eingefärbt nach Lautstärke |
### Phase 3 (Premium)
| ID | Name | Beschreibung |
|----|------|-------------|
| `circular-spectrum` | Circular Spectrum | Kreisförmig, Album-Art in der Mitte, Frequenz als Strahlen |
| `particles` | Particle Flow | Partikel reagieren auf Bass/Transients |
| `waveform-3d` | 3D Waveform | Scrollende 3D-Wellenform (Terrain-Stil) |
| `blob` | Reactive Blob | Organische Form, die zum Beat atmet |
| `bars-circular` | Circular Bars | Bars im Kreis angeordnet |
| `spectrum-wave` | Spectrum Wave | Smooth-kurvige Frequenzlinie |
| `stereo-field` | Stereo Field | L/R Panorama-Visualisierung |
| `kaleidoscope` | Kaleidoscope | Symmetrische Muster aus Frequenzdaten |
---
## 4. Rendering-Ansätze
### Option A: Svelte Components (Built-ins)
Jede Built-in-Visualisierung ist eine Svelte-Komponente mit standardisiertem Interface:
```svelte
<script lang="ts">
import type { AudioData, VisualizerConfig } from '$lib/visualizer/types';
interface Props {
audioData: AudioData;
config: VisualizerConfig;
width: number;
height: number;
}
let { audioData, config, width, height }: Props = $props();
// Rendering-Logik mit Canvas 2D, SVG, oder WebGL
</script>
<canvas bind:this={canvas} {width} {height}></canvas>
```
**Pro:** Volle Svelte-Reaktivität, Tree-Shakeable, einfach zu warten
**Contra:** Nicht dynamisch ladbar für User-Visualisierungen
### Option B: Canvas 2D Render Functions (Custom)
User-Visualisierungen als reine Render-Funktionen:
```typescript
interface CustomVisualizerFn {
setup?: (ctx: CanvasRenderingContext2D, width: number, height: number) => void;
render: (ctx: CanvasRenderingContext2D, data: AudioData, config: VisualizerConfig,
width: number, height: number) => void;
destroy?: () => void;
}
```
**Pro:** Einfach, sicher (nur Canvas-Zugriff), leicht per Code-Editor erstellbar
**Contra:** Nur 2D, kein DOM-Zugriff
### Option C: WebGL/Shader (Advanced)
GLSL Fragment Shaders für GPU-beschleunigte Visualisierungen:
```glsl
// User schreibt nur den Fragment Shader
uniform float u_time;
uniform float u_bass;
uniform float u_mid;
uniform float u_high;
uniform float u_volume;
uniform sampler2D u_frequency; // Frequenzdaten als Textur
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
// ... Shader-Code
gl_FragColor = vec4(color, 1.0);
}
```
**Pro:** GPU-beschleunigt, visuell beeindruckend, Shadertoy-kompatibel
**Contra:** Steile Lernkurve, schwer zu debuggen
### Empfehlung: Hybrid-Ansatz
| Typ | Technologie | Verwendung |
|-----|------------|------------|
| Built-in | Svelte + Canvas 2D | Alle mitgelieferten Visualisierungen |
| Custom Canvas | Sandboxed Canvas 2D Function | User-erstellte 2D-Visualisierungen |
| Custom Shader | WebGL Fragment Shader | User-erstellte GPU-Visualisierungen |
---
## 5. Custom Visualizer System
### User-Workflow
1. **Galerie öffnen** → Alle verfügbaren Visualisierungen als Grid mit Live-Preview
2. **"Create New"** → Code-Editor öffnet sich
3. **Template wählen** → Startercode für Canvas 2D oder Shader
4. **Code schreiben** → Live-Preview neben dem Editor
5. **KI-Assistent** → "Erstelle eine Visualisierung die..." → Code wird generiert
6. **Speichern** → In der persönlichen Bibliothek
7. **Teilen** → Als Community Preset veröffentlichen (optional)
### Code-Editor Integration
```
┌─────────────────────────────────────────────────────┐
│ ┌─────────────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │
│ │ Code Editor │ │ Live Preview │ │
│ │ (Monaco/CM6) │ │ (Canvas) │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ └─────────────────────┘ └──────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐│
│ │ Config Panel: [barCount: 32] [color: #ff0] ││
│ └──────────────────────────────────────────────────┘│
│ [💾 Save] [▶ Test with Audio] [🤖 Ask AI] [📤 Share]│
└─────────────────────────────────────────────────────┘
```
### Sandboxing (Sicherheit)
Custom Code läuft **nicht** direkt im Hauptthread:
```
Option 1: new Function() mit Whitelist
- Kein Zugriff auf window, document, fetch, etc.
- Nur ctx (Canvas), data (AudioData), config erreichbar
- Einfach, performant, leichte Einschränkungen
Option 2: Web Worker + OffscreenCanvas
- Code läuft in isoliertem Worker
- Rendert auf OffscreenCanvas, wird in Hauptthread übertragen
- Sicherer, aber komplexer und nicht alle Browser unterstützen OffscreenCanvas
Option 3: iframe Sandbox
- Maximale Isolation
- Overhead durch postMessage-Kommunikation
- Overkill für Canvas-Rendering
```
**Empfehlung:** Option 1 (`new Function()`) für Canvas 2D, direkte WebGL-Ausführung für Shaders (Shader-Code ist von Natur aus sandboxed auf der GPU).
### Datenbank-Schema
```typescript
// Erweiterung der DB (Backend)
visualizers: {
id: uuid,
userId: uuid,
meta: jsonb, // VisualizerMeta
config: jsonb, // Default VisualizerConfig
configSchema: jsonb, // JSON Schema für Config-UI
code: text, // Render-Function oder Shader-Code
type: enum('canvas-2d', 'shader'),
isPublic: boolean,
likes: integer,
createdAt: timestamp,
updatedAt: timestamp,
}
```
---
## 6. Visualizer Galerie UI
```
┌─────────────────────────────────────────────────────────┐
│ 🎵 Visualisierungen [+ Create New] │
│ │
│ [All] [Spectrum] [Waveform] [Particles] [Community] │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ▓▓▓▓▓▓▓▓ │ │ ◉ ))) ) │ │ · · · · │ │
│ │ ▓▓▓▓▓▓▓▓ │ │ ◉ )))) ) │ │· · · · │ │
│ │ Freq Bars │ │ Circular │ │ Particles │ │
│ │ ★ Built-in│ │ ★ Built-in│ │ ★ Built-in│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ~~~~~~~~ │ │ ╱╲╱╲╱╲╱╲ │ │ ▒▒▒▒▒▒▒▒ │ │
│ │ ~~~~~~~~ │ │ ╱╲╱╲╱╲╱╲ │ │ ▒▒▒▒▒▒▒▒ │ │
│ │ Waveform │ │ Kaleido │ │ My Custom │ │
│ │ ★ Built-in│ │ ★ Built-in│ │ 👤 You │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
```
### Config Panel (pro Visualisierung)
Jede Visualisierung definiert ein `configSchema`, aus dem automatisch UI generiert wird:
```typescript
const configSchema: ConfigSchema = {
barCount: { type: 'range', min: 8, max: 128, step: 1, label: 'Bar Count' },
color: { type: 'color', label: 'Color' },
mirror: { type: 'toggle', label: 'Mirror' },
sensitivity: { type: 'range', min: 0, max: 2, step: 0.1, label: 'Sensitivity' },
colorMode: { type: 'select', options: ['gradient', 'solid', 'rainbow'], label: 'Color Mode' },
};
```
---
## 7. KI-Integration
### Prompt-basierte Erstellung
User beschreibt die gewünschte Visualisierung in natürlicher Sprache:
> "Erstelle einen Visualizer der wie Nordlichter aussieht.
> Die Farben sollen zwischen Grün und Lila wechseln, basierend auf den Bässen."
→ KI generiert Canvas 2D oder Shader Code
→ Live-Preview zeigt das Ergebnis sofort
→ User kann iterieren ("mach die Bewegung schneller", "füge Sterne hinzu")
### System Prompt für KI
```
Du bist ein Musik-Visualisierungs-Experte. Generiere eine render()-Funktion:
function render(ctx, data, config, width, height) {
// ctx: CanvasRenderingContext2D
// data: { frequency, timeDomain, volume, bass, mid, high, beat, currentTime, bpm }
// config: User-konfigurierbare Werte
// width, height: Canvas-Dimensionen
}
Regeln:
- Lösche den Canvas mit ctx.clearRect(0, 0, width, height)
- Nutze data.frequency (Uint8Array, 0-255) für Spektrumdaten
- data.bass/mid/high sind normalisiert (0-1)
- data.beat ist true wenn ein Beat erkannt wurde
- Halte den Code performant (60fps)
- Gib auch ein configSchema-Objekt zurück
```
### Integration über mana-llm Service
```
User Input → mana-llm → Code generiert → Sandbox → Live Preview
```
Nutzt den bestehenden `services/mana-llm` als LLM-Abstraktionsschicht.
---
## 8. Fullscreen Visualizer Mode
Neuer Fullscreen-Modus für immersive Erfahrung:
- Visualisierung füllt den gesamten Bildschirm
- Minimale Transport-Controls (transparent overlay, auto-hide)
- Song-Info eingeblendet bei Track-Wechsel (fade in/out)
- Keyboard-Shortcuts (Space = Play/Pause, Esc = Exit, N = Next Viz)
- Screensaver-Modus: Wechselt automatisch zwischen Visualisierungen
---
## 9. Datei-Struktur (geplant)
```
src/lib/visualizer/
├── analyzer.ts # Audio Analyzer (existiert)
├── audio-data.ts # AudioData Interface + Berechnung
├── types.ts # Alle Type-Definitionen
├── registry.svelte.ts # Visualizer Registry Store
├── sandbox.ts # Custom Code Sandboxing
├── FrequencyBars.svelte # Built-in (existiert)
├── renderers/
│ ├── CircularSpectrum.svelte # Kreisförmiges Spektrum
│ ├── ParticleFlow.svelte # Partikel-System
│ ├── Waveform3D.svelte # 3D Waveform
│ ├── ReactiveBlob.svelte # Organische Form
│ ├── CircularBars.svelte # Bars im Kreis
│ ├── SpectrumWave.svelte # Glatte Frequenzkurve
│ ├── StereoField.svelte # Stereo-Analyse
│ └── Kaleidoscope.svelte # Kaleidoskop-Muster
├── components/
│ ├── VisualizerRenderer.svelte # Router: wählt aktiven Renderer
│ ├── VisualizerGallery.svelte # Galerie-Ansicht
│ ├── VisualizerConfig.svelte # Auto-generiertes Config-Panel
│ ├── VisualizerEditor.svelte # Code-Editor für Custom Viz
│ ├── VisualizerPreview.svelte # Live-Preview Canvas
│ └── FullscreenVisualizer.svelte# Fullscreen-Modus
└── templates/
├── canvas-2d-starter.ts # Template für Canvas 2D
└── shader-starter.glsl # Template für GLSL Shader
```
---
## 10. Implementierungs-Reihenfolge
| Phase | Was | Aufwand |
|-------|-----|---------|
| **1** ✅ | Frequency Bars + Analyzer (done) | — |
| **2** | AudioData erweitern (bass/mid/high/beat), Registry Store, VisualizerRenderer | S |
| **3** | 3-4 weitere Built-in Renderer (Circular, Particles, Blob, Wave) | M |
| **4** | Galerie UI + Config Panel + Fullscreen Mode | M |
| **5** | Custom Visualizer: Sandbox + Code-Editor + Templates | L |
| **6** | KI-Integration (mana-llm) für Code-Generierung | M |
| **7** | Community: Teilen, Likes, öffentliche Galerie | L |
| **8** | Backend: visualizers-Tabelle, CRUD API, User-Presets | M |
---
## 11. Technologie-Entscheidungen
| Bereich | Entscheidung | Begründung |
|---------|-------------|------------|
| Built-in Rendering | Canvas 2D | Performant, einfach, kein Dep overhead |
| GPU-Effekte | Raw WebGL (kein Three.js) | Vermeidet ~150kb Dependency für Shader-only |
| Code Editor | CodeMirror 6 | Leichtgewichtig, Svelte-freundlich, besser als Monaco für embedded |
| Sandboxing | `new Function()` mit Whitelist | Guter Kompromiss aus Sicherheit und Performance |
| State | Svelte 5 Runes Store | Konsistent mit Rest der App |
| Persistenz | JSONB in PostgreSQL | Flexibel, keine Migrationen bei Config-Änderungen |
| KI | mana-llm Service | Bereits vorhanden, LLM-agnostisch |