Audible crackling noises from the web audio panner occur when adjusting the position

Recently, while working on a 3D web app that involves positional 3D audio, I encountered an issue with crackling noise in the output as the sound source changes position. At first, I suspected it could be related to programming or library problems (specifically using howler.js).

To investigate further, I created a basic example using plain JS and the Webaudio API. The code snippet can be found below:

 // Insert code snippet here 

The sample project showcases setting up audio elements like panner, listener, and oscillator within the Web Audio context.

Interestingly, after testing the application by changing the xPosition slider, the annoying crackling sound became more evident, particularly when using headphones. Despite my attempts to modify value ranges for the position change rate, the issue persisted even with minor adjustments.

It seems that maintaining a slow rate of change for the panner's position might not be practical for real-world scenarios. Even utilizing timers to update the position or requestAnimationFrame() didn't resolve the problem.

If anyone has insights into the root cause of this distortion and potential solutions, your input would be greatly appreciated!

You can view the complete example here.

Answer №1

I was experiencing crackling audio when using a simple GainNode. It seemed like it could potentially be a bug, considering that a straightforward implementation in the ScriptProcessor does work. Alternatively, could it be that we are misunderstanding how to properly handle timing with audioCtx.currentTime?

In my case, the solution involved replacing the gain node with a ScriptProcessor (which is deprecated; an AudioWorkletNode is recommended) where I applied my own gain directly to the audio signal:

let myGain = 1.0; // adjust this value as needed to avoid crackling noises

let whiteNoise = audioCtx.createScriptProcessor(4096, 0, 1);
whiteNoise.onaudioprocess = function(e) {
    let output = e.outputBuffer.getChannelData(0);
    for (let i = 0; i < output.length; i++) {
        output[i] = (Math.random() * 2 - 1) * myGain;
    }
}

Although this approach may not directly address the issue with the 3D panner, perhaps you can look into finding source code or examples of such a panner implementation and adapt it to an AudioWorkletNode? Hopefully, this suggestion proves helpful.

Answer №2

To make a quick switch, try using setTargetAtTime instead of setValueAtTime, setting a minimum time limit:

    panner.positionX.setTargetAtTime(0, audioCtx.currentTime, 0.005);
    panner.positionY.setTargetAtTime(1, audioCtx.currentTime, 0.005);
    panner.positionZ.setTargetAtTime(1, audioCtx.currentTime, 0.005);

And the pattern continues.

Concerning the value 0.005, Chris Wilson discusses timeConstant in this thread: WebAudio: how does timeConstant in setTargetAtTime work?

In my experience, 0.005 is the smallest stable value for an immediate change in value.

Generally speaking, I personally would not recommend utilizing setTargetAtTime to modify values since I have noticed audio artifacts while doing so. It's best suited for setting initial values, but I wouldn't take the chance.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Webpack to Compile Sass (and keep class names local)

It's been a long journey trying to configure my Webpack setup to compile Sass, and the process has become quite overwhelming. In my quest for answers, I encountered numerous Github issues, Stackoverflow threads, and blog posts discussing how to integr ...

Is there a way to retrieve the specific point that was clicked on within a THREE.Points object?

I'm currently experimenting with a raycaster to identify points on a 2D plane of points... function dynamic(x) { x.dynamic = true; return x; } geometry.addAttribute("position", dynamic(new THREE.BufferAttribute(positions, 3))); geometry.addAttribute( ...

Divide AJAX result (in JSON format)

Is there a way to divide the AJAX response shown below into three distinct objects based on the 'product_range' attribute using JavaScript/jQuery - creating one object array for 'range-1' products, another for 'range-2', and s ...

Moving back and forth within a range

One simple yet commonly encountered task involves looping through a range forwards and backwards: var currentIndex = 0; var range = ['a', 'b', 'c', 'd', 'e', 'f']; function getNextItem(directi ...

Utilize Vue.js to transmit HTTP requests with specified headers and parameters

I'm trying to figure out how to create a LinkedIn login component, but I'm having trouble finding information about headers and parameters. Can someone help me understand how to send a GET or POST request with parameters and headers? Here's ...

jQuery Mobile's photo swipe with target feature isn't functioning as expected

Utilizing Photo Swipe ( ) on my jQuery Mobile page. I aim to display a thumbnail view of the images upon loading. When clicking on a thumbnail, the user should be taken to a swipe-enabled gallery that takes up 30% of the screen height. Additionally, I wan ...

The mesmerizing world of parallax animations and the smooth scrolling experience

I recently built a website using the SUPERSCROLLORAMA plugin, only to discover later that there are issues with parallax scrolling on iPad and iPhone. Now I'm trying to figure out how to solve this problem. It seems that events are disabled on these ...

The wrapping of cells in React Material UI GridList is not functioning properly

While working with Material-UI GridList, I've encountered an issue where the cells don't wrap correctly within the grid. Upon inspecting the CSS, I noticed that it utilizes flex-wrap, which is intended to flex rows. However, I believe there must ...

Comparing Two Arrays in AngularJS and Disabling on Identical Cases

I currently have two tables: "Available subject" and "Added subject" which are populated by ng-repeat with two separate lists of objects. My objective is to accomplish three things: 1 - When the user clicks on the plus sign, a row with identical content s ...

Utilizing Ajax to dynamically display the outcome of selecting a radio button

I am currently working on a script that aims to utilize Ajax in order to post the result of a radio button click. My progress so far includes: <label> <input type="radio" name="DisableAd" value="0" id="RadioGroup1_0" /> Radio </lab ...

Tips for creating animations with multiple elements that share the same classes

I am trying to create expandable boxes that can open onclick and close again by clicking on the X. The issue I'm facing is that the close jQuery function isn't working. However, my main concern is how to optimize the code so it doesn't becom ...

Creating an Interactive Table Using HTML, JavaScript, and PHP?

After reviewing this project, I am looking to customize the js function in relation to my own table. The key focus is on the following code: $("#employee_table tr:last").after("<tr id='row"+$rowno+"'><td><input type='text&apo ...

Utilize various addMethods on a field in the event a radio button is chosen through the jQuery validator plugin

When a specific radio button value is selected, I need to apply different methods to a single field. If "true" is selected, method X should be applied. If "false" is selected, method Y should be used on the same field. I attempted to achieve this with a ...

Tips for incorporating a page route with an HTML extension in Next.js

I'm facing a challenge in converting a non-Next.js page to Next.js while maintaining my SEO ranking. To preserve the route structure with HTML extensions and enhance visual appeal, I have outlined the folder structure below: https://i.sstatic.net/zO1 ...

Having trouble accessing the href attribute after clicking on an <a> tag with JavaScript

My issue lies in retrieving the href attribute of <a> tags when there is another element nested inside them: Here's the code snippet: const $elems = document.querySelectorAll('.content-container a') var elems = Array.from($elems) e ...

Display or conceal div elements using JavaScript

Is there a way to hide certain divs when clicking on a specific div box? Clicking on the box will trigger the hiding of some application divs. See the first image below: When the boxes are hidden, the other divs will readjust in place of the current divs ...

Executing `removeChild` within a timeout during page load does not yield the expected results

I have an HTML div that is designed to contain dynamically generated children. These children are meant to be removed from the list after a specific amount of time (e.g. 1000 ms). Although some people have experienced scope issues with timeout functions, ...

Passing data between Vue.js components effortlessly without relying on events

At the moment, I am utilizing Laravel Nova. It's worth noting that it operates using vue.js. I've created a personalized vue component which requires the ability to modify data inside another component. This specific component is located in the ...

How to showcase arrays using a variety of vibrant colors

In my web application, users input a paragraph and the backend system scans it to identify hard and very hard sentences. My goal is to display the entire paragraph with these difficult sentences highlighted in yellow and red backgrounds respectively, while ...

Animating planeGeometry with 2D texture/sprite in Three.js

As a beginner in html5 and three.js, I have been playing around with Mesh objects. My goal is to display different Textures on the Mesh and be able to change them later on. Here is a snippet of my code: angelTexture = THREE.ImageUtils.loadTexture("images ...