015 - Parameter Visualization
"Watch how an artwork thinks"
Das Visualization-Modul ermöglicht es, Parameter eines Artworks als kreative Datenquelle zu nutzen. Jede Parameter-Änderung wird in Echtzeit visualisiert - als radiales Mandala oder als lebendige Kreatur.
Konzept
Die Idee: Parameter sind nicht nur Einstellungen, sondern Daten die man visualisieren kann:
- ParameterHistory: Sammelt Parameter-Werte über Zeit (Ring-Buffer)
- ParameterVisualizer: Abstrakte Basis-Klasse für Visualisierungen
- ParameterMandala: Radiale Darstellung - jeder Parameter = ein Strahl
- ParameterCreature: Lebendes Wesen das auf Parameter reagiert
- ParameterMapper: Auto-Mapping von Parametern zu Visualizer-Slots
- ParameterObserver: Sockets Bridge für Remote-Parameter
Artwork → Sockets → ParameterObserver → ParameterHistory → ParameterMapper → Visualizer → CanvasSchnellstart
import {
ParameterMandala,
ParameterCreature,
ParameterObserver
} from '@carstennichte/cc-toolbox';
// Option 1: Eigene Parameter visualisieren
const mandala = new ParameterMandala({ animated: true });
mandala.record(parameter);
mandala.update(deltaTime);
mandala.draw(ctx, width, height);
// Option 2: Parameter eines anderen Artworks beobachten
const observer = new ParameterObserver({
serverUrl: 'http://localhost:4000',
autoConnect: true,
});
const creature = new ParameterCreature({ personality: 'curious' });
creature.connectHistory(observer.getHistory());ParameterHistory
Sammelt numerische Parameter-Werte als Zeitreihen:
import { ParameterHistory } from '@carstennichte/cc-toolbox';
const history = new ParameterHistory({
maxLength: 200, // Max. Datenpunkte pro Parameter
sampleInterval: 50, // Min. ms zwischen Samples
excludePaths: ['tweakpane', 'debug'],
numericOnly: true, // Nur numerische Werte tracken
});
// In der Update-Loop
history.record(parameter);
// Statistiken abrufen
const stats = history.getStats('demo.wave1');
// → { min: 0.1, max: 0.9, avg: 0.5, current: 0.7, trend: 'rising', activity: 0.8 }
// Alle Parameter
const allStats = history.getAllStats();
// Gesamt-Aktivität (0-1)
const activity = history.getTotalActivity();
// Aktivster Parameter
const mostActive = history.getMostActiveParameter();
// 🧠 Smart Filter: Top N aktivste Parameter (nach Aktivität sortiert)
const topActive = history.getTopActiveStats(16, 0.005);
// → Die 16 aktivsten Parameter mit mind. 0.5% Aktivität
// Nur die Pfade der aktivsten Parameter
const activePaths = history.getTopActivePaths(16, 0.005);
// → ['entity.stats.centerX', 'entity.stats.avgSpeed', ...]Filter-Optionen
const history = new ParameterHistory({
includePaths: ['physics', 'motion'], // NUR diese Pfade
excludePaths: ['tweakpane', 'internal'],
numericOnly: true, // Strings, Booleans etc. ignorieren
});ParameterMandala
Radiale Visualisierung aller Parameter:
import { ParameterMandala } from '@carstennichte/cc-toolbox';
const mandala = new ParameterMandala({
// Filter
excludePaths: ['tweakpane', 'artwork.canvas.html'],
// Aussehen
innerRadius: 30,
showLabels: true,
showGrid: true,
showFill: true,
showConnections: true,
showDots: true,
// Animation
animated: true,
animationSpeed: 0.1,
// Farben für Trends
risingColor: '#4ade80', // Grün - steigend
fallingColor: '#f87171', // Rot - fallend
stableColor: '#94a3b8', // Grau - stabil
// 🧠 Smart Filter - zeigt nur aktive Parameter
smartFilter: true, // Aktiviert intelligente Filterung
maxParameters: 16, // Max. angezeigte Parameter
minActivity: 0.005, // Min. 0.5% Aktivität erforderlich
// 🫧 Bulge Effect - 3D-Kuppel/Dome-Effekt
showBulge: true, // Aktiviert den Bulge-Effekt
bulgeIntensity: 0.5, // Stärke (0-2)
bulgeSource: 'activity', // 'activity' | 'value' | 'combined'
});
// In der Update-Loop
mandala.record(parameter);
mandala.update(deltaTime);
mandala.draw(ctx, width, height);Aufbau
- Jeder Parameter wird als Strahl vom Zentrum dargestellt
- Die Länge entspricht dem normalisierten Wert (0-1)
- Die Farbe zeigt den Trend (steigend/fallend/stabil)
- Die Verbindungslinien formen ein organisches Polygon
- Das Zentrum pulsiert mit der Gesamt-Aktivität
🧠 Smart Filter
Ohne Smart Filter werden alle Parameter visualisiert - bei komplexen Artworks können das 100+ Parameter sein. Der Smart Filter zeigt nur die aktivsten Parameter:
const mandala = new ParameterMandala({
smartFilter: true, // Aktiviert Smart Filtering
maxParameters: 16, // Zeigt max. 16 Parameter
minActivity: 0.005, // Min. 0.5% Änderungsrate
});Wie es funktioniert:
ParameterHistorytrackt die Aktivität jedes Parameters- Aktivität = wie oft ändert sich der Wert im Verhältnis zu Samples
getTopActiveStats(maxCount, minActivity)sortiert nach Aktivität- Nur Parameter über dem Schwellwert werden angezeigt
Typische Anwendung: Ein Entities-Artwork sendet 164 Parameter, aber nur Entity-Positionen (e0_x, e0_y, ...) und Statistiken (avgSpeed, centerX) ändern sich. Smart Filter zeigt nur diese ~16 relevanten Parameter.
🫧 Bulge Effect
Der Bulge Effect erzeugt einen 3D-Kuppel/Dome-Effekt, bei dem innere Kreise stärker skaliert werden als äußere:
const mandala = new ParameterMandala({
showBulge: true, // Aktiviert den Effekt
bulgeIntensity: 0.5, // Stärke (0=aus, 2=extrem)
bulgeSource: 'activity', // Was steuert die Skalierung?
});Bulge Sources:
| Source | Beschreibung |
|---|---|
activity |
Aktive Parameter = größerer Bulge |
value |
Hohe Werte = größerer Bulge |
combined |
Mix aus Activity und Value |
Formel: scale = 1 + intensity * (1 - circleIndex / totalCircles)²
Ergebnis: Innere Kreise wölben sich nach außen wie eine Kuppel oder Blase.
ParameterCreature
Ein lebendes Wesen das auf Parameter reagiert:
import { ParameterCreature } from '@carstennichte/cc-toolbox';
const creature = new ParameterCreature({
// Persönlichkeit
personality: 'curious', // 'calm' | 'energetic' | 'curious' | 'sleepy'
// Aussehen
baseSize: 60,
showEyes: true,
tentacleLength: 80,
bodyShape: 'organic', // 'blob' | 'circle' | 'organic'
// Farben
warmColor: '#f97316', // Orange (aktiv)
coldColor: '#3b82f6', // Blau (passiv)
});
// In der Update-Loop
creature.record(parameter);
creature.update(deltaTime);
creature.draw(ctx, width, height);Verhalten
- Körpergröße: Wächst mit Gesamt-Aktivität
- Herzschlag: Pulsiert mit dem aktivsten Parameter
- Farbe: Warm (aktiv) ↔ Kalt (passiv)
- Augen: Folgen der Aktivität, blinzeln bei großen Änderungen
- Tentakel: Repräsentieren Parameter-Gruppen
- Mund: Lächelt bei hoher Aktivität
Persönlichkeiten
| Persönlichkeit | Verhalten |
|---|---|
calm |
Langsame, sanfte Bewegungen |
energetic |
Schnelle, lebhafte Reaktionen |
curious |
Mittlere Geschwindigkeit, folgt Änderungen |
sleepy |
Sehr langsam, schläft bei niedriger Aktivität |
ParameterMapper - Auto-Mapping
Der ParameterMapper ordnet automatisch Parameter zu Visualizer-Slots zu. Er analysiert welche Parameter interessant sind (sich ändern) und matcht sie semantisch.
Grundlegende Verwendung
import { ParameterMapper } from '@carstennichte/cc-toolbox';
const mapper = new ParameterMapper();
// Parameter tracken (jedes Frame aufrufen)
mapper.trackActivity(parameter);
// Nach einigen Frames: Auto-Mapping für Mandala abrufen
const mappedValues = mapper.map(parameter, 'mandala');
// → { 'rings[0]': 0.73, 'rings[1]': 0.45, 'rotation': 127, ... }Auto-Mapping Strategien
const mapper = new ParameterMapper({
mappings: {
mandala: {
enabled: true,
autoMap: {
enabled: true,
strategy: 'hybrid', // 'by-activity' | 'by-name' | 'by-type' | 'hybrid'
maxSlots: 8,
},
},
},
});| Strategie | Beschreibung |
|---|---|
by-activity |
Priorisiert Parameter die sich häufig ändern |
by-name |
Matcht Namen semantisch (z.B. entity.count → tentacles.count) |
by-type |
Matcht nach Wertebereich (0-1, 0-360, etc.) |
hybrid |
Kombiniert alle Strategien (empfohlen) |
Semantische Gruppen
Der Mapper kennt semantische Wortgruppen für intelligentes Matching:
// Eingebaute Gruppen (SEMANTIC_GROUPS):
// - size: size, scale, radius, width, height, count, amount
// - motion: speed, velocity, rate, frequency, acceleration
// - rotation: rotation, angle, direction, heading
// - color: hue, saturation, brightness, color, tint
// - intensity: intensity, strength, force, power, amplitude
// - noise: noise, random, chaos, turbulence
// - entity: entity, particle, agent, item
// - grid: grid, cell, row, column, tileBeispiel: entity_manager.entityCount wird automatisch zu creature.tentacles.count gemappt, weil beide zur Gruppe size/count gehören.
Kernel-Parameter
Diese Standard-Parameter existieren in jedem Artwork und werden vom Auto-Mapper ignoriert (da sie für Visualisierung uninteressant sind):
// Automatisch gefiltert:
// - artwork.canvas.size.width/height
// - artwork.canvas.html.*
// - artwork.animation.timeStamp/deltaTime
// - artwork.meta.*
// - tweakpane.*Manuelles Mapping
// Eigenes Mapping hinzufügen
mapper.setMapping('mandala', {
target: 'rings[0]',
source: 'myCustom.value',
transforms: [
{ type: 'normalize', min: 0, max: 100 },
{ type: 'smooth', smoothing: 0.3 },
],
});
// Mapping entfernen
mapper.removeMapping('mandala', 'rings[0]');Transform-Pipeline
Werte können transformiert werden:
const slot = {
target: 'rotation',
source: 'physics.angle',
transforms: [
{ type: 'normalize', min: 0, max: 360 }, // Auf 0-1 normalisieren
{ type: 'scale', factor: 2, offset: 0.5 }, // Skalieren
{ type: 'smooth', smoothing: 0.2 }, // Glätten
{ type: 'clamp', min: 0, max: 1 }, // Begrenzen
],
};| Transform | Beschreibung |
|---|---|
passthrough |
Keine Änderung |
normalize |
Auf 0-1 normalisieren (min/max angeben) |
scale |
Mit Faktor multiplizieren, Offset addieren |
invert |
1 - value |
clamp |
Auf Bereich begrenzen |
smooth |
Exponentielles Glätten |
threshold |
Binär: 0 oder 1 |
modulo |
Modulo-Operation |
abs |
Absolutwert |
Debug-Ausgabe
// Alle getrackten Parameter mit Aktivität
const info = mapper.getActivityInfo();
// → [{ path: 'demo.wave1', activity: 0.87, range: { min: 0, max: 1 }, ... }, ...]
// Debug-Summary
console.log(mapper.getDebugSummary('mandala'));
// === ParameterMapper Debug ===
// Total tracked params: 24
// Sample count: 500
//
// Top 10 Active Parameters:
// demo.wave1: activity=87% range=[0.00 - 1.00]
// demo.pulse: activity=65% range=[0.00 - 1.00]
// ...
//
// Slot Assignments for 'mandala':
// rings[0] ← demo.wave1 (auto)
// rings[1] ← demo.pulse (auto)
// ...Config speichern/laden
// Als JSON exportieren
const json = mapper.toJSON();
localStorage.setItem('my-mapping', json);
// Aus JSON laden
const loaded = ParameterMapper.fromJSON(json);ParameterObserver - Sockets Bridge
Der ParameterObserver empfängt Parameter von anderen Artworks über Sockets:
import { ParameterObserver, ParameterMandala } from '@carstennichte/cc-toolbox';
const observer = new ParameterObserver({
serverUrl: 'http://localhost:4000',
targetArtwork: 'item-cc-entities', // Optional: spezifisches Artwork
autoConnect: true,
debug: false,
});
// Visualizer mit Observer-History verbinden
const mandala = new ParameterMandala();
mandala.connectHistory(observer.getHistory());
// Event-Handler
observer.onConnection((connected) => {
console.log(connected ? 'Connected!' : 'Disconnected');
});
observer.onUpdate((params, artworkId, timestamp) => {
console.log(`Received ${Object.keys(params).length} params from ${artworkId}`);
});
// Manuelle Kontrolle
await observer.connect();
observer.disconnect();
observer.requestParameterDump(); // Parameter sofort anfordernSockets Integration
Visualisiere Parameter eines anderen Artworks:
import { ParameterHistory, ParameterMandala } from '@carstennichte/cc-toolbox';
import { io } from 'socket.io-client';
// Shared History
const sharedHistory = new ParameterHistory({ maxLength: 300 });
// Visualizer mit externer History
const mandala = new ParameterMandala();
mandala.connectHistory(sharedHistory);
// Sockets Connection
const socket = io('http://localhost:4000');
socket.emit('join-room', 'artwork:item-cc-softbodies');
socket.on('parameter-dump', (payload) => {
sharedHistory.record(payload.data || payload);
});
// Draw Loop
function animate() {
mandala.update(deltaTime);
mandala.draw(ctx, width, height);
requestAnimationFrame(animate);
}RoomSelector - Dynamische Room/Artwork-Auswahl
Der RoomSelector ist eine wiederverwendbare UI-Komponente für die Auswahl von Sockets Rooms und Artworks:
import { RoomSelector, type RoomInfo, type ArtworkInfo } from '@carstennichte/cc-toolbox';
const roomSelector = new RoomSelector({
serverUrl: 'http://localhost:4000',
autoConnect: true, // Auto-connect beim Start
autoRefreshInterval: 5000, // Auto-refresh alle 5s
subscribeToChanges: true, // Updates bei Join/Leave
roomPrefix: 'artwork:', // Nur Artwork-Rooms anzeigen
});Tweakpane Integration
// Fügt komplettes UI zu Tweakpane hinzu
roomSelector.addToPane(pane, {
onRoomSelected: (room: RoomInfo | null) => {
console.log('Room selected:', room?.name);
},
onArtworkSelected: (artwork: ArtworkInfo | null, room: RoomInfo | null) => {
if (artwork && room) {
connectToArtwork(room, artwork);
}
},
onConnectionChange: (connected: boolean) => {
console.log('Connection:', connected);
},
onRoomsRefreshed: (rooms: RoomInfo[]) => {
console.log(`Found ${rooms.length} rooms`);
},
});Manuelle Verwendung
// Connect
await roomSelector.connect();
// Rooms abrufen
const rooms = await roomSelector.refreshRooms();
console.log(rooms);
// → [{ name: 'artwork:item-001', memberCount: 2, artworks: [...] }, ...]
// Room auswählen
roomSelector.selectRoom('artwork:item-001');
// Artwork auswählen
roomSelector.selectArtwork('item-001');
// Status
console.log(roomSelector.getSelectedRoom());
console.log(roomSelector.getSelectedArtwork());Refresh-Button (Wiederverwendbar)
Der RoomSelector enthält einen wiederverwendbaren Refresh-Button-Pattern:
// Statische Methode für eigene Refresh-Buttons
RoomSelector.createRefreshButton(folder, '🔄 Refresh Data', async () => {
const data = await fetchMyData();
updateUI(data);
});Presets - Laden & Speichern
// Preset speichern
roomSelector.savePreset('Mein Setup');
// Letztes Preset laden
roomSelector.loadPreset();
// Alle Presets abrufen
const presets = roomSelector.loadPresets();
// Presets löschen
roomSelector.clearPresets();Exhibition Mode
Für Ausstellungen gibt es einen automatischen Setup-Modus über URL-Parameter:
URL-Parameter
index.html?sketch=observer
&exhibitionId=mosaik-2027
&serverUrl=http://192.168.1.100:4000
&room=artwork:item-cc-softbodies
&artwork=item-cc-softbodies
&autoConnect=true
&hideControls=trueIm Sketch verwenden
const roomSelector = new RoomSelector({
serverUrl: 'http://localhost:4000',
});
// Automatisch URL-Parameter anwenden
if (roomSelector.initFromURL()) {
console.log('Exhibition mode: Auto-connecting...');
}Exhibition Config Datei
Eine exhibition.config.json im Projekt-Root definiert die Exhibition-Einstellungen:
{
"name": "Parameter Visualization Exhibition",
"server": {
"url": "http://192.168.1.100:4000",
"fallbackUrls": ["http://localhost:4000"]
},
"observers": [
{
"id": "mandala-observer",
"visualizer": "mandala",
"autoConnect": true,
"targetRoom": "artwork:item-cc-softbodies",
"url": "index.html?sketch=observer&exhibitionId=param-viz&..."
}
],
"display": {
"fullscreen": true,
"hideControls": true
},
"startup": {
"autoStart": true,
"delay": 2000
}
}Programmatisch konfigurieren
import { type RoomDiscoveryConfig, defaultRoomDiscoveryConfig } from '@carstennichte/cc-toolbox';
const config: RoomDiscoveryConfig = {
exhibitionId: 'mosaik-2027',
serverUrl: 'http://192.168.1.100:4000',
defaultRoom: 'artwork:item-cc-softbodies',
defaultArtwork: 'item-cc-softbodies',
autoConnect: true,
hideControls: true,
};
roomSelector.applyExhibitionConfig(config);ObjectUtils
Das Visualization-Modul nutzt erweiterte Object-Utilities:
import { ObjectUtils } from '@carstennichte/cc-toolbox';
// Nested Object → Flat Map
const flat = ObjectUtils.flatten({
physics: { gravity: 9.8, friction: 0.5 },
motion: { speed: 10 }
});
// → { 'physics.gravity': 9.8, 'physics.friction': 0.5, 'motion.speed': 10 }
// Flat Map → Nested Object
const nested = ObjectUtils.unflatten(flat);
// Filtern
const filtered = ObjectUtils.filter(flat, {
exclude: ['physics'],
numericOnly: true,
});
// Kombiniert
const result = ObjectUtils.flattenAndFilter(myObject, {
exclude: ['tweakpane', 'internal'],
numericOnly: true,
});Demo-Projekt
Das Demo-Projekt item-cc-parameter-viz zeigt alle Features:
cd workspaces/default/catalog/items/item-cc-parameter-viz
npm install
npm run devURL-Parameter für Sketch-Auswahl:
?sketch=mandala- Radiales Mandala (default)?sketch=creature- Lebendige Kreatur?sketch=observer- Sockets Observer mit RoomSelector
Observer Exhibition-Mode:
?sketch=observer&serverUrl=http://localhost:4000&room=artwork:item-cc-softbodies&autoConnect=trueBest Practices
1. Filter sinnvoll setzen
// Nur relevante Parameter tracken
const history = new ParameterHistory({
includePaths: ['physics', 'motion', 'demo'],
excludePaths: ['tweakpane', 'artwork.canvas.html', 'artwork.meta'],
});2. Sample-Intervall anpassen
// Für schnelle Änderungen: kürzeres Intervall
const history = new ParameterHistory({
sampleInterval: 16, // ~60fps
maxLength: 500, // Mehr History
});
// Für langsame Trends: längeres Intervall
const history = new ParameterHistory({
sampleInterval: 100,
maxLength: 200,
});3. Animation glätten
const mandala = new ParameterMandala({
animated: true,
animationSpeed: 0.05, // Langsamer = glatter
});Erweiterbarkeit
Eigene Visualizer erstellen:
import { ParameterVisualizer, ParameterVisualizerConfig } from '@carstennichte/cc-toolbox';
export class MyVisualizer extends ParameterVisualizer {
draw(ctx: CanvasRenderingContext2D, width: number, height: number): void {
const allStats = this.history.getAllStats();
// Eigene Visualisierung hier
for (const [path, stats] of allStats) {
const color = this.getTrendColor(stats);
const value = this.getAnimatedValue(path);
// ...
}
}
}