drawing with the fourier series

With fourier analysis we can break a signal up into simple periodic signals. The best explanation by far is this video by 3Blue1Brown. But recently I saw a video by SmarterEveryDay showing a visual demonstration of the fourier series using circles, so I tried this for myself.

The fourier series is defined as

$$ f(t) = a_0 + \sum_{n=1}^{\infty} \left[ a_n \cos \left( \frac{2 \pi n t}{T} \right) + b_n \sin \left( \frac{2 \pi n t}{T} \right) \right] $$

where the coefficients are defined by

$$ \begin{align} a_0 &= \frac{1}{T} \int_{0}^{T} f(t) \, dt \\ a_n &= \frac{2}{T} \int_{0}^{T} f(t) \cos \left( \frac{2 \pi n t}{T} \right) \, dt \\ b_n &= \frac{2}{T} \int_{0}^{T} f(t) \sin \left( \frac{2 \pi n t}{T} \right) \, dt \end{align} $$

Using these formulas we can calculate the fourier coefficients:

// calculate the discrete difference i.e. out[n] = in[n+1] - in[n]
function diff(arr) {
  let diff = [];
  for (let i = 0; i < arr.length - 1; i++) {
    diff.push(arr[i+1] - arr[i]);
  }
  return diff;
}

function calcFourierCoeff(t, x, N) {
  if (t.length != x.length) {
    console.log("dft: x and t need to be same size");
    return;
  }
  let cosCoeff = [];
  let sinCoeff = [];

  // cumulative sum
  let dt = diff(t);
  let totalT = t[t.length - 1] - t[0];
  let groundFreq = 2*Math.PI/totalT;

  // calculate average
  let average = 0;
  for (let i = 0; i < x.length - 1; i++) {
    average += x[i] * dt[i];
  }
  average *= 1/totalT

  // calculate sin and cos coefficients
  let coeffArr = [];
  for (let n = 1; n < N; n++) {
    let cosCoeff = 0;
    let sinCoeff = 0;
    for (let i = 0; i < x.length - 1; i++) {
      cosCoeff += x[i] * dt[i] * Math.cos(n*groundFreq*t[i]);
      sinCoeff += x[i] * dt[i] * Math.sin(n*groundFreq*t[i]);
    }
    cosCoeff *= 2/totalT;
    sinCoeff *= 2/totalT;
    coeffArr.push({cos: cosCoeff, sin: sinCoeff, freq: n*groundFreq});
  }

  return {
    groundFreq: groundFreq,
    average: average,
    coeff: coeffArr
  };
}

Let's check if the code works. Use the slider to change the amount of coefficients calculated.

Using this Fourier transform we can also redraw 2d drawings. We just need to consider the drawing as a path defined by a periodic x and y signal. Here is an example (you can draw in the square!). Use the slider to change the amount of coefficients calculated.

Using this approach we can now do the same thing SmarterEveryDay does in his video mentioned above. Using our Fourier transform we can calculate the sine and cosine coefficients that give us the speed and size of connected circles that would imitate our drawing. You can again make you own drawing in the square, to see how the circles imitate it using Fourier analysis. Use the slider to change the amount of coefficients calculated.

If we want to have one arm that draws the whole drawing we have to use the fourier series in the complex form:

$$ f(t) = \sum_{n=-\infty}^{\infty} c_n e^{i \frac{2 \pi n t}{T}} $$

where the coefficients are defined by

$$ c_n = \frac{1}{T} \int_{0}^{T} f(t) e^{-i \frac{2 \pi n t}{T}} \, dt $$

Because working with complex numbers is a little bit cumbersome in javascript I use this FFT library (which has the extra benefit that it uses the Fast Fourier Transform algorithm, instead of our naive implementation).

All the javascript code used to run these samples can be found in the source code of this page.