Edit on
<canvas id="fourier-grapher"></canvas>
<div class="fourier-controls">
  <label>
    Frequency
    <input id="fourier-frequency" type="range" min="0.01" max="1" value="0.25" step="0.01" />
  </label>
  <label>
    Order
    <input id="fourier-order" type="range" min="1" max="16" value="4" step="1" /></label>
  <label>
    Type
    <select id="fourier-waveform">
      <option value="square">Square</option>
      <option value="sawtooth">Sawtooth</option>
    </select>
  </label>
</div>
.fourier-controls {
  bottom: 1em;
  color: #000;
  left: 1em;
  position: absolute;
}
View Compiled
class FourierGrapher {
  constructor(canvas) {
    this._canvas = canvas;
    this._ctx = canvas.getContext('2d');
    
    this._frequencyControl = document.getElementById('fourier-frequency');
    this._orderControl = document.getElementById('fourier-order');
    this._waveformControl = document.getElementById('fourier-waveform');
    
    this._bgColor = '#fff';
    this._circleColor = '#333';
    this._limbColor = '#99c';
    this._connectColor = '#f90';
    this._graphColor = '#0f0';
    
    this._scale = 200;
    
    this._time = 0;
    this._prevTime = new Date().getTime();
    
    this._graphX = 300;
    
    this._waveValuePointer = 0;
    this._waveValues = [];
    
    this._startPos = {
      x: 150,
      y: 100
    }
    
    this._setup();
    this._draw();
  }
  
  _setup() {
    let canvas = this._canvas;
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    canvas.style.backgroundColor = this._bgColor;
    this._ctx.lineWidth = 3;
    this._ctx.translate(0.5, 0.5);
  }
  
  _getWaveformDetails() {
    let func;
    let startOrder;
    switch (this._waveformControl.value) {
      case 'square':
        startOrder = 0;
        func = function(order) {
          return (order << 1) + 1;
        }
        break;
      case 'sawtooth':
        startOrder = 1;
        func = function(order) {
          return order << 1;
        }
        break;
    }
    
    console.log(startOrder);
    
    return {
      func: func,
      startOrder: startOrder
    }
  }
  
  _drawFourierCircles() {
    this._pos = {
      x: this._startPos.x,
      y: this._startPos.y
    };
    
    let wD = this._getWaveformDetails();
    
    for (let order = wD.startOrder; order <= this._orderControl.value; order++) {
      this._drawFourierCircle(wD.func(order));
    }
  }
  
  _drawFourierCircle(order) {
    let phase = order * (this._time / 1000 *  this._frequencyControl.value) * (Math.PI * 2);
    let radius = 1 / (order * Math.PI) * this._scale;
    let ctx = this._ctx;
    let pos = this._pos;
    
    ctx.beginPath();
		ctx.strokeStyle = this._circleColor;
    ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
		ctx.stroke();
    
    ctx.beginPath();
		ctx.strokeStyle = this._limbColor;
		ctx.moveTo(pos.x, pos.y);
    
    pos.x += Math.cos(phase) * radius;
    pos.y += Math.sin(phase) * radius;
    
    ctx.lineTo(pos.x, pos.y);
		ctx.stroke();
  }
  
  _drawConnectLine() {
    let ctx = this._ctx;
    let pos = this._pos;
    
    ctx.beginPath();
		ctx.strokeStyle = this._connectColor;
    ctx.moveTo(pos.x, pos.y);
    ctx.lineTo(this._graphX, pos.y);
		ctx.stroke();
  }
  
  _drawWave() {
    let ctx = this._ctx;
    
    this._waveValues[this._waveValuePointer++ & 255] = this._pos.y;
    ctx.beginPath();
    ctx.strokeStyle = this._graphColor;
    ctx.moveTo(this._graphX, this._pos.y);
    for (var i = 1; i < 256; ++i) {
      ctx.lineTo(this._graphX + i, this._waveValues[(this._waveValuePointer - i) & 255]);
    }
    ctx.stroke();
  }
  
  _draw() {
    this._updateTime();
    this._clear();
    this._drawFourierCircles();
    this._drawConnectLine();
    this._drawWave();
    window.requestAnimationFrame(this._draw.bind(this));
  }
  
  _clear() {
    this._ctx.clearRect(0, 0, canvas.width, canvas.height);
  }
  
  _updateTime() {
    let now = new Date().getTime();
    let timeDelta = now - this._prevTime;
    this._time += timeDelta;
    this._prevTime = now;
  }
}

var canvas = document.getElementById('fourier-grapher');
var fG = new FourierGrapher(canvas);
View Compiled
Rerun