Skip to content

Instantly share code, notes, and snippets.

@rileyjshaw
Created March 28, 2015 21:58
Show Gist options
  • Save rileyjshaw/c69e2384d9ef28357fa7 to your computer and use it in GitHub Desktop.
Save rileyjshaw/c69e2384d9ef28357fa7 to your computer and use it in GitHub Desktop.
Waveform visualizer
canvas
p loading audio...
!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);
});
});
}();
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment