Fourier Drawer


Live Demo

The Fourier Drawer is the animation that can be found on the main page of this website. The animation consists of a chains of circles. In each chain, each circle spins at a constant rate around the previous circle. By precisely tuning the size and starting position of each circle, we can control the shape the path of the smallest circle traces out. In the case of the main page, seven of these chains of circles draw out my name. This page will describe the methods and the maths behind determining the starting position and sizes of the circles. This was heavily inspired by 3Blue1Brown's Video on Drawing with Circles.

Below is a live demo. Enter some text into the box below and watch it be drawn out by circles, just like on this website's main page. Here, the font used is Baumans from Google Fonts.

The source code for all this can be found within the source code for this website, here!. What follows now is my derivation of the fourier series used to draw the shapes and the mathematical definitions of the shapes themselves.

The Fourier Transform

We can start by describing the movement of the circles mathematically. For each circle we need three values to describe it. Specifically, for the $n^\text{th}$ circle, we need its radius $r_n$, the integer frequency $f_n$ at which the next circle spins around it, and the starting angle $\theta_n$ of this next circle. This means that relative to this circle, at time $t$, the next circle's centre is positioned at: $$\left(r_n\cos\left(\theta_n + 2\pi tf_n\right),r_n\sin\left(\theta_n + 2\pi tf_n\right)\right)$$ To get the position of the final circle in the chain we just need to add this up for all circles, meaning for some set of circles, the path we trace out will be the equation below where $P(t)$ cna be thought of as a function that traces out the path over time, where $t \in (0, 1)$. $$P(t) = \left(\sum_n r_n\cos\left(\theta_n + 2\pi tf_n\right), \sum_n r_n\sin\left(\theta_n + 2\pi tf_n\right)\right)$$ It turns out its easier if instead of thinking of the path we trace as a set of 2D points, we think of them as complex numbers. To formulate it this way, for each circle instead of using its radius $r_n$ and starting angle $\theta_n$, we just store its starting position $c_i = r_ne^{\theta_ni}$. Additionaly, instead of thinking of frequencies we will use angular velocities $\omega_n$ = $2\pi f_n$. Then, the equation above becomes $$P(t) = \sum_n c_ne^{\omega_nti}$$ Now what we want to do is go the other way. We have a shape we want to draw (say a letter) which we can represent as a complex function of time $P(t)$. Then, we need to find all the $c_i$. The way to do this is with a fourier transform. It turns out that: $$c_n = \int_0^1P(t) \cdot e^{-\omega_nti} \mathrm{d}t$$ We can prove this by splitting the $P(t)$ term as follows: $$\int_0^1\left(c_ne^{\omega_nti} + \sum_{m\neq n}c_me^{\omega_mti}\right)\cdot e^{-\omega_nti} \mathrm{d}t$$ Then by ditributing the $e^{-\omega_nti}$ term and the integral we get $$\int_0^1c_ne^{\omega_nti}\cdot e^{-\omega_nti} \mathrm{d}t + \int_0^1\sum_{m\neq n}c_me^{\omega_mti}\cdot e^{-\omega_nti} \mathrm{d}t$$ Combining exponential terms we get: $$\int_0^1 c_n e^0\mathrm{d}t + \int_0^1\sum_{m \neq n}c_m e^{\left(\omega_m - \omega_n\right)ti}\mathrm{d}t$$ The first integral is just equal to $\int_0^1c_n\mathrm{d}t = c_n$. To evaluate the second integral, it is useful to switch the order of the sum and the integral. $$c_i + \sum_{m\neq n}\int_0^1c_m e^{\left(\omega_m - \omega_n\right)ti}\mathrm{d}t$$ This integral is quite easy to integrate $$ \begin{align*} &c_n + \sum_{m \neq n}\left[\frac{1}{\left(\omega_m-\omega_n\right)i}\cdot e^{\left(\omega_m - \omega_n\right)ti}\right]_0^1\\ =&c_n + \sum_{m \neq n}\frac{1}{\left(\omega_m-\omega_n\right)i} \cdot \left(e^{\left(\omega_m - \omega_n\right)i} - e^0\right) \end{align*} $$ Recall that $\omega_x$ is just an integer multiple of $2\pi$. This means $\left(\omega_m-\omega_n\right)$ is also an integer multiple of $2\pi$. Hence $e^{\left(\omega_m - \omega_n\right)i}=1$ and we get $$ c_n + \sum_{m \neq n} \frac{1}{\left(\omega_m-\omega_n\right)i} \cdot (1 - 1) = c_n + \sum_{m \neq n} 0 = c_n $$ And so we have shown that this fourier transform allows us to extract the values of $c_i$. This does seem to assume that all $P(t)$ can be represented using an infinite sum of spinning circles. Reading this Wikipedia article we can show that we do get pointwise convergence of the fourier series to $P(t)$ for the speicifc class of functions we describe in the later sections.

In practice, I cannot actually calculate the $c_n$ values for an infinite number of circles and then draw an infinite number of circles. However, for any reasonably behaved curve, the $c_n$ values quickly go to $0$ for large-ish $|\omega_n|$ and so if we only draw circles with $-50\pi \le \omega_n \le 50\pi$ we get a reasonably good approximation of our path $P(t)$.

Defining the Path

How do we actually define $P(t)$ to draw out a letter? To make my fourier drawer able to draw very many shapes, I settled on making a representation based on SVG Path Data. This format represents a curve using a series of simple shapes. These shapes are:
  • Straight Lines
  • Quadratic Bezier Curves
  • Cubic Bezier Curves
  • Arcs of Ellipses
Each of these shapes have reasonably simple parametric descriptions and this allows us to have a reasonably simple definition of $P(t)$. Assume our curve is a sequence of $N$ of these shapes. We can define this series of shapes as $f_0(t), f_2(t), \ldots f_{N-1}(t)$ where $f_x(t)$ is a parametric definition of the $x^\text{th}$ shape on the domain $(0, 1)$. Then, we can split up the domain of $P(t)$ into $N$ intervals of equal size numbered from $0$ to $N-1$. For a certain $t$, let $k$ be the number of the interval it lies in. This means $t \in \left(\frac{k}{N}, \frac{k+1}{N}\right)$ Then, $$P(t) = f_k\left(\left(t - \frac{k}{N}\right) \cdot N\right)$$ The whole $\left(t - \frac{k}{N}\right) \cdot N$ thing just transforms the $\left(\frac{k}{N}, \frac{k+1}{N}\right)$ domain into the $(0,1)$ domain of $f_k$.

A slightly nicer way to define this function is to define another set of functions $g_0(t), g_1(t), \ldots, g_{N-1}(t)$. These are defined on the same domain as $P(t)$. We define $g_k(t)$ as follows. When $t$ is in the $k^\text{th}$ interval as described above, $g_k(t)$ traces out the full path of $f_k(t)$. Otherwise $g_k(t)=0$. Specifically, $$ g_k(t) = \begin{cases}f_k\left(\left(t - \frac{k}{N}\right) \cdot N\right) & t \in \left(\frac{k}{N}, \frac{k+1}{N}\right) \\ 0+0i & \text{otherwise}\end{cases} $$ Then, $P(t)$ just becomes the sum of all $g_k(t)$. So, $$P(t) = \sum_{k=0}^{N-1}g_k(t)$$ This representation makes it slightly easier to take the fourier transform. Since the fourier transform is a linear transformation, the fourier transform of $P(t)$ is: $$ \hat{P}(\omega) = \sum_{k=0}^{N-1}\hat{g}_k(\omega) $$

Parametric Definitions of SVG Path Components

This section simply provides mathematical definitions for the $f_k(t)$ functions described above for each of the shapes required.

Lines

A line simply goes from some point $a$ to another point $b$. Therefore we can just write it as: $$f(t) = a + (b-a) \cdot t$$ Where $a,b$ are complex numbers. To simplify it let $v = b-a$. Then, $$f(t) = a + vt$$

Quadratic Bezier Curves

It turns out all quadratic bezier curves can be rewritten as a cubic bezier curve. So, we can just do that and then we can ignore the quadratic bezier curve case.

Cubic Bezier Curves

We can just use the standard bezier curve definition. Let $p_0,p_1,p_2,p_3$ be the four points defining the bezier curve. Then, $$f(t) = (1-t)^3p_0 + 3(1-t)^2tp_1 + 3(1-t)t^2p_2 + t^3p_3$$

Arcs

In an SVG, an arc is just a portion of an ellipse and the ellipse itself may be rotated. From the SVG data we can extract:
  • The center of the arc, call it $C$
  • The start and end angles of the arc, call them $\theta_0, \theta_1$
  • The $x$ and $y$ radius of the ellipse, $r_x$, $r_y$
  • The extra rotation of the ellipse, call it $\alpha$
For simplicty we define $\Delta\theta = \theta_1 - \theta_0$. The equation for a simple circle arc is then: $$f(t) = e^{\left(\theta_0 + t \cdot \Delta\theta\right)i}$$ To scale the $x$ and $y$ radii separately, we need to split this exponential into its real and imaginary components. This can be done as follows $$ \begin{align*} f_\text{real}(t) &= \frac{e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} + e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i}}{2}\\ f_\text{imag}(t) &= \frac{e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} - e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i}}{2} \end{align*} $$ So then, scaling by the two radii and adding stuff back together we get $$ f(t) = r_x \cdot \frac{e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} + e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i}}{2} + r_y \cdot \frac{e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} - e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i}}{2} $$ We can then collect like terms to simplify this slightly $$ f(t) = \frac{1}{2} \cdot e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x-r_y\right) $$ Then, we need to rotate the whole thing by $\alpha$, which can be done by multiplying by $e^{\alpha i}$. This exponential can be distributed over the others. $$ \begin{align*} f(t) &= e^{\alpha i} \cdot \left(\frac{1}{2} \cdot e^{\left(\theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{-\left(\theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x-r_y\right)\right)\\ &= \frac{1}{2} \cdot e^{\left(\alpha + \theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{\left(\alpha - \theta_0 - t \cdot \Delta\theta\right)i} \cdot (r_x - r_y) \end{align*} $$ Finally, we just need to shift it by $C$ to position it correctly. This gives us the complete equation: $$ f(t) = C + \frac{1}{2} \cdot e^{\left(\alpha + \theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{\left(\alpha - \theta_0 - t \cdot \Delta\theta\right)i} \cdot (r_x - r_y) $$ Although this equation may look a little nasty, it turns out its Fourier Transform is quite simple, as will be seen later.

The Fourier Transform of Bezier Curves (and Arcs)

There are two ways to then take the required Fourier Transform. The simplest way is to sample many points of $P(t)$ and take a Discrete Fourier Transform. However, as a challenge, I decided to actually find analytical solutions of the integral of $P(t)$. Recall that for some $n$ we want to calculate the following: $$c_n = \int_0^1 P(t) e^{-\omega_nti} \mathrm{d}t$$ Using our definition for $P(t)$ and then swapping the sum and the integral we get: $$c_n = \int_0^1 e^{-\omega_nti} \cdot \sum_{k=0}^{N-1}g_k(t) \mathrm{d}t = \int_0^1 \sum_{k=0}^{N-1}e^{-\omega_nti} \cdot g_k(t) \mathrm{d}t = \sum_{k=0}^{N-1} \int_0^1 e^{-\omega_nti} \cdot g_k(t) \mathrm{d}t$$ Now, all we need is an equation for $\int_0^1 e^{-\omega_nti} \cdot g_k(t) \mathrm{d}t$ for our three different kinds of $g_k(t)$. We can denote this expression as $\hat{g}(\omega_n)$. Since $g_k(t)$ is zero everywhere except for $t \in \left(\frac{k}{N}, \frac{k+1}{N}\right)$ we can slightly change the bounds of the integral to get $$ \hat{g}(\omega_n) = \int_0^1 e^{-\omega_nti} \cdot g_k(t) \mathrm{d}t = \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot g_k(t) \mathrm{d}t = \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot f_k\left(\left(t - \frac{k}{N}\right) \cdot N\right) \mathrm{d}t $$ To make things easierin the future, we will find the general solution for some integrals that come up often.

Fourier Transform of a Constant

$$I = \int_a^b 1 \cdot e^{-\omega_nti}\mathrm{d}t = \int_a^b e^{-\omega_nti} \mathrm{d}t$$ If $\omega_n$ = 0 we get $I = \int_a^b 1 \mathrm{d}{t} = b-a$. Otherwise, $I = \frac{1}{-\omega_n i}\left[e^{-\omega_nti}\right]_a^b = \frac{i}{\omega_n}\left(e^{-b\omega_ni} - e^{-a\omega_ni}\right)$. For simplicity we will denote this whole thing as $\hat{X}(\omega_n, a, b)$. Hence we have: $$ \hat{X}(\omega_n, a, b) = \begin{cases} \frac{i}{\omega_n}\left(e^{-b\omega_ni} - e^{-a\omega_ni}\right) & \omega_n \neq 0\\ b-a & \omega_n = 0 \end{cases} $$

Fourier Transform of a Linear Term

We wish to evaluate the following: $$I = \int_a^b t \cdot e^{-\omega_nti}\mathrm{d}t = \int_a^b t \cdot e^{-\omega_nti}\mathrm{d}t$$ Once again this is simply when $\omega = 0$. We get: $$I = \int_a^b t \mathrm{d}{t} = \left[\frac{t^2}{2}\right] = \frac{1}{2}\left(a^2 - b^2\right)$$ When $\omega_n \neq 0$, this can be evaluated using integration by parts. $$ \begin{align*} I &= \left[t \cdot \frac{i}{\omega_n} \cdot e^{-\omega_nti}\right]_a^b - \int_a^b\frac{i}{\omega_n} \cdot e^{-\omega_nti}\mathrm{d}t\\ &= \left[t \cdot \frac{i}{\omega_n} \cdot e^{-\omega_nti}\right]_a^b - \hat{X}\left(\omega_n, \frac{i}{\omega_n}, a, b\right)\\ &= \frac{i}{\omega_n}\left(b \cdot e^{-b\omega_ni} - a \cdot e^{-a\omega_ni}\right) - \frac{i}{\omega_n}\hat{X}\left(\omega_n, a, b\right) \end{align*} $$ Let us denote this as $\hat{Y}(\omega_n, a, b)$. $$\hat{Y}(\omega_n, a, b) = \begin{cases} \frac{i}{\omega_n}\left(b \cdot e^{-b\omega_ni} - a \cdot e^{-a\omega_ni}\right) - \frac{i}{\omega_n}\hat{X}\left(\omega_n, a, b\right) & \omega_n \neq 0\\ \frac{1}{2} \cdot (b^2-a^2) & \omega_n = 0 \end{cases}$$

Fourier Transform of Bezier Basis Functions

This may seem slightly arbitrary, but we want to precalculate the fourier transform of $(t - a)^\alpha \cdot (b - t)^\beta$ for non-negative integers $\alpha, \beta$ and where $a,b$ are also the bounds of integration. That is, we want to find $$ I = \hat{Z}(\omega_n, \alpha, \beta, a, b) = \int_a^b (t - a)^\alpha \cdot (b - t)^\beta \cdot e^{-\omega_nti} \mathrm{d}t $$ Unsurprisingly, this will come in useful when finding the fourier series of bezier curves. This is quite a complex integral and requires a lot of cases, integration by parts and recursive definitions. Let us start with the case $\omega_n, \alpha = 0$. Then, $$ \hat{Z}(0, 0, \beta, a, b) = \int_a^b (b-t)^\beta \mathrm{d}{t} = \frac{-1}{\beta+1}\left[(b - t)^{\beta+1}\right]_a^b = \frac{-1}{\beta+1}\left(0 - (b-a)^{\beta+1}\right) = \frac{(b-a)^{\beta+1}}{\beta+1} $$ Similarly for the case $\omega_n, \beta = 0$ we get: $$ \hat{Z}(0, \alpha, 0, a, b) = \int_a^b (t-a)^\alpha \mathrm{d}{t} = \frac{1}{\alpha+1}\left[(t-a)^{\alpha+1}\right]_a^b = \frac{1}{\alpha+1}\left((b-a)^{\alpha+1}-0\right) = \frac{(b-a)^{\alpha+1}}{\alpha+1} $$ Now for the case where we have $\omega = 0, \alpha,\beta \neq 0$ we need some integration by parts. By always selecting $u = (t-a)^\alpha$ and $\mathrm{d}v = (b-t)^\beta$ we end up with an integral where $\alpha$ has decreased by one. We end up recursively repeating this until $\alpha=0$ in which case we can use the case we have already calculated. $$ \begin{align*} \hat{Z}(0, \alpha, \beta, a, b) &= \left[-\frac{1}{\beta+1}(t-a)^\alpha(b-t)^{\beta+1}\right]_a^b - \int_a^b -\frac{\alpha}{\beta+1} (t-a)^{\alpha-1} (b-t)^{\beta+1} \mathrm{d}{t}\\ &=\left[-\frac{1}{\beta+1}(t-a)^\alpha(b-t)^{\beta+1}\right]_a^b +\frac{\alpha}{\beta+1}\hat{Z}(0,\alpha-1,\beta+1,a,b)\\ &= -\frac{1}{\beta+1}(b-a)^\alpha(b-b)^{\beta+1} + \frac{1}{\beta+1}(a-a)^\alpha(a-t)^{\beta+1} +\frac{\alpha}{\beta+1}\hat{Z}(0,\alpha-1,\beta+1,a,b)\\ &= -\frac{1}{\beta+1}(b-a)^\alpha 0^{\beta+1} + \frac{1}{\beta+1}0^\alpha(a-t)^{\beta+1} +\frac{\alpha}{\beta+1}\hat{Z}(0,\alpha-1,\beta+1,a,b)\\ &= \frac{\alpha}{\beta+1}\hat{Z}(0,\alpha-1,\beta+1,a,b) \end{align*} $$ Using a little induction (or just looking at this long enough) we can find a non-recursive form of this case which ends up also being valid even when $\alpha$ or $\beta$ are zero (or both). $$\hat{Z}(0,\alpha,\beta,a,b) = \frac{\alpha!\beta!}{\left(\alpha+\beta+1\right)!}(b-a)^{\alpha+\beta+1}$$ One simple case is when $\alpha,\beta=0$ and $\omega_n \neq 0$. Then we just have $$\hat{Z}(\omega_n,0,0,a,b) = \hat{X}(\omega_n,a,b)$$ Now let's consider the case where only $\alpha=0$. Again we can use integration by parts. $$ \begin{align*} \hat{Z}(\omega_n,0,\beta,a,b) &= \int_a^b(b-t)^\beta e^{-\omega_nti}\mathrm{d}{t}\\ &= \left[\frac{i}{\omega_n}(b-t)^\beta e^{-\omega_nti}\right]_a^b - \int_a^b -\beta \cdot \frac{i}{\omega_n} \cdot (b-t)^{\beta-1}e^{-\omega_nti}\mathrm{d}t\\ &= -\frac{i}{\omega_n}(b-a)^\beta e^{-\omega_n a i} + \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, 0, \beta-1,a,b) \end{align*} $$ There does not appear to be a nicer closed form than this. Now let's consider the almost identical case where only $\beta=0$. $$ \begin{align*} \hat{Z}(\omega_n,\alpha,0,a,b) &= \int_a^b(t-a)^\alpha e^{-\omega_nti}\mathrm{d}{t}\\ &= \left[\frac{i}{\omega_n}(t-a)^\alpha e^{-\omega_nti}\right]_a^b - \int_a^b \alpha \cdot \frac{i}{\omega_n} \cdot (t-a)^{\alpha-1}e^{-\omega_nti}\mathrm{d}t\\ &= \frac{i}{\omega_n}(b-a)^\alpha e^{-\omega_n b i} - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, 0,a,b) \end{align*} $$ Finally we can consider the general case when $\omega_n,\alpha,\beta$ are all non-zero. We again do integraion by parts where $u = (t-a)^\alpha(b-t)^\beta$ and $\mathrm{d}u = e^{-\omega_nti}$. $$ \begin{align*} \hat{Z}(\omega_n, \alpha, \beta, a, b) &= \int_a^b (t - a)^\alpha \cdot (b - t)^\beta \cdot e^{-\omega_nti} \mathrm{d}t\\ &= \left[\frac{i}{\omega_n}(t-a)^\alpha(b-t)^\beta e^{-\omega_nti}\right]_a^b - \int_a^b \frac{i}{\omega_n}\left(\alpha(t-a)^{\alpha-1}(b-t)^\beta-\beta(t-a)^\alpha(b-t)^{\beta-1}\right) e^{-\omega_nti} \mathrm{d}t\\ &= \left[\frac{i}{\omega_n}(t-a)^\alpha(b-t)^\beta e^{-\omega_nti}\right]_a^b - \int_a^b \frac{\alpha i}{\omega_n}(t-a)^{\alpha-1}(b-t)^\beta e^{-\omega_nti} \mathrm{d}t + \int_a^b \frac{\beta i}{\omega_n}(t-a)^\alpha(b-t)^{\beta-1} e^{-\omega_nti} \mathrm{d}t\\ &= \left[\frac{i}{\omega_n}(t-a)^\alpha(b-t)^\beta e^{-\omega_nti}\right]_a^b - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, \beta, a, b) + \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, \alpha, \beta-1, a, b)\\ &= \frac{i}{\omega_n}(b-a)^\alpha(b-b)^\beta e^{-\omega_nti} - \frac{i}{\omega_n}(a-a)^\alpha(b-a)^\beta e^{-\omega_nti} - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, \beta, a, b) + \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, \alpha, \beta-1, a, b)\\ &= \frac{i}{\omega_n}(b-a)^\alpha 0^\beta e^{-\omega_nti} - \frac{i}{\omega_n}0^\alpha(b-a)^\beta e^{-\omega_nti} - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, \beta, a, b) + \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, \alpha, \beta-1, a, b)\\ &= \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, \alpha, \beta-1, a, b) - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, \beta, a, b) \end{align*} $$ Combining everything we get: $$ \hat{Z}(\omega_n, \alpha, \beta, a, b) = \begin{cases} \frac{\alpha!\beta!}{\left(\alpha+\beta+1\right)!}(b-a)^{\alpha+\beta+1} & \omega_n = 0\\ \hat{X}(\omega_n,a,b) & \alpha, \beta = 0\\ -\frac{i}{\omega_n}(b-a)^\beta e^{-\omega_n a i} + \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, 0, \beta-1,a,b) & \alpha = 0\\ \frac{i}{\omega_n}(b-a)^\alpha e^{-\omega_n b i} - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, 0,a,b) & \beta = 0\\ \frac{\beta i}{\omega_n}\hat{Z}(\omega_n, \alpha, \beta-1, a, b) - \frac{\alpha i}{\omega_n}\hat{Z}(\omega_n, \alpha-1, \beta, a, b) & \text{otherwise} \end{cases} $$

Fourier transform of an exponential

$$ \begin{align*} I = \hat{W}(\omega_n, p, a, b) &= \int_a^b e^{pt} \cdot e^{-\omega_nti} \mathrm{d}t\\ &= \int_a^b e^{(p - \omega_n i)t}\mathrm{d}t \end{align*} $$ If $p-\omega_ni=0$, then clearly $I = b-a$. Otherwise, $$ \begin{align*} I = \left[\frac{1}{p-\omega_n i} e^{(p - \omega_n i)t}\right]_a^b = \frac{1}{p-\omega_n i} \cdot \left(e^{(p - \omega_n i)b} - e^{(p - \omega_n i)a}\right) \end{align*} $$ So, $$ \hat{W}(\omega_n, p, a, b) = \begin{cases} \frac{1}{p-\omega_n i} \cdot \left(e^{(p - \omega_n i)b} - e^{(p - \omega_n i)a}\right) & p - \omega_n i \neq 0\\ b-a & \text{otherwise} \end{cases} $$

Fourier Transform of a Line

Recall that our equation for a line is $f(t) = a + vt$. We therefore want to evaluate the following: $$ \begin{align*} \hat{g}(\omega_n) = \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot f_k\left(\left(t - \frac{k}{N}\right) \cdot N\right) \mathrm{d}t &= \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot \left(a + v \cdot \left(\left(t - \frac{k}{N}\right) \cdot N\right)\right) \mathrm{d}t\\ &= \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot \left(a + v \cdot \left(Nt - k\right)\right) \mathrm{d}t\\ &= \int_{\frac{k}{N}}^{\frac{k+1}{N}} e^{-\omega_nti} \cdot \left(\left(a - vk\right) + vNt\right) \mathrm{d}t\\ &= \int_{\frac{k}{N}}^{\frac{k+1}{N}} \left(a - vk\right)e^{-\omega_nti} \mathrm{d}t + \int_{\frac{k}{N}}^{\frac{k+1}{N}} vNt e^{-\omega_nti} \mathrm{d}t \end{align*} $$ Then, using our pre-calculated itegrals we get: $$\hat{g}(\omega_n) = \left(a-vk\right)\hat{X}\left(\omega_n, \frac{k}{N}, \frac{k+1}{N}\right) + vN\hat{Y}\left(\omega_n, \frac{k}{N}, \frac{k+1}{N}\right)$$

Fourier Transform of a Bezier Curve

Recall that for a Bezier Curve we have: $$f(t) = (1-t)^3p_0 + 3(1-t)^2tp_1 + 3(1-t)t^2p_2 + t^3p_3$$ We wish to find the follwing integral: $$\hat{g}(\omega_n)=\int_\frac{k}{N}^\frac{k+1}{N} e^{-\omega_nti} \cdot f_k\left(N\left(t - \frac{k}{N}\right)\right) \mathrm{d}t$$ To clean things up, let's set $a = \frac{k}{N}$ and $b = \frac{k+1}{N}$. $$\hat{g}(\omega_n)=\int_a^b e^{-\omega_nti} \cdot f_k\left(N\left(t - a\right)\right) \mathrm{d}t$$ Subsituting this directly into our equation for $f(t)$ could get messy so let us first find a simpler form for $(1-t')$ where $t' = N(t-a)$. $$ 1-t' = 1-N(t-a) = N\left(\frac{1}{N} - (t-a)\right) = N\left(a+\frac{1}{N}-t\right) $$ If we realise that $a+\frac{1}{N}=b$ we can write it as: $$ 1-t' = N(b-t) $$ And we are now ready to do the substition. $$ \begin{align*} f_k(N(t-a)) &= N^3(b-t)^3p_0 + 3N^3(b-t)^2(t-a)p_1 + 3N^3(b-t)(t-a)^2p_2 + N^3(t-a)^3p_3 \end{align*} $$ And so now we can get $\hat{g}(\omega_n)$ $$ \begin{align*} \hat{g}(\omega_n) &= \int_a^b e^{-\omega_nti} \cdot f_k\left(N\left(t - a\right)\right) \mathrm{d}t\\ &= \int_a^b N^3 \cdot \left((b-t)^3p_0 + 3(b-t)^2(t-a)p_1 + 3(b-t)(t-a)^2p_2 + (t-a)^3p_3\right) e^{-\omega_nti} \mathrm{d}t\\ &= N^3 \int_a^b \left((b-t)^3p_0 + 3(b-t)^2(t-a)p_1 + 3(b-t)(t-a)^2p_2 + (t-a)^3p_3\right) e^{-\omega_nti} \mathrm{d}t\\ &= N^3 \int_a^b (b-t)^3p_0e^{-\omega_nti} + 3(b-t)^2(t-a)p_1e^{-\omega_nti} + 3(b-t)(t-a)^2p_2e^{-\omega_nti} + (t-a)^3p_3e^{-\omega_nti} \mathrm{d}t\\ &= N^3 \left( \int_a^b (b-t)^3p_0e^{-\omega_nti} \mathrm{d}t + \int_a^b 3(b-t)^2(t-a)p_1e^{-\omega_nti} \mathrm{d}t + \int_a^b 3(b-t)(t-a)^2p_2e^{-\omega_nti} \mathrm{d}t + \int_a^b (t-a)^3p_3e^{-\omega_nti} \mathrm{d}t \right)\\ &= N^3 \left( p_0\int_a^b (b-t)^3e^{-\omega_nti} \mathrm{d}t + 3p_1\int_a^b (b-t)^2(t-a)e^{-\omega_nti} \mathrm{d}t + 3p_2\int_a^b (b-t)(t-a)^2e^{-\omega_nti} \mathrm{d}t + p_3\int_a^b (t-a)^3e^{-\omega_nti} \mathrm{d}t \right)\\ &= N^3 \left(p_0\hat{Z}(\omega_N,0,3,a,b)+3p_1\hat{Z}(\omega_N,1,2,a,b)+3p_2\hat{Z}(\omega_N,2,1,a,b)+p_3\hat{Z}(\omega_N,3,0,a,b)\right) \end{align*} $$

Fourier Transform of an Arc

Recall that for an arc we have: $$ f(t) = C + \frac{1}{2} \cdot e^{\left(\alpha + \theta_0 + t \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{\left(\alpha - \theta_0 - t \cdot \Delta\theta\right)i} \cdot (r_x - r_y) $$ Then, $$ \begin{align*} \hat{g}(\omega_n) = \int_\frac{k}{N}^\frac{k+1}{N} e^{-\omega_nti} \cdot f_k\left(N\left(t - \frac{k}{N}\right)\right) \mathrm{d}t \end{align*} $$ Again let us set $a = \frac{k}{N}$ and $b = \frac{k+1}{N}$ $$ \begin{align*} \hat{g}(\omega_n) &= \int_a^b e^{-\omega_nti} \cdot f_k\left(N\left(t - a\right)\right) \mathrm{d}t\\ &= \int_a^b e^{-\omega_nti} \cdot \left(C + \frac{1}{2} \cdot e^{\left(\alpha + \theta_0 + N\left(t - a\right) \cdot \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{\left(\alpha - \theta_0 - N\left(t - a\right) \cdot \Delta\theta\right)i} \cdot (r_x - r_y)\right) \mathrm{d}t\\ &= \int_a^b e^{-\omega_nti} \cdot \left(C + \frac{1}{2} \cdot e^{\left(\alpha + \theta_0 - a\Delta \theta + Nt \Delta\theta\right)i} \cdot \left(r_x + r_y\right) + \frac{1}{2} \cdot e^{\left(\alpha - \theta_0 +a \Delta\theta - Nt\Delta\theta\right)i} \cdot (r_x - r_y)\right) \mathrm{d}t\\ &= \int_a^b e^{-\omega_nti} \cdot \left(C + \frac{1}{2} \left(r_x + r_y\right) e^{\left(\alpha+\theta_0-a\Delta\theta\right)i} e^{Nt \Delta\theta i} + \frac{1}{2} (r_x - r_y) e^{\left(\alpha - \theta_0 + a \Delta\theta\right)i}e^{-Nt\Delta\theta i} \right) \mathrm{d}t\\ &= \int_a^b C \cdot e^{-\omega_nti} \mathrm{d} t + \int_a^b \frac{1}{2} \left(r_x + r_y\right) e^{\left(\alpha+\theta_0-a\Delta\theta\right)i} e^{Nt \Delta\theta i} e^{-\omega_nti} \mathrm{d}t + \int_a^b \frac{1}{2} (r_x - r_y) e^{\left(\alpha - \theta_0 + a \Delta\theta\right)i}e^{-Nt\Delta\theta i} e^{-\omega_nti}\mathrm{d}t\\ &= \int_a^b C \cdot e^{-\omega_nti} \mathrm{d} t + \frac{1}{2} \left(r_x + r_y\right) e^{\left(\alpha+\theta_0-a\Delta\theta\right)i} \int_a^b e^{Nt \Delta\theta i} e^{-\omega_nti} \mathrm{d}t + \frac{1}{2} (r_x - r_y) e^{\left(\alpha - \theta_0 + a \Delta\theta\right)i} \int_a^b e^{-Nt\Delta\theta i} e^{-\omega_nti}\mathrm{d}t\\ &= C \cdot \hat{X}(\omega_n, a, b) + \frac{1}{2} \left(r_x + r_y\right) e^{\left(\alpha+\theta_0-a\Delta\theta\right)i} \cdot \hat{W}(\omega_n, N\Delta\theta i, a, b) + \frac{1}{2} (r_x - r_y) e^{\left(\alpha - \theta_0 + a \Delta\theta\right)i} \cdot \hat{W}(\omega_n, -N\Delta \theta i,a,b) \end{align*} $$

Bringing it all together

Finally, we can calculate the values we need for all the circles. We can select circle frequences $\omega_n = -50\pi, -48\pi, \ldots 0, \ldots, 48\pi, 50\pi$ and then calculate all the starting positions with $$ c_n = \sum_k \hat{g}_k(\omega_n) $$ For what you see on the main page, the starting positions of the circles are pre-calculated (you can even find the data at anatol.nz/circles/anatol.json). Then, a little bit of javascript reads it and renders the moving circles using an HTML Canvas.

As an Easter Egg, when the page is loaded, there is a one in hundred chance that it will display the text "[object Object]" instead of "Anatol".