javascriptaudioweb-audio-apivisualize

Creating a bouncing circle while playing audio


itemCurrently when I hover a HTML element, a song is played. Now, I would like to create a simple circle reacting with that sound (bouncing, or vibrating effect), I just don't know where to look to achieve that kind of stuff maybe plain Javascript or the Web Audio API ?

The code to play sound:

 $('.item').on('mouseenter', function(){
    itemId = $(this).attr('data-id');

    if (document.getElementById(coverId).tagName == 'AUDIO' ) {
        $('#' + itemId).trigger("play");
    }
});
$('.item').on('mouseleave', function(){
    itemId = $(this).attr('data-id');

    if (document.getElementById(itemId).tagName == 'AUDIO' ) {
        // For audio replay
        var audio = document.getElementById(itemId);
        audio.pause();
        audio.currentTime = 0;
        audio.load();
    } else {
        $('#' + itemId).trigger("pause");
    }
});

Any help would be very appreciated! Thanks!


Solution

  • It's not too complicated. First you need to load the audio through Web Audio API - you can do this directly using a AJAX request, or use an Audio element which you tap into. Then:

    Note that the audio you're loading must be from same origin or provided by a server allowing cross-origin use.

    Note also that Web Audio API is not supported in IE.

    Example

    Warning: check your audio volume before running code!

    Adopt as needed -

    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    
    var ctx = c.getContext("2d"),                  // canvas context
        actx = new AudioContext(),                 // audio context
        cx = c.width * 0.5, cy = c.height * 0.5,   // center of canvas
        radiusMax = Math.min(cx, cy) - 20,         // circle min/max radii
        radiusMin = radiusMax * 0.1,
        analyzer, srcNode, bquad, fftLen, fft;     // audio nodes and FFT data
    
    ctx.fillStyle = "#fff";
    ctx.fillText("Loading audio...", 10, 10);
    
    a.oncanplay = function() {
    
      if (srcNode) return;
      
      // link audio element and Web Audio API
      srcNode = actx.createMediaElementSource(this);
      
      // create filter node
      bquad = actx.createBiquadFilter();
      bquad.type = "lowpass";
      bquad.frequency.value = 250;
      
      // create FFT analyzer node
      analyser = actx.createAnalyser();
      analyser.fftSize = 256;
    
      // connect nodes: srcNode (input) -> analyzer -> destination (output)
      srcNode.connect(bquad);
      bquad.connect(analyser);
      
      // connnect source directly to output
      srcNode.connect(actx.destination);
    
      // create FFT data buffer
      fftLen = analyser.frequencyBinCount;
      fft = new Uint8Array(fftLen);
     
      // set up arc look
      ctx.lineWidth = 12;
      ctx.strokeStyle = "#09f";
      
      ctx.fillStyle = "rgba(0,0,0,0.16)";
      
      // start visual galore
      render()
    };
    
    function render() {
    
      // clear semi-transparent
      ctx.fillRect(0, 0, c.width, c.height);
      
      // fill FFT buffer
      analyser.getByteFrequencyData(fft);
      
      // average data from some bands
      v = (fft[1] + fft[2]) / 512;
      
      // draw arc using interpolated range with exp. of v
      ctx.beginPath();
      ctx.arc(cx, cy, radiusMin + (radiusMax - radiusMin) * v*v*v*v, 0, 6.28);
      ctx.closePath();
      ctx.stroke();
      
      // feedback effect
      ctx.drawImage(c, -8, -8, c.width + 16, c.height + 16);
      
      requestAnimationFrame(render)
    }
    body, #c {background:#000}
    #c {border:1px solid #222}
    <canvas id=c height=300></canvas><br>
    <audio id=a crossOrigin="anonymous" autoplay loop 
           src="https://cdn.rawgit.com/epistemex/SO-Demo-3-in-1-video/gh-pages/k3n_thebattle_segbass.mp3">

    Music © K3N / freely distributable for test purposes.