Mandelbrötchen
Table of Contents
Overview
At the 38c3 in Hamburg there was a workshop titled Explorations of the Complex Plane which ended up being about creating pretty pictures and animations from the Mandelbrot set. I had a lot of fun there decided to create a WebGL based version. The quadratic map of the Mandelbrot set served as the starting point, but the final result is pretty far from it although one can sometimes still see traces.
Some of the parameters will be explained below. The code can be found on GitHub and sourcehut. Most of the colormaps are taken from matplotlib (see here).
The original Mandelbrot set
The Mandelbrot set is the subset of the complex plane consisting of those numbers \(c \in \mathbb{C}\) for which the quadratic map
\begin{align} \label{eq:mandelbrot} z_{n+1}(c) = z_{n}(c)^2 + c, \quad z_{0}(c) = 0, \end{align}stays bounded as \(n \to \infty\). In other words, a complex number \(c\) is in the Mandelbrot set if starting from \(z_{0}(c) = 0\) and calculating \(z_1, z_2, \ldots\) using equation \eqref{eq:mandelbrot} the absolute value \(|z_n(c)|\) never diverges.
If \(|z_n(c)| > 2\) for some \(n \in \mathbb{N}\), then the series diverges: By the triangle inequality we have
\begin{align} |z_{n+1}(c)| = |z_{n}(c)^2 + c| \geq |z_n(c)|^2 - |c|. \end{align}Now if \(|c| \leq 2\) and \(|z_n(c)| > 2\), then \(|z_n(c)| > |c|\) and we get
\begin{align} |z_{n+1}(c)| > |z_n(c)|^2 - |z_n(c)| = |z_n(c)| \left(|z_n(c)| - 1\right) > |z_n(c)|. \end{align}This means that the series diverges. If already \(|c| > 2\), then \(|z_2| = |c^2 + c| > |c| > 2\), so the series also diverges.
The upshot is that \(c \in \mathbb{C}\) is in the Mandelbrot set if and only if \(|z_n(c)| \leq 2\) for all \(n \geq 0\). This leads to the most naive algorithm to compute the set which simply iterates the map \eqref{eq:mandelbrot} for points \(c\) in the complex plane and at each step \(n\) checks if \(|z_n(c)|\) is still less than or equal to \(2\). For zooming deep into the fractal this algorithm is not very well-suited because the computations would have to be done with multi- or arbitrary-precision numbers which tend to be a lot slower than machine-precision. There are better algorithms that work around this issue but they will be left for some other time.
Deforming and coloring it
We can now introduce arbitrary parameters into the quadratic map \label{eq:mandelbrot} and the cutoff condition \(|z_n(c)| \leq 2\). In the animation above, the cutoff condition is
\begin{align} \label{eq:cutoff-condition} \left||\Re(z)|^{\alpha} + i |\Im(z)|^{\alpha}\right| > \beta \end{align}where \(\alpha\) and \(\beta\) are parameters that can be adjusted. We also introduce some strange phases depending on a time parameter \(t\), the step \(n\) and the total number of steps to be calculated \(N\):
\begin{align} \gamma_n = \exp\left[i \pi n \left(\frac{1}{2 N} + 2 t\right)\right], \quad \delta_n = \exp\left[-i \pi f_1 \sin(\pi t)\right]. \end{align}We then use them to modulate \(c\) before feeding it into the quadratic map,
\begin{align} \tilde{c}_n = \gamma_n \delta_n \left[\frac{1}{2} \sin(\pi f_0 t) + 1\right] g^n \cdot c. \end{align}The frequencies \(f_0\) and \(f_1\) as well as the growth factor \(g\) can also be adjusted above. These modifications are not at all mathematically motivated and were only chosen because they produced a few nice pictures.
Finally, the color value of each pixel corresponds to the number of steps \(n\) for which the cutoff condition \eqref{eq:cutoff-condition} is satisfied. The color itself is obtained by mapping \(n\) to the interval \([0, 1]\) and taking the color of the chosen colormap at that value.