Fractal software, math and Apollonian gasket animations

Felipe Lopes
7 min readAug 21, 2022

In a previous post, I described how to use a lot of complicated math in order to describe Apollonian gaskets in a way that modern fractal software can render them. After doing that, the path that one should take is pretty obvious: complicate things even more and take it to the next level to render gasket animations!

Below you can see the result of a test run, the quality can probably be improved a bit by making convergence better. The purpose of this article, however, is to just show the process to create this, so let us not worry about the animation quality, at least for now.

Test render of a very simple gasket animation

Before we start, it's important to remember where we left at the previous post. The results there were an Iterated Function System (IFS), which is readable by Apophysis, a modern fractal art software, and renders any possible configuration of the Apollonian gasket. There were, however, several limitations with that approach, all of them needed to be solved in order to accomplish the next step, which I'm publishing here.

The first problem was of a software engineering nature. At that time, my code merely generated the parameters for the IFS transformations, which needed to be entered by hand in the software. That's very cumbersome if you want to test things, since there are a lot of manual steps involved. My first solution to this problem was to add an XML library as a dependency, and generate the flame XML file inside the code, which then I could load directly into Apophysis.

That worked for a while, but although it speeds things up a little bit, there's still some work of changing the code, then running it to generate the file, and finally open it. There's a much better way, which I could do by changing the fractal software, and doing everything in Qosmic. If you don't know Qosmic, it's an alternative to Apophysis that does everything we need. In my opinion, its UI needs a lot more tweaking to work out of the box. However, for my purposes it's much better because it has a killer feature: Lua scripting!

For those who are not familiar with Lua, it's a minimalist programming language very popular for embedding in applications. Although its primary structure for handling data is called table, it does support object oriented programming with some clever code tricks. The way it's used in larger applications is pretty simple. Looking at Qosmic's source code, it's essentially a Qt front-end which routes UI requests to render fractals using the flam3 library. Lua scripting works by Qosmic loading a Lua script and exposing to it an API to its internal data structures.

Because of this, doing what we did before in Apophysis is much simpler in Qosmic, we can simply open up its Lua scripting window, code on it, and our changes are propagated to Qosmic's UI and can be seen immediately, without any indirection created by code compilation, serialization, files, etc.

Armed with this new tool, I re-implemented most things I needed in Lua, and used Qosmic's API, documented here. I uploaded all my Lua scripts to my GitHub repository. There are some important additions to the functions in my previous post, they were needed in order to solve other problems in my previous approach.

Recalling from my previous post, a general Apollonian gasket was generated exploiting the fact that a gasket is activated by an ideal triangle, and by conjugating Möbius transformations that mapped an ideal triangle to another, we could generate a new gasket. However, this representation is far from unique, as any gasket has infinitely many ideal triangles. Worse than that, given an ideal triangle, it's pretty difficult to deduce some pretty basic properties about its geometry, like its size.

First, let us attack the mathematical problem of determining uniquely the parameters that control a general gasket. This problem relates to another question, which is how many Möbius transformations are necessary to create the IFS for the gasket. My previous solution used four transformations, while there's a solution for the symmetrical gasket, commonly attributed to Kravchenko and Mekhontsev, which uses three.

Through a combination of trial and error, and using some general properties of the classification of Möbius transformations, I am glad to announce that I managed to improve on the Kravchenko-Mekhontsev attractor and successfully contructed an attractor that uses only two Möbius transformations, and works for all gaskets, not only the regular one. Two transformations is clearly the minimum, as an IFS with a single Möbius transformation cannot generate a gasket, so this problem is now solved.

Explaining how the solution works is difficult (actually, when you work for a long time in math, you no longer understand things, you just get used to them), it's easier to show the relevant code in Lua:

The way this works is pretty daunting. We start with the Apollonian strip, see here for its precise definition. It's essentially a version of the gasket, which is extended along a strip. To generate all ideal triangles in this configuration, we need two Möbius transformations, the first is a translation of 2 units in the positive x direction. The second is more complicated, it's an elliptic Möbius transformation which, when applied three times, gives back the identity, and it has two fixed points in the complex plane, located precisely at the positive and negative square root of 3.

To construct this second transformation, we start with a rotation of 120 degrees about the origin, which fixes 0 and the point at infinity, and conjugate with a transformation that sends 0 and infinity to the positive and negative square root of 3. This transformation, together with the first one and their inverses, generates all of the ideal triangles in the gasket. In order to implement this in an IFS, we can use the translation as the first transform and the rotation composed with the inverse translation as the second, and the IFS will scan all regions reached by their span.

This, however, only works to generate the Apollonian strip. That's not a problem, though, because by conjugating with the transformation z -> i/z, we go from the Apollonian strip to the Apollonian window, another very symmetrical gasket, but one that is contained entirely in the unit disk of the complex plane.

We could now generate all other gaskets by conjugating with an arbitrary Möbius transformation, but we can restrict them further to gain more control over the geometry. Recall that our gasket is now within the unit disk, so if we conjugate with a transformation that maps the unit disk to itself, the resulting gasket will not change in side. It turns out that the transformations that fix the unit disk form a well known mathematical group, called SU(1,1). An element of this group is determined by three parameters, which I called the hyperbolic angle, that generates the modulus of u and v, and the phases of u and v.

Awesome, but with the mathematical problem solved, we still face another problem, this time of an artistic nature. Even though we managed to take control of the parameters that generate the gasket, we have no idea what they mean. For example, which set of parameters do we use to generate the regular gasket, with three circles of the same size? Well, it's extremely hard to answer that question.

The solution to this involves, of course, more math. It turns out that it's possible to combine the geometry of the gasket's circles with everything we've learned so far, about the SU(1,1) Möbius transformations, and ideal triangles, for a gran finale! It's implemented in the following code:

This is a very long function, which needs to be explained carefully. First of all, we control the geometry of the gasket by specifying the two largest radii of the circles inscribed in the unit circle, of radius 1, and a phase that rotates it. The code then performs some checks to see if the radii are viable. The less trivial of those are the checks to see if the radii are really the largest ones.

This is done using a very elegant formula known as Descartes' circle theorem. It turns out that, whenever four circles are mutually tangent to each other, their curvatures (inverse of radius), subject to a sign convention depending whether the circles are inside or outside, obey a quadratic equation. That way, once you know three curvatures, you immediately know the two possibilities for the fourth. The code simply solves this formula, checking to see if it doesn't give a larger radius than it was provided.

Finally, the code generates the gasket by mapping an ideal triangle of the Apollonian window to a triangle it generates based on the radii data it's provided with. This needs to be done carefully, because we can only use transformations that map the unit disk to itself. There's a simple way to solve this. If the two ideal triangles we are mapping both have two vertices in the unit circle, then the resulting transformation will necessarily have the form we want.

Therefore, to wrap things up, we need to map the ideal triangle of the Apollonian window (given by the points -1, 0, 1) to one in the new gasket with two points in the unit circle. Thankfully, since we are considering the largest circles in the gasket, this can be done using simple trigonometry. From the radii lengths, we can compute the lengths of all sides of a key triangle, and a straightforward application of the law of cosines will give us an angle from which we can find all the vertices we want.

After that, we only need to extract the parameters from the resulting Möbius transformation and construct the gasket. With this setup, constructing the regular gasket is now a fun geometry exercise. We need to find the ratio between the radius of the inner three inscribed circles with respect to the larger one. You can try this as an exercise, the answer is 2sqrt(3)-3.

With all said and done, animations can be made by constructing gaskets you want, finding their SU(1,1) parameters and interpolating between them. In the picture I made one going from the Apollonian window to the regular gasket and back through a different route. Although the quality could be probably improved by tweaking some rendering parameters, I am very happy with the result because we have literally conquered mathematical chaos!

Thanks a lot for reading and see you next time!

--

--