<div class="canvas" name="spectrum">
<audio id="audio1" src="Audio1.ogg" controls></audio>
<canvas id="spectrum" width="512" height="256"></canvas>
<script id="spectrum.js">
(function() {
var canvas      = document.getElementById('spectrum');
var ctx         = canvas.getContext('2d');
var audio       = document.getElementById('audio1');

var toggle = 0;
canvas.onclick = function() {toggle = (toggle + 1) % 2;}

var audioCtx     = new AudioContext();
var analyser     = audioCtx.createAnalyser();
analyser.fftSize = 256;
var data         = new Uint8Array(analyser.frequencyBinCount);
var source       = audioCtx.createMediaElementSource(audio);
source.connect(analyser);
source.connect(audioCtx.destination);

var id = 0;
audio.onplay  = function() {id = requestAnimationFrame(draw);};
audio.onpause = function() {cancelAnimationFrame(id);};

function draw() {
	analyser.getByteFrequencyData(data);

	if (toggle == 0) {
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		ctx.fillStyle = "brown";
		for (var i = 0; i < data.length; i++) {
			var amplitude = data[i];
			ctx.fillRect(i * 4, canvas.height, 3, -amplitude);
		}
	} else if (toggle == 1) {
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		ctx.lineWidth = 1;
		ctx.strokeStyle = "black";
		ctx.beginPath();
		ctx.moveTo(-2, canvas.height - data[0]);
		for (var i = 0; i < data.length; i++) {
			var amplitude = data[i];
			ctx.lineTo(i * 4 + 2, canvas.height - amplitude);
		}
		ctx.stroke();
	}

	ctx.textBaseline = 'top';
	ctx.fillText("click to toggle", 0, 0);

	id = requestAnimationFrame(draw);
}
})();
</script>
</div><div class="canvas" name="spectrogram">
<audio id="audio2" src="Audio1.ogg" controls></audio>
<canvas id="spectrogram" width="500" height="256"></canvas>
<script id="spectrogram.js">
(function() {
var canvas       = document.getElementById('spectrogram');
var ctx          = canvas.getContext('2d');
var audio        = document.getElementById('audio2');
var audioCtx     = new AudioContext();
var analyser     = audioCtx.createAnalyser();
analyser.fftSize = 512;
var data         = new Uint8Array(analyser.frequencyBinCount);
var source       = audioCtx.createMediaElementSource(audio);
source.connect(analyser);
source.connect(audioCtx.destination);

audio.onloadedmetadata = function() {
	canvas.width = Math.ceil(audio.duration * 20);
};

var id = 0;
audio.onplay  = function() {id = requestAnimationFrame(draw);};
audio.onpause = function() {cancelAnimationFrame(id);};

function draw() {
	analyser.getByteFrequencyData(data);
	var x = audio.currentTime * 20;

	for (var i = 0; i < data.length; i++) {
		c = Math.round(data[i]);
		ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
		ctx.fillRect(x, canvas.height - i, 1, 1);
	}

	id = requestAnimationFrame(draw);
}
})();
</script>
</div><div class="canvas" name="sinewave">
<canvas id="sinewave" width="430" height="64"></canvas>
<style>
.music {
display: block;
margin: .5em auto .5em auto;
padding-left: 1.5em;
border: 3px groove yellow;
border-radius: 4px;
background: gold url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' height='20px' width='20px'><text y='16'>🎵</text></svg>") left center no-repeat;
}
</style>
<input class="music" id="sinewave_input" type="text" size="70" maxlength="50" value="533-422-1234555-"></input>
<script id="sinewave.js">
(function() {
var canvas       = document.getElementById('sinewave');
var ctx          = canvas.getContext('2d');

ctx.font = "32pt Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "green";
ctx.fillText("click me", canvas.width/2, canvas.height/2);

var c = 0;
var loop = 0, id = 0;
var fps = 20, fpsInterval = 1000 / fps;
var now, then, elapsed;
canvas.onclick = function() {
	c = 0;
	loop = 1000;
	then = Date.now();
	if (!id) id = requestAnimationFrame(draw);
};

function draw() {
	id = requestAnimationFrame(draw);
	now = Date.now();
	elapsed = now - then;
	if (elapsed < fpsInterval) return;
	then = now - (elapsed % fpsInterval);

	var xunit = 1;
	var yunit = 32;
	var n = 430;
	var speed = 40;

	var y = Math.sin(Math.PI * 2 * c / n) * Math.pow(0.99, c/10);
	c += speed;

	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.lineWidth = 2;
	ctx.strokeStyle = "rgb(192,0,0)";
	ctx.beginPath();
	function f(n) {
		if (n >= 0) return Math.sqrt(n);
		else return -Math.sqrt(-n);
	}
	for (var i = 0; i < n/2; i++)
		ctx.lineTo(i*xunit, canvas.height / 2 + f(y * i / n) * yunit);
	for (var i = n/2; i < n; i++)
		ctx.lineTo(i*xunit, canvas.height / 2 + f(y * (n-i) / n) * yunit);
	ctx.stroke();

	if (--loop) id = requestAnimationFrame(draw);
	else id = 0;
}
})();
</script>
</div><div class="canvas" name="waveguide">
<canvas id="waveguide" width="430" height="64"></canvas>
<input class="music" id="waveguide_input" type="text" size="70" maxlength="50" value="533-422-1234555-"></input>
<script id="waveguide.js">
(function() {
var canvas       = document.getElementById('waveguide');
var ctx          = canvas.getContext('2d');

ctx.font = "32pt Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "green";
ctx.fillText("click me", canvas.width/2, canvas.height/2);

var loop = 0, id = 0;
canvas.onclick = function() {
	init();
	loop = 1000; 
	if (!id) id = requestAnimationFrame(draw);
};

var data;
var c = 0;
function init() {
	c = 0;
	var R = 48000, f = 440.00, N = Math.floor(R / f);
	data = new Float32Array(N);
	for (var i=0; i<N; ++i) data[i] = (Math.random() - 0.5) * 2;
}

function draw() {
	var xunit = 8;
	var yunit = 64;
	var n = data.length;
	for (var i=0; i<4800; ++i) {
		data[c] = data[c] * 0.95 + data[(c+1+n)%n] * (1.0 - 0.95);
		c = (c + 1) % n;
	}

	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.lineWidth = 2;
	ctx.strokeStyle = "rgb(192,0,0)";
	ctx.beginPath();
	for (var i = 0; i < n/2; i++) {
		var y = Math.round((data[(c+i)%n] + data[(c+n-1-i)%n]) * yunit);
		ctx.lineTo(i*xunit, canvas.height / 2 + y);
	}
	ctx.stroke();

	if (--loop) id = requestAnimationFrame(draw);
	else id = 0;
}
})();
</script>
<script id="waveguide.input.js">
(function() {
document.getElementById('sinewave_input').onkeypress = function(event) {
	if (event.keyCode != 13) return;
	var cmd = event.target.value;
	var sampleRate = 8000, beat = 4;
	var src = waveguide(cmd, sampleRate, beat, 'sinewave');
	play(src, sampleRate);
}

document.getElementById('waveguide_input').onkeypress = function(event) {
	if (event.keyCode != 13) return;
	var cmd = event.target.value;
	var sampleRate = 8000, beat = 4;
	var src = waveguide(cmd, 8000, 4, 'guitar');
	play(src, sampleRate);
}

function waveguide(cmd, sampleRate, beat, type) {
	if (cmd.length == 0) return;

	var F    = [0,261.63,293.66,329.63,349.22,392.00,440.00,493.88,523.25];
	var data = new Float32Array(sampleRate / beat * cmd.length);

	var x = 0;
	for (var k=0; k<cmd.length; ++k) {
		var f = F[cmd[k] - '0'];
		var t = 1;
		while (k+1 < cmd.length && cmd[k+1] == '-') k++, t++;

		var n = (sampleRate / beat * t) | 0;
		if (f == 0) {
			for (var i=0; i<n; ++i)
				data[x+i] = 0;
		} else if (type === 'sinewave') {
			for (var i=0; i<n; ++i)
				data[x+i] = Math.sin(Math.PI * 2 * f * i / sampleRate);
		} else if (type === 'guitar') {
			var t = Math.floor(sampleRate / f);
			for (var i=0; i<n; ++i)
				if (i < t)
					data[x+i] = (Math.random() - 0.5) * 2;
				else
					data[x+i] = data[x+i-t] * 0.95
					          + data[x+i+1-t] * (1.0 - 0.95);
		}

		x += n;
	}
	return data;
}

function play(data, sampleRate) {
	var ctx    = new AudioContext();
	var buffer = ctx.createBuffer(1, data.length, sampleRate);
	buffer.copyToChannel(data, 0);

	var node    = ctx.createBufferSource();
	node.buffer = buffer;
	node.connect(ctx.destination);
	node.start(0);
}
})();
</script>
</div>