title: How to visualize Fourier series / Fourier coefficients?
body: <p>I'm currently having difficulties at visualizing Fourier series. I tried the same thing about three times in order to find errors but in vain.</p>
  <p>Now I even don't know what is wrong with my code or understanding of Fourier series.</p>
  <p>What I'm trying to make is a thing like shown in the following Youtube video: <a href="https://youtu.be/r6sGWTCMz2k" rel="nofollow noreferrer">https://youtu.be/r6sGWTCMz2k</a></p>
  <p>I think I know what is Fourier series a bit. I can prove this by showing my previous works:</p>
  <p>(1) square wave approximation</p>
  <p><a href="https://i.stack.imgur.com/4wYlu.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4wYlu.png" alt="simple Fourier series" /></a></p>
  <p>(2) parameter</p>
  <p><a href="https://i.stack.imgur.com/UnIJX.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/UnIJX.png" alt="circle (parametric)" /></a></p>
  <p>So now I would like to draw <strong>more complicated thing in a parametric way</strong>. Please let me show the process I've walked.</p>
  <p>① From svg path, get coordinates. For example,</p>
  <pre class="lang-js prettyprint-override"><code>// svg path
  const d = 'M 0 0 L 20 30 L 10 20 ... ... ... Z';
  ↓
  ↓ convert with some processing...
  ↓
  const cx = [0, 20, 10, ...]; // function Fx(t)
  const cy = [0, 30, 20, ...]; // function Fy(t)
  </code></pre>
  <p>② Get Fourier coefficients from Fx(t), Fy(t), respectively. After that, I can get <strong>approximated</strong> coordinates by calculating Fourier series respectively by using the coefficients I got. For example,</p>
  <pre><code>Let's say I have a0_x, an_x, bn_x, a0_y, an_y, bn_y.
  Then, Fx(t) = a0_x + an_x[1] * cos(1wt) + bn_x[1] * cos(1wt)
                     + an_x[2] * cos(2wt) + bn_x[2] * cos(2wt) + ...;
  
        Fy(t) = a0_y + an_y[1] * cos(1wt) + bn_y[1] * cos(1wt)
                     + an_y[2] * cos(2wt) + bn_y[2] * cos(2wt) + ...;
  
  Therefore a set of points (Fx(t), Fy(t)) is an approximated path!
  </code></pre>
  <p>This is all! Only thing left is just drawing!</p>
  <p>Meanwhile, I processed the data in the following way:</p>
  <pre class="lang-js prettyprint-override"><code>const d = [svg path data];
  const split = d.split(/[, ]/);
  const points = get_points(split);
  const normalized = normalize(points);
  const populated = populate(normalized, 8);
  const cx = populated.x; // Fx(t)
  const cy = populated.y; // Fy(t)
  
  /**
   * This function does the below job.
   * populate([0,3,6], 2) =&gt; output 0 12 3 45 6
   * populate([0,4,8], 3) =&gt; output 0 123 4 567 8
   */
  function populate(data, n) {
    if (data.x.length &lt;= 1) throw new Error('NotEnoughData');
    if (n &lt; 1) throw new Error('InvalidNValue');
  
    const arr_x = new Array(data.x.length + (data.x.length - 1) * n);
    const arr_y = new Array(data.y.length + (data.y.length - 1) * n);
  
    for (let i = 0; i &lt; data.x.length; i++) {
      arr_x[i * (n + 1)] = data.x[i];
      arr_y[i * (n + 1)] = data.y[i];
    }
  
    for (let i = 0; i &lt;= arr_x.length - n - 1 - 1; i += (n + 1)) {
      const x_interpolation = (arr_x[i + n + 1] - arr_x[i]) / (n + 1);
      const y_interpolation = (arr_y[i + n + 1] - arr_y[i]) / (n + 1);
  
      for (let j = 1; j &lt;= n; j++) {
        arr_x[i + j] = arr_x[i] + x_interpolation * j;
        arr_y[i + j] = arr_y[i] + y_interpolation * j;
      }
    }
  
    return { x: arr_x, y: arr_y };
  }
  
  // This function makes all values are in range of [-1, 1].
  // I just did it... because I don't want to deal with big numbers (and not want numbers having different magnitude depending on data).
  function normalize(obj) {
    const _x = [];
    const _y = [];
  
    const biggest_x = Math.max(...obj.x);
    const smallest_x = Math.min(...obj.x);
    const final_x = Math.max(Math.abs(biggest_x), Math.abs(smallest_x));
  
    const biggest_y = Math.max(...obj.y);
    const smallest_y = Math.min(...obj.y);
    const final_y = Math.max(Math.abs(biggest_y), Math.abs(smallest_y));
  
    for (let i = 0; i &lt; obj.x.length; i++) {
      _x[i] = obj.x[i] / final_x;
      _y[i] = obj.y[i] / final_y;
    }
  
    return { x: _x, y: _y };
  }
  
  // returns Fx(t) and Fy(t) from svg path data
  function get_points(arr) {
    const x = [];
    const y = [];
    let i = 0;
  
    while (i &lt; arr.length) {
      const path_command = arr[i];
  
      if (path_command === &quot;M&quot;) {
        x.push(Number(arr[i + 1]));
        y.push(Number(arr[i + 2]));
        i += 3;
      } else if (path_command === 'm') {
        if (i === 0) {
          x.push(Number(arr[i + 1]));
          y.push(Number(arr[i + 2]));
          i += 3;
        } else {
          x.push(x.at(-1) + Number(arr[i + 1]));
          y.push(y.at(-1) + Number(arr[i + 2]));
          i += 3;
        }
      } else if (path_command === 'L') {
        x.push(Number(arr[i + 1]));
        y.push(Number(arr[i + 2]));
        i += 3;
      } else if (path_command === 'l') {
        x.push(x.at(-1) + Number(arr[i + 1]));
        y.push(y.at(-1) + Number(arr[i + 2]));
        i += 3;
      } else if (path_command === 'H') {
        x.push(Number(arr[i + 1]));
        y.push(y.at(-1));
        i += 2;
      } else if (path_command === 'h') {
        x.push(x.at(-1) + Number(arr[i + 1]));
        y.push(y.at(-1));
        i += 2;
      } else if (path_command === 'V') {
        x.push(x.at(-1));
        y.push(Number(arr[i + 1]));
        i += 2;
      } else if (path_command === 'v') {
        x.push(x.at(-1));
        y.push(y.at(-1) + Number(arr[i + 1]));
        i += 2;
      } else if (path_command === 'Z' || path_command === 'z') {
        i++;
        console.log('reached to z/Z, getting points done');
      } else if (path_command === 'C' || path_command === 'c' || path_command === 'S' || path_command === 's' || path_command === 'Q' || path_command === 'q' || path_command === 'T' || path_command === 't' || path_command === 'A' || path_command === 'a') {
        throw new Error('unsupported path command, getting points aborted');
      } else {
        x.push(x.at(-1) + Number(arr[i]));
        y.push(y.at(-1) + Number(arr[i + 1]));
        i += 2;
      }
    }
  
    return { x, y };
  }
  </code></pre>
  <p>Meanwhile, in order to calculate Fourier coefficients, I used numerical integration. This is the code.</p>
  <pre class="lang-js prettyprint-override"><code>/**
   * This function calculates Riemann sum (area approximation using rectangles).
   * @param {Number} div division number (= number of rectangles to be used)
   * @param {Array | Function} subject subject of integration
   * @param {Number} start where to start integration
   * @param {Number} end where to end integration
   * @param {Number} nth this parameter will be passed to 'subject'
   * @param {Function} paramFn this parameter will be passed to 'subject'
   * @returns {Number} numerical-integrated value
   */
  function numerical_integration(div, subject, start, end, nth = null, paramFn = null) {
    if (div &lt; 1) throw new Error(`invalid div; it can't be 0 or 0.x`);
  
    let sum = 0;
    const STEP = 1 / div;
    const isSubjectArray = Array.isArray(subject);
    
    if (isSubjectArray) {
      for (let t = start; t &lt; end; t++) {
        for (let u = 0; u &lt; div; u++) {
          sum += subject[t + 1] * STEP;
        }
      }
    } else {
      for (let t = start; t &lt; end; t++) {
        for (let u = 0; u &lt; div; u++) {
          const period = end - start;
          const isParamFnArray = Array.isArray(paramFn);
  
          if (isParamFnArray) sum += subject((t + 1), period, nth, paramFn) * STEP;
          else sum += subject(((t + STEP) + STEP * u), period, nth, paramFn) * STEP;
        }
      }
    }
  
    return sum;
  
  // console.log(numerical_integration(10, (x) =&gt; x ** 3, 0, 2));
  }
  </code></pre>
  <p>The approximation is near. For (x) =&gt; x, division 10, from 0 to 2, the approximation is 2.1 while actual answer is 2. For (x) =&gt; x ** 2, division 10, from 0 to 2, the approximation is 2.87, while actual answer is 2.67. For (x) =&gt; x ** 3, division 10, from 0 to 2, the approximation is 4.41, while actual answer is 4.</p>
  <p>And I found a0, an, bn by the following: (※ You can find Fourier coefficients formulas in <a href="https://stackoverflow.com/questions/70974735/a-problem-with-sound-producing-how-to-make-sound-with-fourier-coefficients">my previous question</a>)</p>
  <pre class="lang-js prettyprint-override"><code>/**
   * This function will be passed to 'getAn' function.
   * @param {Number} t this function is a function of time
   * @param {Number} period period of a function to be integrated
   * @param {Number} nth integer multiple
   * @param {Array | Function} paramFn
   * @returns {Number} computed value
   */
  function fc(t, period, nth, paramFn) {
    const isParamFnArray = Array.isArray(paramFn);
    const w = 2 * Math.PI / period;
    
    if (isParamFnArray) return paramFn[t] * Math.cos(nth * w * t);
    else return paramFn(t) * Math.cos(nth * w * t);
  }
  
  // This function will be passed to 'getBn' function.
  function fs(t, period, nth, paramFn) {
    const isParamFnArray = Array.isArray(paramFn);
    const w = 2 * Math.PI / period;
  
    if (isParamFnArray) return paramFn[t] * Math.sin(nth * w * t);
    else return paramFn(t) * Math.sin(nth * w * t);
  }
  
  /**
   * This function returns a0 value.
   * @param {Number} period period of a function to be integrated
   * @param {Array | Function} intgFn function to be intergrated
   * @param {Number} div number of rectangles to use
   * @returns {Number} a0 value
   */
  // Why * 30? in order to scale up
  // Why - 1? because arr[arr.length] is undefined.
  function getA0(period, intgFn, div) {
    return 30 * numerical_integration(div, intgFn, 0, period - 1) / period;
  }
  
  /**
   * This function returns an values.
   * @param {Number} period period of a function to be integrated
   * @param {Number} div number of rectangles to use
   * @param {Number} howMany number of an values to be calculated
   * @param {Array | Function} paramFn function to be integrated
   * @returns {Array} an values
   */
  function getAn(period, div, howMany, paramFn) {
    const an = [];
  
    for (let n = 1; n &lt;= howMany; n++) {
      const value = 30 * numerical_integration(div, fc, 0, period - 1, n, paramFn) * 2 / period;
      
      an.push(value);
    }
  
    return an;
  }
  
  // This function returns bn values.
  function getBn(period, div, howMany, paramFn) {
    const bn = [];
  
    for (let n = 1; n &lt;= howMany; n++) {
      const value = 30 * numerical_integration(div, fs, 0, period - 1, n, paramFn) * 2 / period;
  
      bn.push(value);
    }
  
    return bn;
  }
  
  const xa0 = getA0(cx.length, cx, 10);
  const xan = getAn(cx.length, 10, 100, cx);
  const xbn = getBn(cx.length, 10, 100, cx);
  
  const ya0 = getA0(cy.length, cy, 10);
  const yan = getAn(cy.length, 10, 100, cy);
  const ybn = getBn(cy.length, 10, 100, cy);
  </code></pre>
  <p>However, the result was not a thing I wanted... It was a weird shape... Maybe this is life...</p>
  <p><a href="https://i.stack.imgur.com/Hkgnu.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Hkgnu.png" alt="a weird shape" /></a></p>
  <p>The below is the canvas drawing code:</p>
  <pre class="lang-js prettyprint-override"><code>const $cvs = document.createElement('canvas');
  const cctx = $cvs.getContext('2d');
  
  $cvs.setAttribute('width', 1000);
  $cvs.setAttribute('height', 800);
  $cvs.setAttribute('style', 'border: 1px solid black;');
  document.body.appendChild($cvs);
  
  window.requestAnimationFrame(draw_tick);
  
  // offset
  const xoo = { x: 200, y: 600 }; // x oscillator offset
  const yoo = { x: 600, y: 200 }; // y ~
  
  // path
  const path = [];
  
  // drawing function
  let deg = 0;
  
  function draw_tick() {
    const rAF = window.requestAnimationFrame(draw_tick);
  
    // initialize
    cctx.clearRect(0, 0, 1000, 800);
    
    // y oscillator
    const py = { x: 0, y: 0 };
  
    // a0
      // a0 circle
    cctx.beginPath();
    cctx.strokeStyle = 'black';
    cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(ya0), 0, 2 * Math.PI);
    cctx.stroke();
  
      // a0 line
    cctx.beginPath();
    cctx.strokeStyle = 'black';
    cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
  
    py.x += ya0 * Math.cos(0 * deg * Math.PI / 180);
    py.y += ya0 * Math.sin(0 * deg * Math.PI / 180);
    cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
    cctx.stroke();
    
    // an
    for (let i = 0; i &lt; yan.length; i++) {
      const radius = yan[i];
  
      // an circles
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(radius), 0, 2 * Math.PI);
      cctx.stroke();
  
      // an lines
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
  
      py.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
      py.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
      cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
      cctx.stroke();
    }
    
    // bn
    for (let i = 0; i &lt; ybn.length; i++) {
      const radius = ybn[i];
  
      // bn circles
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(radius), 0, 2 * Math.PI);
      cctx.stroke();
  
      // bn lines
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
  
      py.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
      py.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
      cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
      cctx.stroke();
    }
  
    // x oscillator
    const px = { x: 0, y: 0 };
  
    // a0
      // a0 circle
    cctx.beginPath();
    cctx.strokeStyle = 'black';
    cctx.arc(yoo.x + py.x, yoo.y + py.y, Math.abs(xa0), 0, 2 * Math.PI);
    cctx.stroke();
  
      // a0 line
    cctx.beginPath();
    cctx.strokeStyle = 'black';
    cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
  
    py.x += xa0 * Math.cos(0 * deg * Math.PI / 180);
    py.y += xa0 * Math.sin(0 * deg * Math.PI / 180);
    cctx.lineTo(yoo.x + py.x, yoo.y + py.y);
    cctx.stroke();
    
    // an
    for (let i = 0; i &lt; xan.length; i++) {
      const radius = xan[i];
  
      // an circles
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.arc(xoo.x + px.x, xoo.y + px.y, Math.abs(radius), 0, 2 * Math.PI);
      cctx.stroke();
  
      // an lines
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
  
      px.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
      px.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
      cctx.lineTo(xoo.x + px.x, xoo.y + px.y);
      cctx.stroke();
    }
  
    // bn
    for (let i = 0; i &lt; xbn.length; i++) {
      const radius = xbn[i];
  
      // bn circles
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.arc(xoo.x + px.x, xoo.y + px.y, Math.abs(radius), 0, 2 * Math.PI);
      cctx.stroke();
  
      // bn lines
      cctx.beginPath();
      cctx.strokeStyle = 'black';
      cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
  
      px.x += radius * Math.cos((i+1) * deg * Math.PI / 180);
      px.y += radius * Math.sin((i+1) * deg * Math.PI / 180);
      cctx.lineTo(xoo.x + px.x, xoo.y + px.y);
      cctx.stroke();
    }
  
    // y oscillator line
    cctx.strokeStyle = 'black';
    cctx.beginPath();
  
    cctx.moveTo(yoo.x + py.x, yoo.y + py.y);
    cctx.lineTo(xoo.x + px.x, yoo.y + py.y);
  
    cctx.stroke();
  
    // x oscillator line
    cctx.strokeStyle = 'black';
    cctx.beginPath();
  
    cctx.moveTo(xoo.x + px.x, xoo.y + px.y);
    cctx.lineTo(xoo.x + px.x, yoo.y + py.y);
  
    cctx.stroke();
  
    // path
    path.push({ x: px.x, y: py.y });
  
    cctx.beginPath();
    cctx.strokeStyle = 'black';
    
    cctx.moveTo(200 + path[0].x, 200 + path[0].y);
  
    for (let i = 0; i &lt; path.length; i++) {
      cctx.lineTo(200 + path[i].x, 200 + path[i].y);
    }
  
    cctx.stroke();
  
    // degree update
    if (deg === 359) {
      window.cancelAnimationFrame(rAF);
    } else {
      deg++;
    }
  }
  </code></pre>
  <p>So! I decided to be logical. First, I checked whether the converted path data is correct by drawing it at canvas. The below is the canvas code and the data.</p>
  <pre class="lang-js prettyprint-override"><code>let count = 0;
  
  function draw_tick2() {
    const rAF = window.requestAnimationFrame(draw_tick2);
  
    const s = 100; // scale up
  
    // initialize
    cctx.clearRect(0, 0, 1000, 800);
  
    cctx.beginPath();
    
    // 200 has no meaning I just added it to move the path.
    for (let i = 0; i &lt; count; i++) {
      if (i === 0) cctx.moveTo(200 + s * cx[i], 200 + s * cy[i]);
      else cctx.lineTo(200 + s * cx[i], 200 + s * cy[i]);
    }
  
    cctx.stroke();
  
    if (count &lt; cx.length - 1) {
      count++;
    } else {
      window.cancelAnimationFrame(rAF);
    }
  }
  </code></pre>
  <pre class="lang-js prettyprint-override"><code>const paimon = 'm 0,0 -2.38235,-2.87867 -1.58823,-1.29045 -1.9853,-0.893384 -3.17647,-0.39706 1.58824,-1.98529 1.09191,-2.08456 v -2.38235 l -0.79412,-2.87868 1.88603,2.18383 1.6875,1.88602 1.78677,0.99265 1.78676,0.39706 1.78676,-0.19853 -1.6875,1.58824 -0.69485,1.68749 -0.0993,2.084564 0.39706,2.18383 9.62867,3.87132 2.77941,1.9853 4.66544,-1.09192 3.07721,-1.88603 1.9853,-2.58088 -3.97059,0.49633 -3.375,-0.79412 -2.87868,-2.58088 -2.08456,-3.077214 2.38235,1.48897 2.08456,0.19853 3.57353,-0.89338 2.58089,-2.48162 -3.07721,0.39706 -3.87132,-1.88603 -2.97794,-2.08456 -2.48162,-2.87868 -3.87133,-4.06985 -4.06985,-2.68015 -5.95588,-2.58088 -5.85662,-0.79412 -5.45956,0.99265 0.59559,1.6875 -0.99265,1.09191 -0.79412,3.47427 -1.29044,-2.97794 -0.89338,-1.19118 0.79412,-1.48897 1.6875,-0.79412 0.39706,-3.772057 1.48897,1.290441 1.78676,0.09926 -2.08456,-1.985293 1.78677,-0.893382 4.36765,-0.19853 4.86397,0.992648 1.19117,1.091912 -2.38235,1.985301 3.17647,-0.49633 2.87868,-2.680149 -3.57353,-2.580881 -5.45956,-1.488972 h -4.46691 l -3.6728,-3.176471 -0.79412,1.389706 -0.79411,-1.488969 0.69485,-0.595588 -1.58824,-3.871325 -0.39706,3.672795 -0.69485,0.297794 0.89338,1.091911 v 1.091912 h -1.19113 l -0.59559,-0.992648 -1.98529,2.878677 -4.06986,1.588236 -4.26838,1.985293 3.27574,3.871329 2.87867,1.88603 2.58088,0.29779 -2.58088,-1.58823 -0.89338,-2.084566 4.86397,-0.992645 -1.19118,2.382351 h 1.58824 l 1.48897,-1.88603 0.29779,2.77942 -2.38235,2.38235 -3.57353,2.87868 -3.97059,4.86397 -2.08456,3.67279 -2.58088,2.58088 -2.68015,1.09192 -3.17647,0.0993 -1.3897,-0.69485 1.09191,3.17647 2.18382,3.573534 3.375,2.38235 -1.78676,5.85662 -1.38971,6.05514 0.39706,4.36765 1.38971,4.66544 3.87132,4.46691 -0.79412,-3.57352 -0.49632,-4.06986 v -2.48162 l 1.78676,5.85662 3.07721,3.17647 3.07721,1.29044 3.37499,0.79412 2.28309,-0.89338 0.69486,-1.48897 -1.19118,0.49632 -2.48162,-1.98529 -2.28309,-2.87868 2.28309,2.48162 h 0.99265 l 0.69485,-0.49632 0.2978,-1.19118 0.0993,-0.79412 -0.89339,0.59559 -1.58823,-0.99265 -1.29044,-1.3897 -1.19118,-2.38236 -0.89338,-4.86397 -0.0993,-4.56617 0.29779,-4.96324 0.39706,0.89338 1.19118,-0.44669 0.0496,-0.89338 1.09191,0.69485 1.48897,0.2978 1.53861,0.89338 0.99264,0.64522 h -0.79411 l 0.49632,2.43199 -0.44669,1.58823 -1.78676,0.39706 -1.24081,-1.24081 -0.24817,-1.43934 0.84375,-0.94301 1.19118,-0.49633 1.14154,0.94302 0.24816,1.14154 -0.0993,1.48897 -1.83639,0.64523 -1.58824,-1.53861 -0.44669,-1.48897 -0.24816,-2.18382 -1.43934,0.99264 0.0496,-0.99264 -0.44669,1.78676 0.69485,3.12684 1.09192,4.26838 1.78676,1.78677 6.89889,3.02757 -2.53124,0.99265 -3.17647,1.3897 -0.79412,0.39706 0.59559,0.39706 1.34007,-0.69485 0.0496,1.19117 1.98529,-0.39705 2.68015,-0.44669 -0.2978,-1.93567 0.79412,1.58824 2.82905,-0.44669 4.06985,-1.34008 1.04229,-0.59559 -0.2978,-1.78676 -0.34743,-1.73713 -4.9136,2.48162 -2.58088,0.94301 -3.17648,-4.81434 1.53861,0.49633 1.3897,0.0496 1.43935,-0.24816 -1.34008,0.24816 h -1.58824 l -1.41452,-0.54596 3.12684,4.78953 2.63052,-0.89339 4.86397,-2.4568 2.65533,-2.08456 0.39706,-5.90625 -0.84375,1.5386 -1.14155,0.54596 -1.5386,0.19853 -1.29044,-0.89338 -0.59559,-1.09191 -0.24816,-1.73714 0.24816,-1.3897 -2.08456,0.54595 -0.29779,-0.34742 0.34743,-0.49633 0.64522,-0.39706 1.5386,-0.39705 2.18382,-0.19853 1.24081,0.0993 1.14154,0.54596 0.4467,1.43934 -0.19853,1.63786 -0.59559,1.29044 -1.24081,0.89339 -1.43934,-0.39706 -0.99264,-1.09191 -0.0496,-1.19118 0.79412,-0.89338 0.89338,-0.44669 1.19118,-0.0496 0.64522,1.04228 0.34742,0.79412 -0.14889,1.14155 0.99265,-0.4467 0.29779,-1.34007 -0.19853,-4.06985 -1.93566,-0.44669 -2.53125,-1.6875 -2.23346,-1.88603 -2.23345,-4.069864 -0.44669,3.920964 0.64522,4.21875 1.5386,3.92096 0.74448,0.44669 h -1.73713 l -2.18383,-0.54596 -3.12684,-2.08456 -1.58823,-2.28309 -1.14154,-2.08456 -1.29044,-3.871324 -1.38971,2.481624 -1.48897,2.63051 -0.94302,1.9853 3.8217,-6.948534 1.29044,3.672794 2.33272,3.92096 2.9283,2.13419 0.49633,0.44669 2.28309,0.49632 h 1.63787 l -0.69485,-0.69485 -0.84375,-1.93566 -1.34008,-5.80698 0.44669,-3.970594 2.33273,4.069854 4.56617,3.47426 2.08456,0.59559 0.19853,2.82905 -0.0496,3.97058 -0.0993,6.00552 -0.54595,3.02757 -1.58824,2.77941 -1.5386,0.89339 -1.19118,0.24816 -1.48897,-0.69485 -0.69485,-0.1489 0.69485,1.24081 1.43934,1.6875 2.68015,1.19117 3.17647,0.2978 3.77206,-2.23346 1.3897,-2.77941 0.89339,-3.82169 0.0496,-3.375 0.14889,6.25368 -1.14154,5.11213 -2.08456,3.27573 -2.08456,1.6875 -1.88603,0.59559 -2.28308,-0.79412 1.78676,1.6875 4.9136,1.88603 2.43199,0.2978 2.68015,-0.39706 2.72977,-1.09191 3.62317,-3.27574 0.89338,-3.97059 0.49632,-3.57353 -0.0993,-2.87867 -0.39706,-3.17647 -0.49632,-3.07721 1.98529,3.47427 1.19117,2.18382 0.39706,1.29044 0.39706,-2.28309 -0.39706,-3.0772 -1.29044,-3.77206 -1.29044,-2.87868 -1.6875,-3.27573 -10.125,-4.16912 z';
  </code></pre>
  <p><a href="https://i.stack.imgur.com/hkgCW.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hkgCW.png" alt="paimon chan" /></a></p>
  <p>This is ★Paimon chan★. Thus it is proved that there are no flaws at the data, since all the data is plotted correctly.</p>
  <p>Next, I plotted the approximated (Fx(t), Fy(t)) points so that I can check whether there is a problem. And It turned out that there was a problem. But I don't understand what is the problem. At the same time this path is interesting; The beginning part of the path seems like the hairpin.</p>
  <p><a href="https://i.stack.imgur.com/BtxdJ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BtxdJ.png" alt="a weird path" /></a></p>
  <p>This is the drawing code:</p>
  <pre class="lang-js prettyprint-override"><code>function approxFn(t) {
    let x = xa0;
    let y = ya0;
  
    for (let i = 0; i &lt; xan.length; i++) {
      x += xan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
      x += xbn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
      y += yan[i] * Math.cos(2 * Math.PI * i * t / cx.length);
      y += ybn[i] * Math.sin(2 * Math.PI * i * t / cx.length);
    }
  
    return { x, y };
  }
  
  function draw_tick3() {
    const rAF = window.requestAnimationFrame(draw_tick3);
  
    const s = 5;
  
    // initialize
    cctx.clearRect(0, 0, 1000, 800);
  
    cctx.beginPath();
  
    for (let t = 0; t &lt; count; t++) {
      if (count === 0) cctx.moveTo(200 + s * approxFn(t).x, 200 + s * approxFn(t).y);
      else cctx.lineTo(200 + s * approxFn(t).x, 200 + s * approxFn(t).y);
    }
  
    cctx.stroke();
  
    if (count &lt; cx.length - 1) {
      count++;
    } else {
      window.cancelAnimationFrame(rAF);
    }
  }
  </code></pre>
  <p>The above is all the code in my js file. In where I made a mistake? It's a mystery! I know this question is exceptionally seriously long question. But please help me! I want to realize Paimon chan! ㅠwㅠ</p>
  <p>※ (This section is irrelevant with the question) Meanwhile I made a success to draw the path in a <strong>complex number plane</strong>. If you're interested, please see my work... I would like to add circle things to this but I have no idea what is 'radius' in this case.</p>
  <p><a href="https://i.stack.imgur.com/NqQ2O.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/NqQ2O.jpg" alt="complex Fourier coefficients" /></a></p>
  <p><a href="https://i.stack.imgur.com/RN2A3.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/RN2A3.jpg" alt="complex Fourier series" /></a></p>
  <p><a href="https://i.stack.imgur.com/22snB.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/22snB.png" alt="comparison of results of Fourier series" /></a></p>
  <pre class="lang-js prettyprint-override"><code>// You can see that I used real part for x and imaginary part for y.
  
  for (let i = 0; i &lt;= count; i++) {
      if (i === 0) {
        cctx.moveTo(coords[i].real * scaler + paimonPosition, coords[i].imag * scaler + paimonPosition);
      } else {
        cctx.lineTo(coords[i].real * scaler + paimonPosition, coords[i].imag * scaler + paimonPosition);
      }
    }
  </code></pre>
  <p>And this is the result. But what makes me confused is a case of cn = -5000 ~ 5000. As far as I understand, more cn, more accurate as original wave. But why it crashes when cn is so big?</p>
  <p>Anyways, thank you very much for reading this long question!</p>