skip to Main Content

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!

2

Answers


  1. 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>
    Login or Signup to reply.
  2. To do this, you’ll need to analyze the audio data in real-time to detect peaks and then trigger the CSS animation, which you can do with AudioContext. I have console.log() your Peak, which is more than 17. And also according to the new browser policy, the user must first interact with the DOM before playing any Audio element. So that’s Why I have also used click EventListener to make it work.

    Styling part:

    div {
      width: 100px;
      height: 100px;
      background: red;
      position: relative;
    }
    
    @keyframes mymove {
      from {top: 0px;}
      to {top: 200px;}
    }
    
    .animate {
      animation: mymove 0.5s 1 ease-in-out;
    }
    

    HTML part:

    <div id="thunder-box"></div>
    

    JavaScript part:

    window.onload = function() {
      const thunderBox = document.getElementById('thunder-box');
      thunderBox.addEventListener('click', function() {
        console.log('Thunder box clicked');
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const audioElement = new Audio("sound.mp3"); // replace your own sound url
        const track = audioContext.createMediaElementSource(audioElement);
        const analyser = audioContext.createAnalyser();
        track.connect(analyser);
        analyser.connect(audioContext.destination);
    
        analyser.fftSize = 256;
        const bufferLength = analyser.frequencyBinCount;
        const dataArray = new Uint8Array(bufferLength);
    
        audioElement.play();
        console.log('Audio playing');
    
        const detectPeaks = () => {
          analyser.getByteFrequencyData(dataArray);
    
          let sum = 0;
          for (let i = 0; i < bufferLength; i++) {
            sum += dataArray[i];
          }
          const average = sum / bufferLength;
          console.log('Average frequency value:', average);
          if (average >= 17) { // Adjust this threshold based on the audio
            console.log('Peak detected');
            triggerAnimation();
          }
    
          requestAnimationFrame(detectPeaks);
        };
    
        const triggerAnimation = () => {
          console.log('Triggering animation');
          thunderBox.classList.remove('animate');
          // Trigger a reflow, flushing the CSS changes
          void thunderBox.offsetWidth;
          thunderBox.classList.add('animate');
        };
    
        detectPeaks();
      }, { once: true });
    };
    

    Hope it helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search