<div class="audio-viz">
	<h1 class="audio-viz__title">WebAudio Visualizer</h1>

	<form class="audio-viz__form">
    <input type="radio" class="audio-viz__radio" id="senventies" name="radio-selection" value="http://ice1.somafm.com/seventies-128-aac" checked>
    <label for="senventies"><span></span>70's Radio</label>

    <input type="radio" class="audio-viz__radio" id="eighties" name="radio-selection" value="http://ice1.somafm.com/u80s-128-aac">
    <label for="eighties"><span></span>80's Radio</label>

		<input type="radio" class="audio-viz__radio" id="deep-space" name="radio-selection" value="http://ice1.somafm.com/deepspaceone-128-aac">
    <label for="deep-space"><span></span>Deep Space</label>
	</form>

	<canvas id="oscilloscope"></canvas>
	<button class="audio-viz__btn" id="start">Start Audio</button>
</div>
$bg: #4674FA;
html,body{margin:0;padding:0;height:100%;}body{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;background:$bg;}

.audio-viz__form {
	max-width: 350px;
	margin: 1rem auto 1rem;
	font-family: 'Roboto', sans-serif;
	color: white;
}

.audio-viz__radio + label:hover {
	cursor: pointer;
}

.audio-viz__radio {
  display: none;
}

.audio-viz__radio + label span {
	border: 2px solid #fff;
	border-radius: 80%;
	font-size: 1rem;
	display: inline-block;
	width: 10px;
	height: 10px;
	margin: 4px 8px 8px 8px;
	padding: 2px;
	text-align: center;
	vertical-align: middle;
}

.audio-viz__radio:checked + label span {
	background: #1F007E;
}

.audio-viz {
	width: 100%;
}

.audio-viz__title {
	margin-bottom: 0.5rem;
	font-family: 'Oleo Script', cursive;
	font-size: 2.5em;
	text-align: center;
	color: white;
}

.audio-viz__btn {
	appearance: none;
	border: none;
	border-radius: 4px;
	box-shadow: inset 0px 1px 0px 0px rgba(255, 255, 255, .4), 2px 6px 10px 0px rgba(0, 0, 0, 0.2);
	width: 100%;
	max-width: 200px;
	margin: auto;
	display: flex;
	justify-content: center;
	outline: none;
	margin-top: 0.75rem;
	padding: 1em;
	position: relative;
	z-index: 1;
	font-family: 'Roboto', sans-serif;
	font-size: 0.875rem;
	background: #5FA0FB;
	color: white;
	&:hover {
		cursor: pointer;
	}
}

#oscilloscope {
	border-top: 4px solid #e6ebf1;
	border-bottom: 4px solid #e6ebf1;
	height: 200px;
	width: 100%;
	background-color: darken(#5661F9, 40%);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%239C92AC' fill-opacity='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
	background-position: -35px 95%;
}
View Compiled
// @ref
// https://www.w3.org/TR/webaudio
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement
// https://noisehack.com/build-music-visualizer-web-audio-api

// ========================================================
// Global Config
// ========================================================

let start_button  = document.getElementById('start'),
		radios        = document.querySelectorAll('input[name="radio-selection"]'),
		radios_length = radios.length,
		audioContext,
		masterGain;


// ========================================================
// Audio Setup
// ========================================================

function audioSetup() {
	let source;

	audioContext = new (window.AudioContext || window.webkitAudioContext)();
  masterGain = audioContext.createGain();
  masterGain.connect(audioContext.destination);

	for(var i = 0, max = radios_length; i < max; i++) {
		if(radios[i].checked === true) {
			source = radios[i].value;
		}
	}

	let song        = new Audio(source),
			songSource  = audioContext.createMediaElementSource(song),
			songPlaying = false;

	song.crossOrigin = 'anonymous';
	songSource.connect(masterGain);

	for(var i = 0, max = radios_length; i < max; i++) {
		radios[i].addEventListener('change', function() {
			if(songPlaying) {
				song.pause();
				start_button.innerHTML = 'Start Audio';
				songPlaying = !songPlaying;
			}

			// Without these lines the oscilloscope won't update
			// when a new selection is made via radio inputs
			song = new Audio(this.value);
			songSource  = audioContext.createMediaElementSource(song),
			song.crossOrigin = 'anonymous';
			songSource.connect(masterGain);
		});
	}

  start_button.addEventListener('click', function() {
    if(songPlaying) {
      song.pause();
			start_button.innerHTML = 'Start Audio';
    } else {
      song.play();
			drawOscilloscope();
			updateWaveForm();
			start_button.innerHTML = 'Stop Audio';
    }

		songPlaying = !songPlaying;
  });
}

audioSetup();


// ========================================================
// Create Wave Form
// ========================================================

const analyser = audioContext.createAnalyser();
masterGain.connect(analyser);

const waveform = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatTimeDomainData(waveform);

function updateWaveForm() {
  requestAnimationFrame(updateWaveForm);
  analyser.getFloatTimeDomainData(waveform);
}


// ========================================================
// Draw Oscilloscope
// ========================================================

function drawOscilloscope() {
	requestAnimationFrame(drawOscilloscope);

	const scopeCanvas = document.getElementById('oscilloscope');
	const scopeContext = scopeCanvas.getContext('2d');

	scopeCanvas.width = waveform.length;
	scopeCanvas.height = 200;

	scopeContext.clearRect(0, 0, scopeCanvas.width, scopeCanvas.height);
  scopeContext.beginPath();

	for(let i = 0; i < waveform.length; i++) {
    const x = i;
    const y = ( 0.5 + (waveform[i] / 2) ) * scopeCanvas.height;
		

		if(i == 0) {
      scopeContext.moveTo(x, y);
    } else {
      scopeContext.lineTo(x, y);
    }
  }

	scopeContext.strokeStyle= '#5661FA';
	scopeContext.lineWidth = 2;
	scopeContext.stroke();
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.