I am visualizing web audio data with d3. I continuously run the function that appends the data to d3 by using requestanimationframe. When I console log the frequency data in the recursive function call, I get the result as expected. However, when I bind the data to an svg and attempt to create elements with d3, the loop only runs twice.
I tried passing different data sets to the data binding, but each outcome only calls the function twice. For example:
import createBoomBox from './boombox';
const frequencyData = new Uint8Array(200);
const createFrequency = (analyser) => {
requestAnimationFrame(() => createFrequency(analyser));
analyser.getByteFrequencyData(frequencyData);
createBoomBox(frequencyData);
};
const createAudio = (audioId) => {
console.log('in audio');
const audioCtx = new (window.AudioContext ||
window.webkitAudioContext)();
const audioElement = document.getElementById(audioId);
const audioSrc = audioCtx.createMediaElementSource(audioElement)
|| window.audiosrc;
window.audioSrc = audioSrc;
const analyser = audioCtx.createAnalyser();
audioSrc.connect(analyser);
audioSrc.connect(audioCtx.destination);
createFrequency(analyser);
};
export default createAudio;
Is where I create the audio context and pass the frequency data into createBoomBox. createBoomBox looks as follows:
const svgHeight = 200;
const svgWidth = 200;
const boomBox = d3.selectAll('.boom-box');
const svg = boomBox.append('svg')
.attr('height', svgHeight)
.attr('width', svgWidth);
const createBoomBox = (frequencyData) => {
const rangeScale = d3.scaleLinear()
.domain([0, d3.max(frequencyData)])
.range([0, svgHeight]);
const hslScale = d3.scaleLinear()
.domain([0, d3.max(frequencyData)])
.range([0, 360]);
console.log(frequencyData);
svg.selectAll('circle')
.data(frequencyData)
.enter()
.append('circle')
.attr('r', d => rangeScale(d))
.attr('cx', svgWidth / 2)
.attr('cy', svgHeight / 2)
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('stroke-opacity', 1)
.attr('stroke', d => d3.hsl(hslScale(d), 1, 0.5))
.exit()
.remove();
};
export default createBoomBox;
The problem occurs in the createBoomBox function, at
svg.selectAll
createBoomBox is repeatedly called with requestAnimationFrame. When console log the frequency data right above the svg.selectAll line, I get the result as expected. Namely, an array in the form of Uint8Array with frequency data.
However, when I console log the data inside the
svg.selectAll('circle')
.data(frequencyData)
.enter()
.append('circle')
.attr('r', d => rangeScale(d))
.attr('cx', svgWidth / 2)
.attr('cy', svgHeight / 2)
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('stroke-opacity', 1)
.attr('stroke', d => d3.hsl(hslScale(d), 1, 0.5))
.exit()
.remove();
The iteration in the above code block only occurs twice. So even though the entire function is being repeatedly called through requestAnimationFrame, the svg.selectAll block only gets run twice. Any idea as to what is causing the problem?