CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-howler

Javascript audio library for the modern web with Web Audio API and HTML5 Audio support.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

spatial-audio.mddocs/

Spatial Audio

The spatial audio plugin extends Howler.js with 3D positional audio capabilities and stereo panning, enabling immersive audio experiences with realistic spatial positioning, orientation, and distance-based attenuation.

Capabilities

Stereo Panning

Control stereo positioning of sounds across the left-right audio field.

/**
 * Helper method to update stereo panning position globally
 * @param pan - Panning value (-1.0 = full left, 0.0 = center, 1.0 = full right)
 * @returns Howler instance for chaining
 */
Howler.stereo(pan: number): Howler;

/**
 * Set stereo panning for specific sound or sound group
 * @param pan - Panning value (-1.0 to 1.0)
 * @param id - Sound ID (optional, affects all sounds in group if not provided)
 * @returns Current panning if getting, Howl instance if setting
 */
Howl.prototype.stereo(pan: number, id?: number): Howl | number;

Usage Examples:

import { Howl, Howler } from "howler";

// Global stereo panning for all sounds
Howler.stereo(-0.5); // Pan all sounds to the left

// Per-sound stereo panning
const leftSound = new Howl({
  src: ['left.mp3']
});

const rightSound = new Howl({
  src: ['right.mp3']
});

leftSound.stereo(-1.0);  // Full left
rightSound.stereo(1.0);  // Full right

// Control specific sound instances
const id = leftSound.play();
leftSound.stereo(0.5, id); // Pan this specific instance to the right

// Get current panning
const currentPan = leftSound.stereo(); // Returns number

3D Listener Position

Set the position of the audio listener in 3D space, affecting how all positioned sounds are heard.

/**
 * Get/set the position of the listener in 3D cartesian space
 * @param x - The x-position of the listener
 * @param y - The y-position of the listener (optional)
 * @param z - The z-position of the listener (optional)
 * @returns Current position array if getting, Howler instance if setting
 */
Howler.pos(x: number, y?: number, z?: number): Howler | number[];

Usage Examples:

// Set listener position
Howler.pos(0, 0, 0); // Center position
Howler.pos(10, 5, -2); // Move listener to specific coordinates

// Get current listener position
const listenerPos = Howler.pos(); // Returns [x, y, z] array

// Update position over time (e.g., player movement)
function updatePlayerPosition(x, y, z) {
  Howler.pos(x, y, z);
}

3D Listener Orientation

Control the direction the listener is facing and their up vector in 3D space.

/**
 * Get/set the direction the listener is pointing in 3D space
 * @param x - The x-orientation of the listener's front vector
 * @param y - The y-orientation of the listener's front vector  
 * @param z - The z-orientation of the listener's front vector
 * @param xUp - The x-orientation of the listener's up vector
 * @param yUp - The y-orientation of the listener's up vector
 * @param zUp - The z-orientation of the listener's up vector
 * @returns Current orientation vectors if getting, Howler instance if setting
 */
Howler.orientation(x: number, y: number, z: number, xUp: number, yUp: number, zUp: number): Howler | number[];

Usage Examples:

// Set listener to face forward with normal up vector
Howler.orientation(0, 0, -1, 0, 1, 0);

// Face right
Howler.orientation(1, 0, 0, 0, 1, 0);

// Get current orientation
const orientation = Howler.orientation(); // Returns [x, y, z, xUp, yUp, zUp]

// Rotate listener based on player camera
function updateListenerRotation(forward, up) {
  Howler.orientation(forward.x, forward.y, forward.z, up.x, up.y, up.z);
}

3D Sound Positioning

Position individual sounds in 3D space relative to the listener.

/**
 * Set 3D position for sound or sound group
 * @param x - The x-position of the sound
 * @param y - The y-position of the sound (optional)
 * @param z - The z-position of the sound (optional)
 * @param id - Sound ID (optional, affects all sounds in group if not provided)
 * @returns Current position if getting, Howl instance if setting
 */
Howl.prototype.pos(x: number, y?: number, z?: number, id?: number): Howl | number[];

Usage Examples:

const ambientSound = new Howl({
  src: ['forest.mp3'],
  loop: true
});

const footsteps = new Howl({
  src: ['footsteps.mp3']
});

// Position sounds in 3D space
ambientSound.pos(0, 0, 0);     // Center
footsteps.pos(-5, 0, 10);      // To the left and forward

// Position specific sound instance
const stepId = footsteps.play();
footsteps.pos(2, 0, 5, stepId); // Move this specific footstep

// Get current position
const soundPos = ambientSound.pos(); // Returns [x, y, z]

// Animate sound movement
let x = -10;
setInterval(() => {
  x += 0.1;
  footsteps.pos(x, 0, 0); // Sound moves from left to right
}, 100);

3D Sound Orientation

Set the directional orientation of sounds for directional audio effects.

/**
 * Set 3D orientation for sound
 * @param x - The x-orientation of the sound's front vector
 * @param y - The y-orientation of the sound's front vector
 * @param z - The z-orientation of the sound's front vector  
 * @param id - Sound ID (optional, affects all sounds in group if not provided)
 * @returns Current orientation if getting, Howl instance if setting
 */
Howl.prototype.orientation(x: number, y: number, z: number, id?: number): Howl | number[];

Advanced Panner Attributes

Configure detailed 3D audio properties for realistic sound attenuation and directional effects.

/**
 * Set 3D panner attributes for realistic audio positioning
 * @param o - Panner attributes object (optional for getting current attributes)
 * @param id - Sound ID (optional, affects all sounds in group if not provided)
 * @returns Current attributes if getting, Howl instance if setting
 */
Howl.prototype.pannerAttr(o?: PannerAttributes, id?: number): Howl | PannerAttributes;

interface PannerAttributes {
  /** Inner cone angle in degrees (default: 360) */
  coneInnerAngle?: number;
  /** Outer cone angle in degrees (default: 360) */  
  coneOuterAngle?: number;
  /** Gain outside the outer cone (0.0-1.0, default: 0) */
  coneOuterGain?: number;
  /** Distance model for attenuation ('inverse' | 'linear' | 'exponential') */
  distanceModel?: 'inverse' | 'linear' | 'exponential';
  /** Maximum distance for attenuation (default: 10000) */
  maxDistance?: number;
  /** Panning model ('equalpower' | 'HRTF') */
  panningModel?: 'equalpower' | 'HRTF';
  /** Reference distance for attenuation (default: 1) */
  refDistance?: number;
  /** Rolloff factor for attenuation (default: 1) */
  rolloffFactor?: number;
}

Usage Examples:

const directionalSound = new Howl({
  src: ['speaker.mp3'],
  loop: true
});

// Configure realistic speaker characteristics
directionalSound.pannerAttr({
  coneInnerAngle: 40,        // 40-degree inner cone
  coneOuterAngle: 120,       // 120-degree outer cone  
  coneOuterGain: 0.3,        // 30% volume outside cone
  distanceModel: 'inverse',   // Realistic distance falloff
  maxDistance: 100,          // Inaudible beyond 100 units
  refDistance: 1,            // Full volume at 1 unit
  rolloffFactor: 2,          // Faster distance falloff
  panningModel: 'HRTF'       // Head-related transfer function
});

// Position and orient the directional sound
directionalSound.pos(0, 0, 10);        // 10 units forward
directionalSound.orientation(0, 0, -1); // Pointing toward listener

// Get current panner attributes
const attrs = directionalSound.pannerAttr();
console.log(attrs.coneInnerAngle); // 40

Spatial Audio Patterns

Game Audio Positioning

class GameAudio {
  constructor() {
    this.sounds = new Map();
    
    // Initialize listener at player position
    Howler.pos(0, 0, 0);
    Howler.orientation(0, 0, -1, 0, 1, 0);
  }
  
  createPositionalSound(name, src, loop = false) {
    const sound = new Howl({
      src: src,
      loop: loop
    });
    
    // Configure for realistic 3D audio
    sound.pannerAttr({
      distanceModel: 'inverse',
      maxDistance: 50,
      refDistance: 1,
      rolloffFactor: 1.5
    });
    
    this.sounds.set(name, sound);
    return sound;
  }
  
  updatePlayerPosition(x, y, z, forwardX, forwardY, forwardZ) {
    Howler.pos(x, y, z);
    Howler.orientation(forwardX, forwardY, forwardZ, 0, 1, 0);
  }
  
  playAt(soundName, x, y, z) {
    const sound = this.sounds.get(soundName);
    if (sound) {
      sound.pos(x, y, z);
      return sound.play();
    }
  }
}

// Usage
const gameAudio = new GameAudio();
gameAudio.createPositionalSound('gunshot', ['gunshot.mp3']);
gameAudio.createPositionalSound('footsteps', ['footsteps.mp3']);

// Play sound at specific location
gameAudio.playAt('gunshot', -10, 0, 5);

Dynamic Stereo Effects

// Create moving stereo effect
const movingSound = new Howl({
  src: ['car.mp3'],
  loop: true
});

// Animate from left to right
let pan = -1.0;
const moveTimer = setInterval(() => {
  movingSound.stereo(pan);
  pan += 0.1;
  
  if (pan > 1.0) {
    pan = -1.0; // Reset to left
  }
}, 100);

movingSound.play();

Environmental Audio Zones

class AudioZone {
  constructor(sound, centerX, centerZ, radius, maxVolume = 1.0) {
    this.sound = sound;
    this.centerX = centerX;
    this.centerZ = centerZ;
    this.radius = radius;
    this.maxVolume = maxVolume;
    this.soundId = null;
  }
  
  updateVolume(listenerX, listenerZ) {
    const distance = Math.sqrt(
      Math.pow(listenerX - this.centerX, 2) + 
      Math.pow(listenerZ - this.centerZ, 2)
    );
    
    if (distance <= this.radius) {
      const volume = this.maxVolume * (1 - (distance / this.radius));
      
      if (!this.soundId) {
        this.soundId = this.sound.play();
      }
      
      this.sound.volume(volume, this.soundId);
    } else if (this.soundId) {
      this.sound.stop(this.soundId);
      this.soundId = null;
    }
  }
}

// Create environmental zones
const forestZone = new AudioZone(
  new Howl({ src: ['forest.mp3'], loop: true }),
  0, 0, 20, 0.8
);

const riverZone = new AudioZone(
  new Howl({ src: ['river.mp3'], loop: true }),
  30, 10, 15, 0.6
);

// Update as player moves
function updateEnvironmentalAudio(playerX, playerZ) {
  forestZone.updateVolume(playerX, playerZ);
  riverZone.updateVolume(playerX, playerZ);
}

Browser Compatibility

Spatial audio features require Web Audio API support:

  • Chrome 14+ - Full support
  • Firefox 25+ - Full support
  • Safari 6+ - Full support
  • Edge 12+ - Full support
  • iOS Safari 6+ - Limited (no HRTF)
  • Android Chrome 25+ - Full support

Graceful Degradation:

// Check for spatial audio support
if (Howler.usingWebAudio && Howler.ctx && Howler.ctx.listener) {
  // Use full spatial audio features
  sound.pos(x, y, z);
  sound.pannerAttr({ panningModel: 'HRTF' });
} else {
  // Fallback to stereo panning only
  const pan = (x > 0) ? Math.min(x / 10, 1) : Math.max(x / 10, -1);
  sound.stereo(pan);
}

docs

global-audio.md

index.md

sound-playback.md

spatial-audio.md

tile.json