javascriptcssaudiosynchronization

Sync CSS with Audio Loud Noise via Javascript


I am totally a non-musical person and I have no idea how to find a solution for this. I have a simple CSS animation that I want to fire via Javascript every time a thunder (peak of the sound bar) is detected in the audio playing in the background.

I have found all sorts guides with javascript regarding audio sync, but they all relate to creating global audio bars for the whole background noise, while I just want for one particular one.

CSS Animation:

<style> 
div {
width: 100px;
height: 100px;
background: red;
position: relative;
animation: mymove 5s 1 ease-in-out;
}

@keyframes mymove {
from {top: 0px;}
to {top: 200px;}
}
</style>

<div></div>

<script>
window.onload = function() {
new Audio("./070115-glorious-early-morning-t-storm-19045.mp3").play();
};
</script>

Audio Track: https://pixabay.com/sound-effects/070115-glorious-early-morning-t-storm-19045/

Thank you!


Solution

  • Using the AudioContent you can analyze the audio data in real-time by AnalyserNode with audioContext.createAnalyser();, where you check if the threshold of the audio ( at this moment ) exceeds a specific value, then run the animation.

    window.onload = async function() {
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const audioElement = new Audio("./070115-glorious-early-morning-t-storm-19045.mp3");
        const track = audioContext.createMediaElementSource(audioElement);
        const analyser = audioContext.createAnalyser();
        track.connect(analyser);
        analyser.connect(audioContext.destination);
        audioElement.play();
    
        function detectThunder() {
            const dataArray = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteTimeDomainData(dataArray);
            let maxVal = Math.max(...dataArray) - 128;
    
            if (maxVal > 25) { // Threshold for "loud" noise, adjust as needed
                document.getElementById('animatedDiv').classList.add('thunder');
            } else {
                document.getElementById('animatedDiv').classList.remove('thunder');
            }
    
            requestAnimationFrame(detectThunder); // Keep looping to check sound
        }
    
        detectThunder();
    };
    div {
        width: 100px;
        height: 100px;
        background: red;
        position: relative;
        animation-name: mymove;
        animation-duration: 5s;
        animation-timing-function: ease-in-out;
        animation-fill-mode: forwards;
        animation-play-state: paused; /* Initially paused */
    }
    
    @keyframes mymove {
        from {top: 0px;}
        to {top: 200px;}
    }
    
    .thunder {
        animation-play-state: running;
    }
    <div id="animatedDiv"></div>