Concept
Spatializer
Stereo pan or full 3D positional audio. PannerNode wrapped, HRTF on by default.
TL;DR
Pass spatializer in play() to insert a
StereoPannerNode (2D) or PannerNode (3D HRTF)
between the voice and its bus. For positions that change over time —
e.g. a moving NPC — construct a Spatializer manually so
you can hold its reference and update it per frame.
Mental model
API surface
interface SpatialOptions {
pan?: number; // [-1, 1] — 2D
position?: [number, number, number]; // x, y, z — 3D
}
class Spatializer {
setPan(pan: number): void; // 2D only
setPosition(x: number, y: number, z: number): void; // 3D only
connectInto(dest: AudioNode): AudioNode;
dispose(): void;
} Live demo
Drag the puck to pan a looping sound. Best with headphones.
Recipes
2D pan from play()
engine.sound('footstep').play({
spatializer: { pan: -0.6 }, // [-1, 1] stereo
}); 3D position from play()
engine.sound('footstep').play({
spatializer: { position: [x, y, z] }, // world-space coords
}); Dynamic position — moving source
import { Spatializer } from 'zvuk';
const sp = new Spatializer(engine.context, { position: [0, 0, 0] });
sp.connectInto(engine.bus('sfx').input);
sp.setPosition(playerX, playerY, playerZ); // each frame
// when done:
sp.dispose(); Pitfalls
Don't 3D-spatialize music.
Music wants to feel "wide", not "located." HRTF on a stereo music bus is
a noticeable downgrade. Pan it manually if you want a side-bias.
Don't construct a Spatializer per frame.
HRTF nodes are cheap-ish but not free. Hold one per emitter and
setPosition it.