I'm creating a program to interact with music, and I was trying to synchronize the drum beats or the bass sounds with CSS, so in every high frequency sound, the background would change just in time.
My first idea was create an spectrum of the audio frequency, to understand how does it interacts with the sound played. In this case, I'm using Web Audio API analyserNode.fftSize
to get all the points of the frequency in a audio file and create the spectrum with <canvas>
const audioCtx = new AudioContext();
//Create audio source
//Here, we use an audio file, but this could also be e.g. microphone input
const audioEle = new Audio();
audioEle.src = "good-day.mp3"; //insert file name here
audioEle.autoplay = true;
audioEle.preload = "auto";
const audioSourceNode = audioCtx.createMediaElementSource(audioEle);
//Create analyser node
const analyserNode = audioCtx.createAnalyser();
analyserNode.fftSize = 256;
const bufferLength = analyserNode.frequencyBinCount;
const dataArray = new Float32Array(bufferLength); // this is where all the frequencies are stored
Then, using this dataArray
with all the frequencies of that moment, I run a condition that says: if that frequency is above X, change background to blue:
if (Math.max(...dataArray) + 100 > 70) { // +- 70 is a good number for that specific song
await changeBgColor();
}
The final results seems to work, but isn't perfect, since that if
condition runs 60 times per second and sometimes doesn't catch the bass playing because of a voice or another sound in the audio file that mess with the frequency of the bass.
I don't know if there is something that really can be useful for this. I tried to use ToneJS and wadJS but I couldn't get anywhere with these libs.
I wonder what could be the best way to make these CSS iteration with sounds.
The song used for test this code was: Ice Cube - It Was A Good Day (good-day.mp3)
Full code:
index.html
<!DOCTYPE html>
<body id="bd">
<script>
const audioCtx = new AudioContext();
//Create audio source
//Here, we use an audio file, but this could also be e.g. microphone input
const audioEle = new Audio();
audioEle.src = "good-day.mp3"; //insert file name here
audioEle.autoplay = true;
audioEle.preload = "auto";
const audioSourceNode = audioCtx.createMediaElementSource(audioEle);
//Create analyser node
const analyserNode = audioCtx.createAnalyser();
analyserNode.fftSize = 256;
const bufferLength = analyserNode.frequencyBinCount;
const dataArray = new Float32Array(bufferLength);
//Set up audio node network
audioSourceNode.connect(analyserNode);
analyserNode.connect(audioCtx.destination);
//Create 2D canvas
const canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.top = 0;
canvas.style.left = 0;
canvas.width = window.innerWidth;
canvas.height = 300;
document.body.appendChild(canvas);
const canvasCtx = canvas.getContext("2d");
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
async function draw() {
const changeBgColor = () => {
document.getElementById("bd").style["background-color"] = "blue";
};
const sleep = () => {
// setTimeout(() => null, 1000)
console.log("bass sound");
};
//Schedule next redraw
requestAnimationFrame(draw);
//Get spectrum data
analyserNode.getFloatFrequencyData(dataArray);
//Draw black background
canvasCtx.fillStyle = "rgb(0, 0, 0)";
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
//Draw spectrum
const barWidth = (canvas.width / bufferLength) * 2.5;
let posX = 0;
document.getElementById("bd").style["background-color"] = "red";
if (Math.max(...dataArray) + 100 > 70) { // +- 70 is a good number for that specific song
await changeBgColor();
}
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] + 100) * 2;
// console.log(barHeight)
canvasCtx.fillStyle = `rgb(${Math.floor(
barHeight + 100
)}, 255, ${Math.floor(barHeight + 200)})`;
canvasCtx.fillRect(
posX,
canvas.height - barHeight / 2,
barWidth,
barHeight / 2
);
posX += barWidth + 1;
}
}
draw();
</script>
</body>
My reference to this code was a doc in MDN about AnalyserNode