AUDIOSTORM


"use strict";
var datastorm = datastorm || {};

datastorm.audioStorm = (function(){
  var my = {};

  var config = {};

  var music = null;

  window.AudioContext = window.AudioContext || window.webkitAudioContext;
  var context = new AudioContext();
  var analyser;

  var animationActive = true;

  var dataArray, bufferLength, source;

  var ctx = datastorm.canvas.ctx;

  var width = document.body.clientWidth;
  var height = document.body.clientHeight;

  var waveXSpread = 0.1;
  var xScale = d3.scale.linear().domain([0, 255]).range([(0.5 - waveXSpread) * width, (0.5 + waveXSpread) * width]);
  var yScale = d3.scale.linear().domain([0, 512]).range([0, height]);

  var lightningWaveXSpread = 0.15;
  var lightningXScale = d3.scale.linear().domain([0, 255]).range([(0.5 - lightningWaveXSpread) * width, (0.5 + lightningWaveXSpread) * width]);
  var xRandomOffset = 1000;

  var yStep = 100;

  var frameTimer, decreaseTimeStepTimer, clearFrameTime, expandXTimer, increaseXRandomOffsetTimer, decreaseYStepTimer;
  var timeInterval = 1300;

  function clearFrame() {
    ctx.globalAlpha = 1;
    ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
    ctx.fillRect(0, 0, width, height);
  }

  function getSumOfDeltas(data) {
    var sum = 0;
    for(var i = 1; i < bufferLength; i ++) {
      sum += Math.abs(dataArray[i] - dataArray[i-1]);
    }
    return sum;
  }

  function chanceOfLightning(sumOfDeltas) {
    if(sumOfDeltas < 600)
      return 0;
    return sumOfDeltas / 15000;
  }

  function doFrame() {
    analyser.getByteTimeDomainData(dataArray);

    ctx.strokeStyle = '#689';
    ctx.lineWidth = 1;
    ctx.globalAlpha = 1;

    var chance = chanceOfLightning(getSumOfDeltas(dataArray));

    if(Math.random() > chance) {
      // normal

      var xOffset = (Math.random() * xRandomOffset) - 0.5 * xRandomOffset;

      for(var i = 1; i < bufferLength; i += yStep) {
        var ii = i - yStep ;
        datastorm.canvas.drawLine(xScale(dataArray[ii]) + xOffset, yScale(ii), xScale(dataArray[i]) + xOffset, yScale(i));
      }
    } else {
      // ctx.strokeStyle = '#7DF9FF';
      ctx.globalAlpha = Math.random() * 0.5 + 0.5;
      var xOffset = 0.5 * (Math.random() * width - 0.5 * width);
      // ctx.lineWidth = 2;
      for(var i = 1; i < bufferLength; i += yStep) {
        var ii = i - yStep ;
        datastorm.canvas.drawLine(lightningXScale(dataArray[ii]) + xOffset, yScale(ii), lightningXScale(dataArray[i]) + xOffset, yScale(i));
      }
    }
  }

  function expandX() {
    if(waveXSpread > 0.3)
      return;

    waveXSpread += 0.1;
    xScale.range([(0.5 - waveXSpread) * width, (0.5 + waveXSpread) * width]);
  }

  function increaseXRandomOffset() {
    if(xRandomOffset < 1)
      return;

    xRandomOffset -= 50;
  }

  function decreaseYStep() {
    yStep -= 5;

    if(yStep < 1)
      yStep = 1;
  }

  function decreaseTimeStep() {
    if(timeInterval <= 100)
      return;

    clearInterval(frameTimer);

    timeInterval -= 200;
    if(timeInterval < 100)
      timeInterval = 100;

    frameTimer = setInterval(doFrame, timeInterval);
  }

  function ready(buffer) {
    music = buffer;

    source = context.createBufferSource();
    source.buffer = music;

    analyser = context.createAnalyser();
    analyser.fftSize = 1024;
    bufferLength = analyser.frequencyBinCount;
    dataArray = new Uint8Array(bufferLength);
    source.connect(analyser);

    analyser.connect(context.destination);

    if(config.autoStart === true)
      my.start();
  }

  my.init = function(conf) {

    config = conf;
    var url = config.audioUrl;

    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
      context.decodeAudioData(request.response, ready);
    }
    request.send();

  }

  my.start = function() {
    source.start(0);

    frameTimer = setInterval(doFrame, timeInterval);
    decreaseTimeStepTimer = setInterval(decreaseTimeStep, 20000);
    clearFrameTime = setInterval(clearFrame, 40);
    expandXTimer = setInterval(expandX, 30000);
    increaseXRandomOffsetTimer = setInterval(increaseXRandomOffset, 10000);
    decreaseYStepTimer = setInterval(decreaseYStep, 5000);    
  }

  my.stop = function() {
    animationActive = false;

    clearInterval(frameTimer);
    clearInterval(decreaseTimeStepTimer);
    clearInterval(clearFrameTime);
    clearInterval(expandXTimer);
    clearInterval(increaseXRandomOffsetTimer);
    clearInterval(decreaseYStepTimer);

    source.stop();
  }

  return my;
}());



datastorm.audioStorm.init({
  audioUrl: 'https://d28qoto45d39ov.cloudfront.net/datastorm/visualisations/audiostorm/sounds/allegretto.mp3',
  autoStart: true
});

                

See the Pen Datastorm - Audiostorm by Genevieve Smith-Nunes (@readysaltedcode) on CodePen.