Oberon
Oberon — Claude Context
Reusable Metal-based audio-reactive visualization engine. Standalone SPM package — no dependencies on Horatio, Enobarbus, Touchstone, or Ariel. Used by Hecate (full-screen Immerse mode) and VizLab (shader design workbench).
Architecture
App provides VizInput (audio analysis + musical fields + params)
→ VizCanvasView (SwiftUI wrapper)
→ VizRenderer (MTKViewDelegate)
→ Metal fragment shader (from ShaderCatalog)
→ Ping-pong feedback textures (decay + warp)
→ Cosine palette coloring
→ Drawable output
Audio isolation: Oberon runs entirely on GPU via Metal. No audio framework dependencies, no shared mutable state with audio threads, no locks that could contend with real-time audio. Zero performance impact on audio processing.
Modules
Config
| File |
Purpose |
Config/ShaderCatalog.swift |
Enum of 21 shaders with display names, descriptions, and per-shader VizConfig defaults |
Config/VizConfig.swift |
Codable shader configuration: fragment function name, feedback decay/warp, palette, up to 6 parameter definitions |
Config/VizParamDef.swift |
Codable per-parameter metadata: name, display name, default value, range |
Config/CosinePalette.swift |
SIMD3-based cosine palette (Inigo Quilez formula), 11 presets: cosmic, fire, ocean, aurora, neon, mercury, earth, moss, amber, cathedral, coven |
| File |
Purpose |
Input/VizInput.swift |
Per-frame data struct: audio analysis (rms, bass, mid, high, spectralCentroid, dominantFrequency), musical fields (chordIntervalMask, vowelF1, vowelF2), shader params (param0–5), optional overrides (feedbackDecay, feedbackWarp, palette), raw waveform/spectrum arrays |
Input/AudioAnalyzer.swift |
1024-point FFT analyzer (Accelerate): 3-band RMS (bass/mid/high, smoothed α=0.3), spectral centroid, 32 log-spaced spectrum bins. Call analyze(_:) at ~30 Hz from main thread. |
Renderer
| File |
Purpose |
Renderer/VizRenderer.swift |
MTKViewDelegate: compiles all .metal files into single MTLLibrary, lazy pipeline creation per fragment function, ping-pong feedback textures (BGRA8Unorm), 2-frame inflight semaphore, two-pass render (offscreen → blit to drawable) |
Renderer/VizUniforms.swift |
Struct matching Metal layout: time, resolution, audio bands, dominantFrequency, musical fields (chordIntervalMask as UInt32, vowelF1, vowelF2), params, feedback, cosine palette components |
Views
| File |
Purpose |
Views/VizCanvasView.swift |
SwiftUI wrapper (UIViewRepresentable / NSViewRepresentable), maps VizInput → VizUniforms, idle throttling (60 fps → 10 fps when RMS < 0.005) |
Audio Analysis (updated ~30 Hz by AudioAnalyzer)
| Field |
Type |
Range |
Description |
rms |
Float |
0–1 |
Overall loudness, smoothed |
bass |
Float |
0–1 |
Low-frequency RMS (20–250 Hz) |
mid |
Float |
0–1 |
Mid-frequency RMS (250–4000 Hz) |
high |
Float |
0–1 |
High-frequency RMS (4000+ Hz) |
spectralCentroid |
Float |
0–1 |
Brightness proxy, normalized log scale |
dominantFrequency |
Float |
Hz |
Peak FFT bin frequency, 0 = not populated |
Musical Fields (populated by host app)
| Field |
Type |
Range |
Description |
chordIntervalMask |
UInt8 |
0–63 |
Bitmask: bit 0=root, 1=3rd, 2=5th, 3=7th, 4=9th, 5=octave. 0 = not populated. |
vowelF1 |
Float |
0–1 |
Formant 1 (jaw openness): 0=closed, 1=wide open |
vowelF2 |
Float |
0–1 |
Formant 2 (vowel shape): 0=back vowel, 1=front vowel |
Shader Parameters
| Field |
Type |
Range |
Description |
param0–param5 |
Float |
0–1 |
Host-defined, shader-specific meaning — see ShaderCatalog per-shader defaults |
OberonShaders.metal — shared utilities: fullscreen vertex, gradient noise (Perlin-style with quintic interpolation), FBM 2D/3D, domain warping, cosine palette, SDF primitives, feedback sampling, vowelColor, chordPopcount, soapFilm (thin-film interference), sacredRing (n-fold petal ring with float petal count + variable depth), flameForm (flame SDF), blit fragment.
| Shader |
Fragment Function |
Character |
| Nebula |
nebula_fragment |
Triple-nested domain warp with luminous hot spots |
| Metaballs |
metaball_fragment |
SDF liquid mercury + specular |
| CRT Scope |
lissajous_fragment |
Autonomous Lissajous trace with CRT phosphor |
| Volumetric |
volumetric_fragment |
Luminous fog/cloud volume |
| Circuit |
circuit_fragment |
Rotating grid with morphing junction shapes + diagonal traces |
| Aurora |
aurora_fragment |
5-layer smooth curtains, sine-based (no noise artifacts) |
| Attractor Trace |
attractor_trace_fragment |
Lorenz attractor, 3D tumble rotation, large scale |
| Warp Field |
warp_field_fragment |
Continuous domain-warped feedback (no atan2 seam) |
| Flocking Metaballs |
flocking_metaballs_fragment |
Boids + SDF liquid |
| Spiral Plasma |
spiral_plasma_fragment |
Spiraling plasma vortex (reference shader) |
| Living EQ |
living_eq_fragment |
16-bar organic spectrum, soft edges, subdued palette |
| Moss Grid |
moss_grid_fragment |
Wandering luminous dots with tendril connections |
| Wind Erosion |
wind_erosion_fragment |
Diving through layered geological strata |
| Physarum |
physarum_fragment |
Slime mold vein networks |
| Ink Diffusion |
ink_diffusion_fragment |
Ink in still water with autonomous seeping |
| Soap Film |
soap_film_fragment |
Smooth thin-film interference membrane |
| Bioluminescence |
bioluminescence_fragment |
Wandering/spiraling deep-sea organisms, configurable dot size |
| Sacred Geometry |
sacred_geometry_fragment |
Morphing petal rings with spinning trails |
| Candle Oracle |
candle_oracle_fragment |
Floating flames on random orbits, configurable count |
| Stellar Nebula |
stellar_nebula_fragment |
Gas cloud with visual reverb (smoothed energy via feedback) |
| Vowel Weave |
vowel_weave_fragment |
Wide woven threads colored by vowel formants |
All shaders accept uniforms (buffer 0), waveform samples (buffer 1, up to 256), spectrum bins (buffer 2, 32 bands), and the previous frame's feedback texture.
Shader Design Principles
All shaders follow these consistency rules (Spiral Plasma is the reference):
- Always visible at rest: Minimum brightness floor of 0.4+ when all audio is zero. No shader goes black.
- Always animated: Time-driven autonomous motion independent of audio. Never static at silence.
- Audio enhances, never gates: Audio makes things brighter/larger/faster but doesn't enable/disable content.
- No cheap sparkle: Avoid hash21 + step white-dot sparkle. Use luminous hot spots, smooth shimmer, or soft Gaussian points instead.
- No atan2 for color: atan2 creates a visible seam at ±π. Use normDir.x/normDir.y (continuous cos/sin components) for angular color variation.
- Gradient noise only: noise2d/noise3d use Perlin-style gradient noise with quintic interpolation. Never value noise (grid artifacts).
Cosine Palette Presets
cosmic, fire, ocean, aurora, neon, mercury, earth, moss, amber, cathedral, coven
Tests
| Test |
Validates |
shaderCatalogHasConfigs |
All 21 shaders have non-empty fragment functions and parameter definitions |
vizConfigRoundTrips |
VizConfig JSON encode/decode |
cosinePaletteRoundTrips |
CosinePalette JSON encode/decode |
vizInputDefaults |
VizInput zero-initializes correctly (includes new fields) |
chordIntervalMaskPopcount |
activeChordToneCount popcount helper returns correct values |
cd Packages/Oberon && swift test
Rules
- No dependencies on other packages — Oberon is standalone Metal + SwiftUI.
- All shaders gracefully handle zero audio input (no NaN, always visible, always animated).
- When
chordIntervalMask is 0, shaders that use it must fall back to a sensible default (e.g. 3-fold symmetry).
- Feedback textures use
.private storage mode (GPU-only).
- Idle throttling: 60 fps active → 10 fps when RMS < 0.005.
- Shader library compiled by concatenating all
.metal files (OberonShaders.metal first for shared utilities).
- VizUniforms layout must match exactly between Swift (
VizUniforms.swift) and Metal (OberonShaders.metal). chordIntervalMask is UInt32 in Swift / uint in Metal for alignment.
thread is a reserved Metal keyword — never use it as a variable name.
sacredRing() accepts float petal count for smooth morphing and variable depth parameter.