JSPM

@trap_stevo/voxen

0.0.0
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 168
    • Score
      100M100P100Q58576F
    • License See License in LICENSE.md

    Unifies interface audio, spatial environments, and intelligent sound behavior inside one modular, extensible system โ€” from reactive UI cues to full 3D soundfields, audio flows with precision, elegance, and control.

    Package Exports

    • @trap_stevo/voxen

    Readme

    ๐Ÿ”Š @trap_stevo/voxen

    Universal Sound Intelligence Layer.
    Every sound. Every space. One voice.

    Unifies interface audio, spatial environments, and intelligent sound behavior inside one modular, extensible system.
    From reactive UI cues to full 3D soundfieldsโ€”audio flows with precision, elegance, and control.


    โœจ Features

    • ๐Ÿงญ Universal Spatial Audio โ€” 3D/2D positional playback (HRTF/equal-power) with distance rolloffs & cones
    • ๐Ÿงฉ Plugin Architecture โ€” Built-ins: Occlusion, Proximity, Limiter, Reverb + custom DSP extensions
    • ๐ŸŽš๏ธ Layered Mixing โ€” Arbitrary buses ("layer:<name>") with smooth volume automation
    • ๐Ÿ“ฆ Direct Sources & Streaming โ€” load() for direct refs; auto-decoding + <audio> streaming cache
    • ๐Ÿง  AI Hearing Propagation โ€” propagate() / cancelPropagation() waves; occlusion-aware intensity; heard event
    • ๐Ÿšฆ Audience Targeting โ€” Include/exclude/byTag/custom predicates per play/propagation
    • ๐Ÿš€ Optimized Core โ€” Node pooling (Gain/Panner), audibility culling, guarded listener updates
    • ๐Ÿ—‚๏ธ Soundboards โ€” Declarative registries with hierarchical extends and versioning
    • ๐Ÿงพ Rich Events โ€” Play/fade/updates, plugin diagnostics, preload + propagation lifecycle

    โš™๏ธ Constructor

    import { Voxen } from "@trap_stevo/voxen";
    
    const voxen = new Voxen({
      cullHz: 60
    });

    Options

    Option Type Default Description
    cullHz number 30 Tick rate for listener and plugin updates

    ๐Ÿง  Core Concepts

    • Listener โ€“ Defines hearing point. Multiple listeners allowed; one active at a time.
    • Soundboard โ€“ Groups related sounds; supports overrides and inheritance.
    • Layer โ€“ Controls volume and mix per domain (UI, world, music).
    • Plugin โ€“ Extends system with DSP or behavioral logic.
    • Propagation โ€“ Expanding hearing waves for AI detection and stealth simulation.

    โš™๏ธ Core Methods

    Method Description Params (summary) Returns Async
    resume() Unlocks and resumes the AudioContext. โ€” Promise<void> โœ…
    destroy() Stops all sounds, clears timers/caches, closes context. โ€” void โŒ
    on(type, handler) Subscribes to an event. type, handler(payload) () => void โŒ
    off(type, handler) Unsubscribes an event handler. type, handler void โŒ
    registerPlugin(plugin) Registers a custom plugin. { name, ... } void โŒ
    enableReverb(options?) Enables built-in reverb bus. { mix?, ir? } Promise<void> โœ…
    disableReverb() Disables built-in reverb. โ€” void โŒ
    enableLimiter(options?) Enables built-in limiter. { threshold?, knee?, ratio?, attack?, release? } void โŒ
    disableLimiter() Disables built-in limiter. โ€” void โŒ
    enableOcclusion(options?) Enables occlusion (raycast-aware). { raycast?, getListenerPosition?, maxAttenuation?, smoothing? } void โŒ
    disableOcclusion() Disables occlusion. โ€” void โŒ
    setProximityTriggers(options) Starts/updates proximity trigger loop. { getListenerPosition, getInstancePosition?, triggers[], tickHz?, distance? } void โŒ
    registerSoundboard(board) Registers a soundboard. { name, items, extends? } void โŒ
    useSoundboards(names) Activates soundboards (topmost wins). string[] void โŒ
    contains(id) Checks if an active soundboard has id. string boolean โŒ
    preload(ids) Prepares/decodes assets by ids. string[] Promise<void> โœ…
    addListener(listener) Adds a listener. { id, position, orientation?, tags? } void โŒ
    updateListener(id, patch) Patches listener fields. string, { position?, orientation?, tags? } void โŒ
    removeListener(id) Removes a listener. string void โŒ
    setActiveListener(id) Sets active listener and applies to context. string void โŒ
    load(soundId, sourceRef, defaults?) Registers & prepares a direct sound. string, SourceRef, { loop?, volume?, pitch?, distance? } Promise<void> โœ…
    play(idOrDirect, opts?) Plays by soundboard id or direct source. `string DirectRef, PlayOptions` Promise<string> (instanceId)
    stop(instanceId, opts?) Stops an instance (optional fade). string, { fadeOut? } void โŒ
    updateInstance(instanceId, patch) Updates volume/pitch/spatial origin. string, { volume?, pitch?, spatial? } void โŒ
    getActiveInstances() Lists active instance ids. โ€” string[] โŒ
    setVolume(target, value) Sets "master" or "layer:<name>" volume. string, 0..1 void โŒ
    ensureLayer(name, node?) Ensures a named layer exists (Gain by default). string, AudioNode? void โŒ
    propagate(options) Starts an expanding hearing wave. PropagateOptions string (token) โŒ
    cancelPropagation(token, extra?) Cancels an in-flight wave. string, { natural? } void โŒ

    ๐Ÿ”” Events

    Event Description
    play Fires when playback starts.
    ended Fires when playback stops.
    audibility Signals sound visibility to listener.
    layerVolumeChange Occurs when layer volume changes.
    pluginError Reports plugin failure.
    fadeStart Marks fade-in or fade-out begin.
    fadeEnd Marks fade completion.
    instanceUpdate Reflects runtime parameter change.
    layerMute Toggles layer mute state.
    preloadComplete Confirms preload success.
    contextStateChange Indicates context resume or suspend.
    propagateStart Emitted when a sound wave begins.
    heard Emitted when a listener perceives a propagated sound.
    propagateEnd Fired when propagation completes or is canceled.

    ๐Ÿ“˜ Parameter Reference

    This section defines the shapes and defaults of Voxenโ€™s primary configuration objects. All objects are plain JSON-like structures; functions are allowed where noted (e.g., for live positions).

    ๐Ÿ”ค Common Scalar & Utility Types

    Type Meaning
    Seconds Number in seconds (e.g., 0.25)
    Hertz Number in Hz (e.g., 60)
    Decibels Number in dBFS for WebAudio nodes (e.g., -3)
    Linear01 Number clamped to 0..1
    Vec3 { x:number, y:number, z:number }
    Vec3Fn () => Vec3 (evaluated per frame/tick)
    TimeFn<T> `T

    ๐Ÿ—‚๏ธ Sound Sources

    SourceRef

    A reference to media to be decoded or streamed.

    Field Type Required Notes
    kind "url" | "element" | "buffer" โœ… How audio is provided
    href string โฌ… when kind:"url" Absolute/relative URL
    element HTMLMediaElement โฌ… when kind:"element" <audio> or <video>
    buffer AudioBuffer โฌ… when kind:"buffer" Pre-decoded PCM
    stream boolean โŒ If true, uses media element streaming path
    loop boolean โŒ Default loop behavior (can be overridden)
    mime string โŒ Hint (e.g., "audio/ogg")

    DirectRef

    Play without a soundboard entry.

    Field Type Required Notes
    id string โœ… Logical id for this sound
    sources SourceRef[] โœ… One or more fallbacks
    meta Partial<PlayDefaults> โŒ Default volume/pitch/distance/loop

    ๐Ÿงญ Spatial & Distance

    DistanceModel

    "inverse" | "linear" | "exponential"

    DistanceOptions

    Field Type Default Notes
    refDistance number 1 Distance at which volume is 100%
    maxDistance number 10000 Beyond this, no further attenuation
    rolloff DistanceModel "inverse" Attenuation curve
    coneInner number 360 Degrees of full volume
    coneOuter number 360 Degrees at outer cone edge
    coneOuterGain Linear01 0 Gain at outer cone edge

    SpatialOptions

    Field Type Required Default Notes
    origin TimeFn<Vec3> โœ… โ€” World position of emitter
    forward TimeFn<Vec3> โŒ {0,0,1} Emitter forward vector
    up TimeFn<Vec3> โŒ {0,1,0} Emitter up vector
    distance DistanceOptions โŒ see above Distance/cone controls
    hrtf boolean โŒ true If false, uses equal-power panner
    stereo boolean โŒ false If true, bypasses HRTF for stereo sources

    ๐ŸŽš๏ธ Playback

    FadeOptions

    Field Type Default Notes
    in Seconds 0 Fade-in duration
    out Seconds 0 Fade-out duration

    PlayDefaults

    Field Type Default Notes
    volume Linear01 1.0 Base gain
    pitch number 1.0 PlaybackRate
    loop boolean false Looping behavior
    distance DistanceOptions โ€” If spatialized

    PlayOptions

    Field Type Default Notes
    layer string "master" Route to "layer:<name>"
    volume Linear01 inherits Overrides default
    pitch number 1.0 0.5 = down octave, 2.0 = up octave
    loop boolean inherits โ€”
    spatial SpatialOptions โ€” Enables 3D/2D panning
    fade FadeOptions {} Smooth in/out
    tags string[] [] Arbitrary metadata tags
    audience Audience โ€” Targeting rules (see below)
    onEnd () => void โ€” Convenience callback

    updateInstance(instanceId, patch)

    Field Type Notes
    volume Linear01 Smooth internal ramping
    pitch number PlaybackRate update
    spatial Partial<SpatialOptions> Live move/aim updates
    layer string Re-route to a different bus

    ๐Ÿง‘โ€๐Ÿฆป Audience Targeting

    Use to filter who can hear a sound or who participates in a propagation wave.

    Audience

    A discriminated union:

    type Audience =
      | { kind:"all" }
      | { kind:"include"; ids:string[] }
      | { kind:"exclude"; ids:string[] }
      | { kind:"byTag"; tags:string[] }
      | { kind:"predicate"; test:(meta:{ id:string; tags?:string[]; extra?:any }) => boolean }
    • When used for playback, audience is applied at instance connect time.
    • When used for propagate(), audience is sampled per wavefront step.

    ๐Ÿ›ฐ๏ธ Propagation (AI Hearing)

    PropagateOptions

    Field Type Required Default Notes
    origin TimeFn<Vec3> โœ… โ€” Source of the wave
    power number โœ… โ€” Scalar โ€œloudnessโ€ that maps to effective radius
    maxRadius number โœ… โ€” Upper bound for distance check
    velocity number โŒ 343 m/s (speed of sound at ~20ยฐC)
    sampleHz Hertz โŒ 30 Wave stepping rate
    decay Linear01 โŒ 0.0 Per-meter loss beyond distance model
    occlusionAware boolean โŒ false Enables raycast penalty
    occlusionPenalty Linear01 โŒ 0.5 Additional attenuation when blocked
    audience Audience โŒ {kind:"all"} Target listeners/entities
    tags string[] โŒ [] Labels attached to heard events
    token string โŒ auto Return value; can be supplied for idempotence

    Events (Propagation)

    • propagateStart โ€” { token, origin, power, maxRadius }
    • heard โ€” { token, listenerId, distance, intensity, tags }
    • propagateEnd โ€” { token, reason:"completed"|"canceled" }

    ๐Ÿงฒ Proximity Triggers

    setProximityTriggers(options)

    Field Type Required Default Notes
    getListenerPosition () => Vec3 โœ… โ€” Primary tracked subject
    getInstancePosition (id:string) => Vec3 โŒ โ€” Resolve instance anchor
    triggers ProximityTrigger[] โœ… โ€” Zones to monitor
    tickHz Hertz โŒ 30 Polling rate
    distance `{ metric?:"euclidean" "manhattan" }` โŒ "euclidean"

    ProximityTrigger

    Field Type Required Default Notes
    id string โœ… โ€” Unique zone id
    position TimeFn<Vec3> โœ… โ€” Center of zone
    radius number โœ… โ€” Meters
    hysteresis number โŒ 0.0 Entry/exit stability band
    cooldown Seconds โŒ 0.0 Rate limit events
    enterPlay { soundId:string, options?:PlayOptions } โŒ โ€” Fire on first entry
    exitPlay { soundId:string, options?:PlayOptions } โŒ โ€” Fire on first exit
    tags string[] โŒ [] Zone metadata

    Events:

    • proximityEnter โ€” { id, position }
    • proximityExit โ€” { id, position }

    ๐Ÿงฑ Occlusion Plugin

    enableOcclusion(options)

    Field Type Required Default Notes
    raycast (from:Vec3, to:Vec3) => boolean โœ… โ€” true if blocked
    getListenerPosition () => Vec3 โœ… โ€” For primary listener
    maxAttenuation Linear01 โŒ 0.55 Max gain drop when blocked
    smoothing Seconds โŒ 0.08 Attack/release toward target
    bypassLayers string[] โŒ [] Skip occlusion on these buses

    ๐Ÿงฐ Limiter Plugin

    enableLimiter(options)

    Field Type Default Notes
    threshold Decibels -1.0 Compressor threshold
    knee Decibels 0.0 Soft knee amount
    ratio number 8.0 Compression ratio
    attack Seconds 0.005 Attack time
    release Seconds 0.100 Release time
    makeupGain Decibels 0.0 Optional post-comp gain
    bypassLayers string[] [] Do not route these layers through limiter (alias: excludeLayers)

    Recommended: bypass UI, keep world/music under control.

    engine.enableLimiter({
      threshold : -3, ratio : 12, attack : 0.003, release : 0.120,
      bypassLayers : ["ui"]
    });

    ๐ŸŒซ๏ธ Reverb Bus

    enableReverb(options)

    Field Type Default Notes
    mix Linear01 0.2 Wet contribution on master
    ir `AudioBuffer URL` builtin
    preDelay Seconds 0.0 Optional pre-delay
    decay Seconds 1.2 Tail shaping (when using algorithmic IR)
    bypassLayers string[] [] Do not send these layers to reverb

    ๐ŸŽ›๏ธ Mixing & Layers

    • setVolume("master", value) โ€” Set master gain.
    • setVolume("layer:<name>", value) โ€” Set per-bus gain.
    • ensureLayer(name, node?) โ€” Create a custom bus (default GainNode); you can pass a pre-wired AudioNode for advanced routing (e.g., side-chains).

    ๐Ÿ‘‚ Listeners

    addListener(listener)

    Field Type Required Default Notes
    id string โœ… โ€” Unique id
    position TimeFn<Vec3> โœ… โ€” Ear position
    orientation.forward TimeFn<Vec3> โŒ {0,0,-1} Facing
    orientation.up TimeFn<Vec3> โŒ {0,1,0} Up vector
    tags string[] โŒ [] Audience/meta

    setActiveListener(id) switches the engine to drive WebAudioโ€™s AudioListener from this definition.


    ๐Ÿงฉ Plugin Hook Context (ctx)

    Hook argument available to beforeConnect, afterConnect, frame, onStop.

    type PluginCtx = {
      engine : Voxen;
      audioCtx : AudioContext;
      nodes : {
        source : AudioNode;
        gain : GainNode;
        layer : GainNode;      // or custom node if provided
        master : GainNode;
      };
      layer : string;          // "master" or "layer:<name>"
      contextTime : number;    // audioCtx.currentTime
      instanceId : string;     // current play instance
      meta? : any;             // item meta / tags
    };

    ๐Ÿ“ก Events (Payload Shapes)

    • play โ€” { instanceId, id, layer, tags? }
    • ended โ€” { instanceId, id, reason:"natural"|"stop"|"error" }
    • audibility โ€” { instanceId, audible:boolean }
    • layerVolumeChange โ€” { layer, value }
    • pluginError โ€” { plugin, error }
    • fadeStart โ€” { instanceId, kind:"in"|"out", duration }
    • fadeEnd โ€” { instanceId, kind:"in"|"out" }
    • instanceUpdate โ€” { instanceId, patch }
    • preloadComplete โ€” { ids:string[] }
    • contextStateChange โ€” { state:"running"|"suspended"|"closed" }
    • proximityEnter / proximityExit โ€” { id, position }
    • propagateStart / heard / propagateEnd โ€” see Propagation above

    ๐Ÿ“ฆ Installation

    npm install @trap_stevo/voxen
    
    # or
    
    yarn add @trap_stevo/voxen

    โšก Quick Start & Examples

    Example 1 โ€” UI Click

    import { Voxen } from "@trap_stevo/voxen";
    
    const voxen = new Voxen();
    
    voxen.registerSoundboard({
      name: "ui",
      items: [
        {
          id: "ui/click",
          sources: [{ kind: "url", href: "https://upload.wikimedia.org/wikipedia/commons/2/26/Computer_mouse_single_click.ogg" }],
          meta: { volume: 0.85 }
        }
      ]
    });
    
    voxen.useSoundboards(["ui"]);
    await voxen.preload(["ui/click"]);
    await voxen.resume();
    
    document.addEventListener("click", () => voxen.play("ui/click", { layer: "ui" }));

    Example 2 โ€” 3D Fountain Ambience

    voxen.addListener({
      id: "listener",
      position: () => ({ x: camera.x, y: camera.y, z: camera.z }),
      orientation: {
        forward: () => camera.forward(),
        up: () => ({ x: 0, y: 1, z: 0 })
      }
    });
    voxen.setActiveListener("listener");
    
    voxen.registerSoundboard({
      name: "scene",
      items: [
        {
          id: "env/fountain",
          sources: [{ kind: "url", href: "https://upload.wikimedia.org/wikipedia/commons/5/54/Fountainnoise.ogg" }],
          meta: { loop: true, volume: 0.85, distance: { refDistance: 1, maxDistance: 45 } }
        }
      ]
    });
    
    voxen.useSoundboards(["scene"]);
    await voxen.preload(["env/fountain"]);
    await voxen.play("env/fountain", {
      spatial: {
        origin: () => ({ x: 6, y: 0.5, z: -10 }),
        distance: { refDistance: 1, maxDistance: 45 },
        hrtf: true
      },
      layer: "ambient",
      fade: { in: 1.0 }
    });

    Example 3 โ€” Proximity + Occlusion + AI Propagation

    voxen.enableOcclusion({
      raycast: (from, to) => performRaycast(from, to),
      getListenerPosition: () => player.position,
      maxAttenuation: 0.55,
      smoothing: 0.08
    });
    
    voxen.setProximityTriggers({
      getListenerPosition: () => player.position,
      triggers: [
        {
          id: "pickup-zone",
          position: { x: 5, y: 0, z: -8 },
          radius: 1.5,
          enterPlay: { soundId: "ui/click", options: { layer: "ui" } }
        }
      ],
      tickHz: 45
    });
    
    // AI Hearing Example
    voxen.on("heard", e => console.log("AI heard:", e.listenerId, e.tags));
    
    voxen.propagate({
      origin: () => player.position,
      power: 1,
      maxRadius: 40,
      velocity: 343,
      sampleHz: 30,
      audience: { kind: "byTag", tags: ["ai"] },
      occlusionAware: true,
      occlusionPenalty: 0.6
    });

    Example 4 โ€” Layer Mixing & Live Updates

    voxen.setVolume("layer:ambient", 0.3);
    voxen.updateInstance(fountainId, { pitch: 1.15 });
    voxen.stop(fountainId, { fadeOut: 0.5 });

    ๐Ÿงฉ Custom Plugin API

    Custom DSP, analyzers, or automation logic can slot directly into the playback chain.

    Configuration Hooks

    Hook Trigger Description
    onRegister({ engine, audioCtx }) Plugin registration Prepare nodes or parameters.
    beforeConnect(ctx, node) Before connection Insert filters, delays, or custom nodes.
    afterConnect(ctx) After connection Run post-setup logic.
    frame(ctx, dt) Every tick (cullHz) Update dynamic parameters.
    onStop(ctx) Sound stop Clean resources.

    Usage Example

    const EchoPlugin = {
      name: "custom:echo",
    
      onRegister({ audioCtx }) {
        this.delay = audioCtx.createDelay();
        this.delay.delayTime.value = 0.3;
        this.feedback = audioCtx.createGain();
        this.feedback.gain.value = 0.4;
        this.delay.connect(this.feedback);
        this.feedback.connect(this.delay);
      },
    
      beforeConnect(ctx, node) {
        node.connect(this.delay);
        return this.delay;
      },
    
      frame(ctx, dt) {
        const g = ctx.nodes.gain.gain.value;
        this.feedback.gain.setTargetAtTime(g * 0.4, ctx.nodes.master.contextTime, 0.05);
      },
    
      onStop() {
        this.delay.disconnect();
        this.feedback.disconnect();
      }
    };
    
    voxen.registerPlugin(EchoPlugin);

    โšก Performance & Integration Notes

    ๐ŸŽ›๏ธ Plugin Chain Order

    Source โ†’ [Plugin.beforeConnect nodes] โ†’ preGain โ†’ gain โ†’ layer โ†’ master โ†’ destination

    Use beforeConnect for filters or delays.
    Use custom layers for post-processing or global DSP.

    ๐Ÿงฎ Frame Efficiency

    Set cullHz to match simulation tick:

    • 90 Hz for rhythmic or rapid feedback loops
    • 30โ€“45 Hz for dense spatial environments

    ๐Ÿง  Memory & Pooling

    Gain and Panner nodes reuse pooled memory.
    Inactive nodes disconnect automatically.
    Streamed sources skip redundant decoding.

    โšก Latency Control

    Chromium engines reach sub-5 ms latency with preloaded buffers.
    Call .preload() before playback to prevent decode delays.
    Trigger .resume() once after user gesture to unlock playback.

    ๐Ÿ•น๏ธ Integration Guidelines

    Three.js

    • Update listener in render loop via updateListener().
    • Pass camera.getWorldPosition() and camera.getWorldDirection().

    Babylon.js

    • Hook into camera.onViewMatrixChangedObservable.

    React / DOM

    • Resume audio after click:
      useEffect(() => {
        const click = () => voxen.play("ui/click");
        document.addEventListener("click", click);
        return () => document.removeEventListener("click", click);
      }, []);

    Physics Engines

    • Provide world coordinates in spatial.origin.
    • Interpolation handled internally.

    ๐Ÿงญ Debugging

    Monitor pluginError for DSP exceptions.
    Audit getActiveInstances() for pool usage.
    Trace audibility for live diagnostics:

    voxen.on("audibility", e => console.log("Audible:", e.audible));

    ๐Ÿš€ Best Practice Summary

    Area Action
    Update Rate Maintain 30โ€“60 Hz listener updates
    Buffering Preload every large asset
    Layers Separate UI, World, Music buses
    Plugins Register once; reuse globally
    Stop Fade out softly to avoid clicks
    Environment Use 48 kHz audio contexts for consistency

    ๐Ÿ“œ License

    See License in LICENSE.md


    ๐Ÿ”Š Orchestrate Sound. One Voice.

    Unify UI clicks, spatial worlds, and cinematic layers into one deterministic audio fabric. From web apps to 3D games, Voxen delivers low-latency playback, precise spatialization, and event-driven controlโ€”so sound design scales with your vision, not your constraints. Every sound. Every space. One voice.