CodePen

HTML

            
              <div>
		<h3>Fourier series</h3>
		<canvas width="512" height="256"></canvas>
		<div>
				<span>Frequency</span>
				<input frequency type="range" min="-4" max="-3" value="-3.5" step="0.0001">
		</div>
		<div>
				<span>Order</span>
				<input order type="range" min="0" max="16" value="4" step="1">
		</div>
		<div>
				<span>Waveform</span>
				<form id="waveform">
						<label>Square
								<input type="radio" name="waveform" value="square" checked>
						</label>
						<label>Sawtooth
								<input type="radio" name="waveform" value="sawtooth">
						</label>
				</form>
		</div>
</div>
            
          
!

CSS

            
              html, body {
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	font-family: Open Sans;
	font-size: 12px;
	color: orange;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	display: -webkit-flex;
	-webkit-align-items: center;
	-webkit-justify-content: center;
	background: #222;
}

h3 {
	margin: 0 0 3px 1px;
}

canvas {
	background: black;
}

span {
	width: 80px;
	margin: 0;
	padding: 0;
	display: inline-block;
}
form, label {
	display: inline-block;
}
input[type="range"] {
	margin: 0;
	padding: 0;
	width: 432px;
	display: inline;
	vertical-align: middle;
}
            
          
!

JavaScript

            
              var frequencyInput = document.querySelector("input[frequency]");
var orderInput = document.querySelector("input[order]");
var waveformInput = document.getElementById("waveform").elements["waveform"];
var canvas = document.querySelector("canvas");
var context = canvas.getContext("2d");

var PI2 = Math.PI * 2.0;
var Scale = 64.0;
var time = 0.0;
var startTime = new Date().getTime();
var values = [];
var valuePointer = 0;
var x = 128.0,
		y = 128.0;

function fourier(order) {
		var phase = order * time * PI2;
		var radius = 4.0 / (order * Math.PI) * Scale;
		context.beginPath();
		context.lineWidth = 1.0;
		context.strokeStyle = "rgba(255,128,32,1.0)";
		context.arc(x, y, radius, 0, PI2);
		context.stroke();
		context.strokeStyle = "rgba(255,255,255,0.4)";
		context.moveTo(x, y);
		x += Math.cos(phase) * radius;
		y += Math.sin(phase) * radius;
		context.lineTo(x, y);
		context.stroke();
};

function connect() {
		context.beginPath();
		context.moveTo(x + 0.5, y + 0.5);
		context.lineTo(256 + 0.5, y + 0.5);
		context.strokeStyle = "rgba(255,255,32,1.0)";
		context.stroke();
};

function drawWave() {
		values[valuePointer++ & 255] = y;
		context.beginPath();
		context.strokeStyle = "rgba(0,255,0,1)";
		context.moveTo(256 + 0.5, y + 0.5);
		for (var i = 1; i < 256; ++i) {
				context.lineTo(256 + i + 0.5, values[(valuePointer - i) & 255] + 0.5);
		}
		context.stroke();
}

(function frame() {
		canvas.width = canvas.clientWidth;
		canvas.height = canvas.clientHeight;
		x = 144.0;
		y = 128.0;
		switch (waveformInput.value) {
				case "square":
						for (var order = 0; order <= orderInput.value; order++) {
								fourier((order << 1) + 1);
						}
						break;
				case "sawtooth":
						for (var order = 1; order <= orderInput.value; order++) {
								fourier(order << 1);
						}
						break;
		}
		connect();
		drawWave();
		var now = new Date().getTime();
		time += (now - startTime) * Math.pow(10.0, frequencyInput.value);
		startTime = now;
		window.requestAnimationFrame(frame);
})();
            
          
!
999px
Loading ..................