Gradually Change Web Audio API Panner

1.8k Views Asked by At

I'm trying to use a simple HTML range input to control the panning of my Web Audio API audio but I can only get 3 "positions" for my audio output:
-Center
-100% to the left
-100% to the right.

I would like to have something in between does positions, like 20% left and 80% right and so on...

The code that I'm using is:

//Creating the node
var pannerNode = context.createPanner();
//Getting the value from the HTML input and using it on the position X value 
document.getElementById('panInput').addEventListener('change', function () {
    pannerNode.setPosition(this.value, 0, 0);
});

And it refers to this input on my HTML file:

<input id="panInput" type="range" min="-1" max="1" step="0.001" value="0"/>

Does anyone knows what am I doing wrong?

3

There are 3 best solutions below

3
On BEST ANSWER

You shouldn't need to use two panners - Panner is stereo. This old answer is a great one to this question:

How to create very basic left/right equal power panning with createPanner();

1
On

I've actually found simple left/right panning to be kind of difficult with the Web Audio API. It's really set up for surround / spatial stuff, and I honestly don't understand it very well.

The way that I usually do panning is like this:

var panLeft = context.createGain();
var panRight = context.createGain();
var merger = context.createMerger(2);

source.connect(panLeft);
source.connect(panRight);
panLeft.connect(merger, 0, 0);
panRight.connect(merger, 0, 1);
merger.connect(context.destination);

document.getElementById('panInput').addEventListener('change', function () {
  var val = this.value;
  panLeft.gain.value = ( val * -0.5 ) + 0.5;
  panRight.gain.value = ( val * 0.5 ) + 0.5;
});

Basically, you send the signal to two gain nodes that you're going to use as your left and right channel. Then you take the value from your range element and use it to set the gain on each of the nodes.

This is sort of the lazy version though. In serious audio apps, there's usually a bit more math involved with the panning to make sure there aren't changes in overall level -- but hopefully this is enough to get you started.

0
On

I'm quite sure there is a better and easier way to do that but, for now, it definitely works for me.
If anyone else have a better/cleaner way of doing it, please share it here!
Thanks to Kevin Ennis for giving me this hint!

JavaScript File

//Create a splitter to "separete" the stereo audio data to two channels.
var splitter = context.createChannelSplitter(2);

//Connect your source to the splitter (usually, you will do it with the last audio node before context destination)
audioSource.connect(splitter);

//Create two gain nodes (one for each side of the stereo image)
var panLeft = context.createGain();
var panRight = context.createGain();

//Connect the splitter channels to the Gain Nodes that we've just created
splitter.connect(panRight,0);
splitter.connect(panLeft,1);

//Getting the input data from a "range" input from HTML (the code used on this range will be shown right on the end of this code)
var panPosition = document.getElementById("dispPanPositionLiveInput");
document.getElementById('panControl').addEventListener('change', function () {
  var val = this.value;
  panPosition.value = val;
  panLeft.gain.value = ( val * -0.5 ) + 0.5;
  panRight.gain.value = ( val * 0.5 ) + 0.5;
});

//Create a merger node, to get both signals back together
var merger = context.createChannelMerger(2);

//Connect both channels to the Merger
panLeft.connect(merger, 0, 0);
panRight.connect(merger, 0, 1);

//Connect the Merger Node to the final audio destination (your speakers)
merger.connect(context.destination);

HTML File

< input id="panControl" type="range" min="-1" max="1" step="0.001" value="0"/>