Superposition, baby!
A Pen by Riley Shaw on CodePen.
| 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 | |
Superposition, baby!
A Pen by Riley Shaw on CodePen.