As an avid fan of abstract art, fractals make for an excellent basis for the making of intricate images that even though they are packed with detail and vague complexion, contain recognizable, aesthetically-pleasing patterns.

Perhaps the most well known fractal is the Mandelbrot set. The generation of a high-resolution rendering of its form is painfully slow to compute when forced to do so sequentially. But when utilizing the parallel processing power of modern graphical processors, it becomes possible to render and navigate through it in real-time.

Table of contents


View the the full album in high-resolution here.


The Mandelbrot set is a mathematical set defined on the complex plane. Given the function:

$$ f_c: \mathbb{C} \rightarrow \mathbb{C} $$ $$ f_c(z) = z^2 + c $$

where \(c \in \mathbb{C}\) is an independently chosen parameter, and the following sequence:

$$ V_c := (0, f_c(0), f_c(f_c(0)), \ldots) $$

where \(f_c\) is repeatedly composed with itself. If we denote the \(i\)th item in \(V_c\) as \(V_c^i\), then the Mandelbrot set can be expressed as:

$$ M := \{c \in \mathbb{C} \vert |V_c^i| \le 2 \text{ for all } i \ge 0\} $$

From the definition we can see that the Mandelbrot set is compact, i.e. contained within a closed region. In our case, this region is the complex circle of radius two.

Figure 1: Complete view of the Mandelbrot set.

System design

The rendering pipeline of our application looks like this: First, the user defines the rectangular portion of the set he/she wishes to render by means of moving a virtual camera. The position of the camera determines the position of the viewport and the zoom-value determines its size.

Once the viewport is determined, we begin the rendering process by computing the complex number \(c\) that is associated with each pixel in the image, given the current viewport properties and the target resolution of the output. Next, we compute \(V_c\) until we either reach a value \(V_c^i\) that does not belong to \(M\), or we reach a user-supplied threshold of performed iterations \(i_{max}\) that we won't compute beyond.

At this point, with each pixel \(p\) we have associated a number \(i_p\) that indicates how many iterations it took before we reached a value for which we had to stop the computation of \(V_c\). Now we will color each pixel based on this number.

There are several ways for us to map \(i_p\) to a color. The simplest is to map it onto an index in a predefined list of colors, and take the color at that index as our chosen one. For example, the palette used to color the images in the showcase is visible below:

Figure 2: Example of a color-palette.


We implemented our application using C++ and OpenGL. The entire computation is performed on the GPU via shaders, and the result looks like this:

Figure 3: Zooming into the Mandelbrot set.

Source code

You can view the source code and documentation of the main module on GitHub.

See also