Solomko2
3/28/2015 - 9:58 PM

Waveform visualizer

Waveform visualizer


body
  background: #000
  
canvas, p
  position: absolute
  top: 50%
  left: 50%
  transform: translate(-50%, -50%)
  
canvas
  cursor: pointer
  
  &:hover + p.fa
    display: block
  
p
  pointer-events: none
  margin: 0
  font-family: monospace
  color: #fff
  background: #000
  
  &.fa-play
    padding-left: 0.8em
    
  &.fa
    display: none
    padding: 0.6em
    border: 4px solid #fff
    border-radius: 50%
    font-size: 24px
    
!function () {
	// play with these
  var MIN_RADIUS = 100;
  var JITTER_RANGE = 35;
  var HEIGHT = 100;
  var RESOLUTION = 2; // one point every 2
  var NUM_NODES = 512; // only 1/2 of these are actually drawn
  var COLOR = [255, 255, 255];
  
  // derived
  var CENTER = HEIGHT / 2;
  
  // math
  var PI2 = Math.PI * 2;
  var sin = Math.sin;
  var cos = Math.cos;

  // canvas
  var canvas = document.querySelector('canvas');
  var ctx = canvas.getContext('2d');
  canvas.height = HEIGHT;
  canvas.width = NUM_NODES;

  // main loop
  function animate (analyser, SAMPLE_RATE) {
    var binCount = analyser.frequencyBinCount;
    var freqArray = new Uint8Array(binCount);
    
    !function draw () {
      analyser.getByteFrequencyData(freqArray);

      ctx.beginPath();
      ctx.strokeStyle = 'rgb(' + COLOR + ')';
      ctx.lineWidth = 2;

      for (var t = 0, node; t < binCount; t += RESOLUTION) {
        node = 0;
        for (var i = 0, f, A; i < binCount; ++i) {
          f = i * SAMPLE_RATE / NUM_NODES;
          A = freqArray[i];
          node += A * sin(PI2 * f * t);
        }
        ctx.lineTo(t, CENTER + node / NUM_NODES * HEIGHT / 8);
      }

      // close the loop and draw the stroke
      ctx.clearRect(0, 0, NUM_NODES, HEIGHT);
      ctx.stroke();

      requestAnimationFrame(draw);
    }();
  }
  
  function loadSound (url, cb) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    request.onload = function () { cb(request.response); };
    request.send();
  }
  
  // load the mp3 and initialize our visualizer
  loadSound('//katiebaca.com/tutorial/odd-look.mp3', function (res) {
    var audioContext = new (window.AudioContext || window.webkitAudioContext)();
    var SAMPLE_RATE = audioContext.sampleRate;
    audioContext.decodeAudioData(res, function (buffer) {
      var analyser = audioContext.createAnalyser();
      var sourceNode = audioContext.createBufferSource();
      analyser.smoothingTimeConstant = 0.6;
      analyser.fftSize = NUM_NODES * 2;
      analyser.minDecibels = -90;
      analyser.maxDecibels = -10;
      sourceNode.buffer = buffer;
      analyser.connect(audioContext.destination);
      sourceNode.connect(analyser);
      sourceNode.start(0);
      animate(analyser, SAMPLE_RATE);
      
      // add play/pause control
      var playing = true;
      var control = document.querySelector('p');
      control.className = 'fa fa-pause';
      control.textContent = '';
      
      canvas.addEventListener('click', function () {
				sourceNode[(playing ? 'dis' : '') + 'connect'](analyser);
        control.className = 'fa fa-' + (playing ? 'play' : 'pause');
        playing = !playing;
			}, false);
    });
  });
}();
canvas
p loading audio...

Waveform visualizer

Superposition, baby!

A Pen by Riley Shaw on CodePen.

License.