Files
meshcore-analyzer/AUDIO-PLAN.md

6.3 KiB
Raw Permalink Blame History

Mesh Audio — Sonification Plan

Turn raw packet bytes into generative music.

What Every Packet Has (guaranteed)

  • raw_hex — melody source
  • hop_count — note duration + filter cutoff
  • observation_count — volume + chord voicing
  • payload_type — instrument + scale + root key
  • node_lat/lon — stereo pan
  • timestamp — arrival timing

Final Mapping

Data Musical Role
payload_type Instrument + scale + root key
payload bytes (evenly sampled, sqrt(len) count) Melody notes (pitch)
byte value Note length (higher = longer sustain, lower = staccato)
byte-to-byte delta Note spacing (big jump = longer gap, small = rapid)
hop_count Low-pass filter cutoff (more hops = more muffled)
observation_count Volume + chord voicing (more observers = louder + stacked detuned voices)
node longitude Stereo pan (west = left, east = right)
BPM tempo (user control) Master time multiplier on all durations

Instruments & Scales by Type

Type Instrument Scale Root
ADVERT Bell / pad C major pentatonic C
GRP_TXT Marimba / pluck A minor pentatonic A
TXT_MSG Piano E natural minor E
TRACE Ethereal synth D whole tone D

How a Packet Plays

  1. Header configures the voice — payload type selects instrument, scale, root key. Flags/transport codes select envelope shape. Header bytes are NOT played as notes.
  2. Sample payload bytes — pick sqrt(payload_length) bytes, evenly spaced across payload:
    • 16-byte payload → 4 notes
    • 36-byte payload → 6 notes
    • 64-byte payload → 8 notes
  3. Each sampled byte → a note:
    • Pitch: byte value (0-255) quantized to selected scale across 2-3 octaves
    • Length: byte value maps to sustain duration (low byte = short staccato ~50ms, high byte = sustained ~400ms)
    • Spacing: delta between current and next sampled byte determines gap to next note (small delta = rapid fire, large delta = pause). Scaled by BPM tempo multiplier.
  4. Filter: low-pass cutoff from hop_count — few hops = bright/clear, many hops = muffled (signal traveled far)
  5. Volume: observation_count — more observers = louder
  6. Chord voicing: if observations > 1, stack slightly detuned voices (±5-15 cents per voice, chorus effect)
  7. Pan: origin node longitude mapped to stereo field
  8. All timings scaled by BPM tempo control

UI Controls

  • Audio toggle — on/off (next to Matrix / Rain)
  • BPM tempo slider — master time multiplier (slow = ambient, fast = techno)
  • Volume slider — master gain
  • Mute button — pause audio without losing toggle state

Implementation

Library: Tone.js (~150KB)

  • Tone.Synth / Tone.PolySynth for melody + chords
  • Tone.Sampler for realistic instruments
  • Tone.Filter for hop-based cutoff
  • Tone.Chorus for observation detuning
  • Tone.Panner for geographic stereo
  • Tone.Reverb for spatial depth

Integration

  • animatePacket(pkt) also calls sonifyPacket(pkt)
  • Optional "Sonify" button on packet detail page
  • Web Audio runs on separate thread — won't block UI/animations
  • Polyphony capped at 8-12 voices to prevent mudding
  • Voice stealing when busy

Core Function

sonifyPacket(pkt):
  1. Extract raw_hex → byte array
  2. Separate header (first ~3 bytes) from payload
  3. Header → select instrument, scale, root key, envelope
  4. Sample sqrt(payload.length) bytes evenly across payload
  5. For each sampled byte:
     - pitch = quantize(byte, scale, rootKey)
     - duration = map(byte, 50ms, 400ms) × tempoMultiplier
     - gap to next = map(abs(nextByte - byte), 30ms, 300ms) × tempoMultiplier
  6. Set filter cutoff from hop_count
  7. Set gain from observation_count
  8. Set pan from origin longitude
  9. If observation_count > 1: detune +/- cents per voice
  10. Schedule note sequence via Tone.js

Percussion Layer

Percussion fires instantly on packet arrival — gives you the rhythmic pulse while the melodic notes unfold underneath.

Drum Kit Mapping

Packet Type Drum Sound Why
Any packet Kick drum Network heartbeat. Every arrival = one kick. Busier network = faster kicks.
ADVERT Hi-hat Most frequent, repetitive — the timekeeper tick.
GRP_TXT / TXT_MSG Snare Human-initiated messages are accent hits.
TRACE Rim click Sparse, searching — light metallic tick.
8+ hops OR 10+ observations Cymbal crash Big network events get a crash. Rare = special.

Sound Design (all synthesized, no samples)

Kick: Sine oscillator, frequency ramp 150Hz → 40Hz in ~50ms, short gain envelope.

Hi-hat: White noise through highpass filter (7-10kHz).

  • Closed (1-2 hops): 30ms decay — tight tick
  • Open (3+ hops): 150ms decay — sizzle

Snare: White noise burst (bandpass ~200-1000Hz) + sine tone body (~180Hz). Observation count scales intensity (more observers = louder crack, longer decay).

Rim click: Short sine pulse at ~800Hz with fast decay (20ms). Dry, metallic.

Cymbal crash: White noise through bandpass (3-8kHz), long decay (500ms-1s). Only triggers on exceptional packets.

Byte-Driven Variation

First payload byte mod 4 selects between variations of each percussion sound:

  • Slightly different pitch (±10-20%)
  • Different decay length
  • Different filter frequency

Prevents machine-gun effect of identical repeated hits.

Timing

  • Percussion: fires immediately on packet arrival (t=0)
  • Melody: unfolds over 0.6-1.6s starting at t=0
  • Result: rhythmic hit gives you the pulse, melody gives you the data underneath

The Full Experience

Matrix mode + Rain + Audio: green hex bytes flow across the map, columns of raw data rain down, and each packet plays its own unique melody derived from its actual bytes. Quiet periods are sparse atmospheric ambience; traffic bursts become dense polyrhythmic cascades. Crank the BPM for techno, slow it down for ambient.

Future Ideas

  • "Record" button → export MIDI or WAV
  • Per-type mute toggles (silence ADVERTs, only hear messages)
  • "DJ mode" — crossfade between regions
  • Historical playback at accelerated speed = mesh network symphony
  • Presets (ambient, techno, classical, minimal)
  • ADVERT ambient drone layer (single modulated oscillator, not per-packet)