Signal Lab
Hear State Changes Before Logs Catch Up
Zusound turns Zustand updates into instant audio cues. Trigger actions, shape aesthetics, and learn the API through a guided, hands-on flow.
Step 1: Enable Audio
Browsers block audio until a user gesture. You can explore visuals anytime, and enable sound when ready.
Audio is locked
Scene 1 of 4 - State Triggers
Run basic state actions and listen for distinct operation cues.
Scene 1 - State Triggers
Start here. Adds should trend brighter, removals should feel tenser, and normal updates should stay more neutral.
Danger Zone
Hear what an infinite loop sounds like.
20 rapid state updates in 500ms.
Scene 2 - Aesthetic Mapping
Apply presets, then fine-tune parameters to hear how pleasantness, brightness, and simultaneity reshape each dyad.
Scene 3 - Timing and Throughput
Stress timing behavior and API-like bursts to evaluate stability under rapid updates.
Scene 4 - API Reference
Use this in-page API guide while you tune Signal Lab. Types and defaults are aligned to package-level canonical docs.
Usage Patterns
Middleware wraps your store creator. Subscriber attaches to an existing store and gives explicit lifecycle control.
Middleware
import { create } from 'zustand'
import { zusound } from 'zusound'
const useStore = create(
zusound(
(set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}),
{ volume: 0.3, debounceMs: 50 }
)
)
Subscriber
import { create } from 'zustand'
import { createZusound } from 'zusound'
const useStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}))
const instance = createZusound({ volume: 0.3 })
const unsubscribe = useStore.subscribe(instance)
// cleanup
unsubscribe()
instance.cleanup()
ZusoundOptions
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true in dev, false in prod-like envs |
Enable or disable audio feedback. |
volume |
number |
0.3 |
Global playback gain from 0..1. |
debounceMs |
number |
0 |
Coalesce rapid changes before sound emission. |
aesthetics |
Partial<AestheticParams> |
Type-specific defaults | Static per-change tuning overrides. |
mapChangeToAesthetics |
(change) => Partial<AestheticParams> |
undefined |
Dynamic aesthetics hook for each detected change. |
soundMapping |
Record<string, Partial<SoundParams>> |
undefined |
Path-level frequency, waveform, duration, and volume overrides. |
performanceMode |
boolean |
false |
Use static consonance ranking for lower compute cost. |
onError |
(error, context) => void |
undefined |
Non-fatal middleware and playback error hook. |
AestheticParams
| Parameter | Range | Perceptual Effect |
|---|---|---|
pleasantness |
0..1 |
Consonance target from tense to stable intervals. |
brightness |
0..1 |
Harmonic rolloff from muted to sharp timbre. |
arousal |
0..1 |
Envelope speed from slow fades to percussive attacks. |
valence |
0..1 |
Sustain character from brief to full sustain. |
simultaneity |
0..1 |
Dyad spread: 1 together, 0 staggered. |
baseMidi |
number |
Base MIDI pitch center before interval mapping. |
duration |
number (seconds) |
Override note duration for change-driven synthesis. |
Change Object Example
// incrementing count from 5 to 6
{
path: 'count',
operation: 'update',
valueType: 'number',
oldValue: 5,
newValue: 6,
}
Demo Mode Notes
Local demo mode (node demo/server.js) connects to real SSE events at
/events. Hosted/static mode does not open SSE and instead generates a
deterministic mock event sequence every 6 seconds.
Recipes
mapChangeToAesthetics
zusound(stateCreator, {
mapChangeToAesthetics: (change) => {
if (change.operation === 'add') {
return { pleasantness: 0.9, valence: 0.8 }
}
if (change.operation === 'remove') {
return { pleasantness: 0.3, arousal: 0.7 }
}
if (change.path === 'error') {
return { pleasantness: 0.1, brightness: 0.9 }
}
return {}
},
})
Per-path soundMapping
zusound(stateCreator, {
soundMapping: {
count: {
frequency: 440,
waveform: 'sine',
duration: 100,
volume: 0.5,
},
error: {
frequency: 220,
waveform: 'sawtooth',
duration: 300,
volume: 0.7,
},
},
})
What You Should Hear
Adds and positive transitions should feel more consonant than removals.
Signal Meter
Waiting for state activity...