Skip to content

VizLab

VizLab — Claude Context

Audio-reactive visualization design workbench. Loads Touchstone recipes, runs them through a RecipeEngine with mock signal input, polls snapshot data, and renders visual arrangements on a SwiftUI Canvas.

Dependencies

Touchstone + Horatio only (same as Workbench). No Ariel, Enobarbus, camera, or Bluetooth.

Data Flow

Recipe JSON → RecipeEngine (real audio processing)
                  ↓ (30 Hz snapshot polling)
             SnapshotData (per-node FDSPSnapshot)
                  ↓
             VisualState (flat [String: Float] dictionary)
                  ↓ (ParameterMappings with curves/ranges)
             ArrangementRenderer (TimelineView + Canvas, 60 fps)

Signal Chain

SignalGenerator → Input Level → RecipeEngine L/R
    → Auto-Gain → BrickwallLimiter L/R
    → [Mute gate] → Waveform ring buffer → Output

Architecture

Audio

File Role
Audio/VizLabAudioEngine.swift Effects-mode-only engine: SignalGenerator → RecipeEngine → limiter. Snapshot access via readNodeSnapshot(). outputMuted flag for silent processing.
Audio/SignalGenerator.swift Phase-accumulator signal generation (13 types, no live/sample)
Audio/SignalType.swift Signal type enum (no live input)

Models

File Role
Models/VizLabState.swift @Observable central state: recipe loading, snapshot polling (30 Hz), visual state rebuild, arrangement management, manual overrides
Models/VisualRecipe.swift Codable: arrangement type + parameter mappings + color config
Models/ParameterMapping.swift Source → output with curve/range, reuses Touchstone ModulationCurve
Models/ArrangementType.swift Enum: plasmaNebula, dotSwarm, eqBars, waveformScope, ringVisualizer, lissajous, gridWarp
Models/VisualDocument.swift FileDocument for JSON export/import
Models/SnapshotReader.swift Field extraction from FDSPSnapshot (ported from Workbench)

Views

File Role
ContentView.swift NavigationSplitView: sidebar (recipes + arrangements) + detail (canvas + inspector)
Views/CanvasView.swift TimelineView + Canvas at 60 fps, dispatches to active Arrangement
Views/ArrangementPicker.swift Browse/select arrangement type
Views/MappingEditor.swift Connect snapshot fields → visual params
Views/MockControlsView.swift Signal type + frequency + manual override sliders

Arrangements

File Role
Views/Arrangements/Arrangement.swift Protocol: update(dt:visualState:), draw(context:size:time:visualState:), parameterDefinitions
Views/Arrangements/PlasmaArrangement.swift Particle system with spiral attractor (ported from Hecate PlasmaFieldView)

Arrangement Protocol

protocol Arrangement: AnyObject {
    func update(dt: Double, visualState: [String: Float])
    func draw(context: inout GraphicsContext, size: CGSize, time: Double, visualState: [String: Float])
    static var parameterDefinitions: [ArrangementParamDef] { get }
}
Class-type for mutable frame-to-frame state (particle pools, phase accumulators).

Visual State

Flat [String: Float] dictionary. Keys: - "rms" — computed from waveform samples - "nodeId.fieldName" — extracted from FDSPSnapshot per node - Mapped parameter names from VisualRecipe mappings - Manual overrides take precedence

Rules

  • No Ariel or Enobarbus imports.
  • @ObservationIgnored + _rt prefix for all audio-thread state.
  • BrickwallLimiter always on, never bypassable.
  • Snapshot polling on main thread (30 Hz timer), Canvas draw at 60 fps.
  • Arrangements are reference types (class) — hold mutable state.