For the well-known Sierpinsky Triangle a triangle is replaced by three smaller ones. One at each corner off the large triangle. The small triangles touch each other at a corner. This process is repeated. We can do the same for any regular polygon. However, using a square this produces a trivial periodic square grid. But for polygons with five or more corners we get interesting fractals. Take as an example the regular hexagon. The iteration looks like this:

This is the result after repeating this procedure seven times:

The black pixels approximate the fractal hexagon. But it is more interesting to look at the space in between and outside. The grey area surrounding the hexagon is the outside of an “inverted” Koch snowflake. Inside you see white Koch snowflakes of different sizes. Actually, the entire surface of the fractal hexagon is covered by these snowflakes. This is similar to the Sierpinsky triangle, which is covered by triangles of different sizes if one looks at the complementing space, and to the Apollonian Gasket, covering a circle with small circles.

You can play with these images using my browser app at

The Ammann-Beenker tiling is a quasiperiodic tiling of eight-fold rotational symmetry. It has squares and rhombi with an acute angle of 45 degrees as tiles. This is a small patch:

The square tiles are divided into two triangles. The substitution rules do not change their shapes:

This results in nice super-tiles useful for decoration. We could also define substitution rules using squares and rhombi; but then iterated tiles have complicated irregular shapes.

Note that the rhombus has a substitution that is symmetric upon rotation by 180 degrees. Thus we not need to care about its orientation. In the JSON input file I gave it the name “rhomb”. The two triangles are mirror images of each other. For some obscure reason I used “RSquare” and “LSquare” as names.

First we have to create the shapes of these tiles. We need unit vectors at angles of 45 degrees and thus use eight for the value of the property “order” of the tiling. The property “drawGeneration” gets to value zero for drawing the shape without substitution. While editing a shape it is convenient to set the property “initial” to the name of the tile we are doing. Then it will be directly shown upon loading the file. All tiles have their left corner at the origin, written as [], which is the first corner. The rhombus tile with name “rhomb” has the second corner at (1,0) for sides of unit length. This becomes the array [1]. The third corner is one unit in horizontal direction and one unit in diagonal direction away from the origin. Thus it lies at [1,1] in the coordinate system of eight directions. The fourth corner is one unit in diagonal direction away from the origin and its coordinates are [0,1]. All four corners define the rhombus shape. It is the array [[], [1], [1,1], [0,1]]. For the two triangles we get the same second corner. The third corner lies one unit at the right of the origin and one unit in vertical direction. This gives it the coordinates [1,0,1]. Thus the triangles have the shape [[], [1], [1,0,1]]. The input file is at this stage:

Upon loading, this shows the rhombus. I have redefined its color as blue instead of the default value red. You can see other tiles by changing the selection for “initial tile” in the user interface of the program or by giving the “initial” parameter another value. You can change the color of tiles in the sub interface “colors of tiles” or by using “color” definitions in the input file. Yellow and green are default colors for the tiles defined as second and third ones.

We now set up the substitution rules. As you can see in the figure above, on each side of a parent tile there is one side of a child rhombus and a hypotenuse of a child triangle, except for the hypotenuse of a parent triangle. Thus the parent triangle is larger by a factor of 1+sqrt(2)=2.414 and we have to use this value for the “inflation” property of the tiling. While editing the substitution rule of a tile you should put the properties “drawGeneration” and “maxGeneration” equal to one and “initial” to the name of the tile. Then you immediately see changes upon loading the modified JSON file.

The substitution rules for tiles require a lot of typing. Each child tile requires a name, its origin and orientation. The program simplifies this using default values. At the beginning of each child tile definition, the default value for the origin is the same as the parent tile. Each new definition of an origin serves as default value for further child tiles. The default value for the orientation is zero at the beginning and after each new definition of the origin. As long as the origin is not changed, each child tile changes the default orientation by the value of its “angle” property. This is the angle between the lines of the tile meeting at its origin, given as a multiple of the angle between the basis vectors. For the Ammann-Beenker tiling the “order” is equal to eight and defines an angle of 45 degrees. From the second figure of this post you can see that for all tiles the angle at the origin (the left corner) is 45 degrees. Thus, for all these tiles the “angle” property es equal to one.

To define a substitution is easy. For each child tile you add an object with its name, origin and orientation to the “substitution” array. Take into account that most sides of child tiles have unit length and use the special coordinate system of eight basis vectors. You can save some typing by using default values.

For a parent rhombus we see that that there are three child rhombi. One of them is at the left corner of the parent rhombus. Its origin and orientation is the same and thus {“name”:”rhomb”} is enough to generate it. Another rhombus lies at the other acute angle of the parent. To go to its left corner you have to take one step to the right, a diagonal up, a diagonal down and a vertical step up. In the special coordinate system this is the vector [1,1,1,-1]. We do not need to type its “orientation” with default value zero. This child rhombus then results from {“name”:”rhomb”,”origin”:[1,1,1,-1]}. The last rhombus has its acute angle at the obtuse corner of the parent. To get there, we do a step in horizontal, diagonal and vertical directions which gives the origin as [1,1,1]. The base line of the rhombus points in the negative y-direction. This corresponds to an angle of 270 degrees with respect to the positive x-direction and is the 6th basis vector. The “orientation” property thus gets the value six. The object describing the third rhombus is {“name”:”rhomb”,”orientation”:6,”origin”:[1,1,1]}. We can proceed similarly to complete the substitution rules for all three tiles:

Using larger values for “maxGeneration” and “drawGeneration” this already generates the Ammann-Beenker tiling. But the images aren’t yet nice. Diagonal lines are drawn across the squares because there is a line around the border of the triangles “RSquare” and “LSquare”. This is the default behavior of the program for making borders; it closes the path given by the shape. We can correct this with the “border” property of the tile. Its value is an array of coordinates of points for drawing the border similarly to the “shape” property, except that the program now does not connect the last point with the first one. If the array is empty, then the points of the “shape” array will be used. Here this is what we need. So, the “border” property of the two triangles will have [] as value. This eliminates the diagonal lines. But now a small gap appears at the diagonals of squares because the browser does not completely fill the triangles. We can correct this with the “overprint” property of tiles. Its value is again an array of coordinates. The program connects the points with a line using the same color as the inside of the tile. Here we have to use the endpoints of the hypotenuse, giving [[], [1,0,1]] as appropriate value. With these two additions we can get good images.

Using the rhombus or a triangle as initial tile we do not get an image of eight-fold rotational symmetry. Note that in the Ammann-Beenker tiling there are stars of eight rhombi with eight-fold rotational symmetry. Using such a star as initial configuration and doing the substitutions we get a patch of the tiling with perfect eight-fold rotational symmetry.

Thus we define such a star as an additional tile, which is only used initially. The “shape” property of this tile has the coordinates of its 16 corner points and is easy to define using the coordinate system of eight basis vectors. We do not set its “substitution” property, instead we use a “composition” property. This tells the program that a tile is made of parts using as data the same array of objects as the “substitution” property. But for “composition” the program replaces the tile immediately by these parts and applies their substitution rules. In this way we can profit from the work we have done for the rhombi. We use that the default origin for child tiles is the origin of the parent tile and that the default orientation is initially zero, increasing by the “angle” property of each added child tile. Thus the “composition” property is an array containing eight times a {“name”:”rhomb} object. Finally, the “range” property of the tiling gets a sufficiently large value to account for the size of the star tile. This gives in the end a rather lengthy JSON file:

It is also known as the paper-folding curve because you can make it with a strip of paper, folding it several times in the middle and opening the folds, such that they have angles of 90 degrees. From this idea we easily get the substitution rule for the dragon curve:

The dragon curve is made of line segments. They are successively replaced by smaller line segments at angles of 90 degrees. The position of this angle is important. It is indicated by the black dots that serve as markers. Note that if you reduce the 90 degree angles to zero, then the two line segments and their markers fit together. This means that the folds match.

The JSON file for this fractal is quite similar to the one of the last post that creates the fractal tree. We simply have to change the position and orientation of lines. But we have now something new: The marker. To have a marker one simply adds the “marker” property to the tile object. Its value is the position of the marker defined in the same way as corners of tiles and origins in the substitution rule. Here it lies half way between the end points of the line and a bit below the line. In the eight-fold coordinate system this gives the array [0.5, 0, -0.1] for the position. You can switch off the marker in the user-interface of the app. There you can also change its size and color. All this results in:

you can create many of the popular fractals using line segments, such as the Dragon curve. I am showing you how to define a fractal tree and presenting some additional options of the program. The fractal looks like this:

And this is its substitution rule:

A line segment gets replaced by two smaller lines at relative angles of 45 degrees. The “order” property of the fractal is set to eight, which gives basis vectors at angles of multiples of 45 degrees. To draw a vertical line we note that the third unit vector (i=2) is parallel to the y-axis. Thus the endpoints of such a line are the origin at [] and the point above it at [0,0,1] in the coordinate system of eight basis vectors. An object made of line segments can be defined like this:

This is similar to the last post. But now we have only a line and not a tile. Thus there is no fill and an outline does not make sense. We can switch both off by setting the properties “fill” and “outline” to false:

To get the fractal we have to add the substitution rule. We note that the child lines lie at the top of the parent line. Thus the “origin” property of the objects defining substitutions is [0,0,1]. The substitution rule now requires the orientations for child lines. They are determined with the property “orientation” of the substitution. Its default value is zero, which is good for the Sierpinsky triangle but not for this tree. An angle of +45 degrees corresponds to the basis vector with index 1 and -45 degrees is the direction of the basis vector 7. Thus we have to use 1 and 7 as orientations. The child lines are smaller than the parent line by a factor of 0.707. We can obtain this by setting the property “scale” to this value. To get a nice image we have to iterate the substitution rule many times. Thus we set “maxGeneration” and “drawGeneration” to 11. The JSON input file becomes:

Loading this, you will not see the entire tree. Instead, only the last generation is shown. To correct this we set its property “drawing” to “lower in back”, which shows all generations of the substitution. The tree appears offset in the image and is too large. We can correct this by setting the “origin” property as before and using the “range” property. The “range” property corresponds to the range of x- and y-value. It is only exact for a square image. So finally we get the fractal tree from:

I will do it step by step. The Sierpinsky triangle and its substitution rule look like this:

As mentioned previously, the browser app reads textfiles written in Javascript Object Notation (JSON). This defines an object having all relevant data for creating a fractal or tiling. Generally, it is useful to add some documentation. This is easy to do. Simply add suitable tags with some text as string data. Some suggestions:

{
"date": "3/5/22",
"author": "Peter",
"comment": "this is a simple example"
}

Actually, this adds some properties to the object, but which will be ignored. Always use quotes around strings!

To begin with a new tiling you should give it a “name” property, such as

{
name: "new Sierpinsky Triangle"
}

Loading a JSON file with this text with the “open file …” button or the “=” key shortcut will add the choice “new Sierpinsky Triangle” to the structure selection. There will be no image as there are no tiles defined yet. Thus the initial tile selection will only show the option “none”.

The property “tiles” defines the tiles of the tiling or fractal. Its value is an object which has as properties the tiles. The tag of each property is the name of a tile and its value is an object, that defines the corresponding tile. The Sierpinsky triangle has only a triangle as tile. The object value of the “tiles” property thus has the single property “triangle”. We can use an empty object as its value:

Loading this, you will see that the selection for the initial tile now shows the option “triangle”. Thus the program knows that this fractal is made of triangles. But nothing is shown yet because there is no definition of the shape of the triangle or its substitution rule.

We now want to add the shape of the triangle, such that it can be drawn. Putting the origin at its left corner and using Cartesian coordinates, its corners are at: (0,0), (1,0), and (0.5,0.832), for sides of unit length. Note that the third corner requires a lot of typing, which is inconvenient. Furthermore, a Cartesian coordinate system requires a lot of trigonometric computations. To avoid this, we use a different coordinate system with unit vectors that are parallel to the sides of the triangle. We simply use the property “order” of the tiling and set its value to six:

{
....
"order": 6;
.....
}

This generates six unit vectors u_{i }= (cos(i*2π/6),sin(i*2π/6)). The position of points can be given in terms of the corresponding 6 coordinates. They are elements of an array and trailing vanishing components need not be written. Thus (0,0) becomes [] (an empty array), (1,0) is [1], and (0.5,0.832) is simplified to [0,1].

In general the property “order” refers to the order of the rotational symmetry of the coordinate system. This is also the rotational symmetry of a quasi-periodic tiling generated by the program. Setting its value to an integer n gives n unit vectors u_{i }= (cos(i*2π/n),sin(i*2π/n)). They are used to define the corners of tiles and the substitution rule for the tiling or fractal.

We add the shape to the triangle object as the value of the property “shape”. It is an array with the coordinates of the triangle corners as elements. For convenience, we add the property “drawGeneration” with value 0 to the tiling object. This tells the program not to do any substitution before drawing and is useful for creating complicated shapes. It is a good idea to check the result after adding each corner. A single triangle will appear if we use as input:

Its color is red, because this is the default color for the first tile. You can change the color with the property “color”. It has as value a six digit hexadecimal color string, such as “#00aa00” for dark green. The triangle appears off center in the image because by default the center of the image is the origin of the coordinate system. You can move the image by dragging the mouse and zoom with the mouse wheel. But often, it is better to define explicitly the coordinates of the image center. You can do this with the property “center” of the tiling or fractal. Its value is an array of x- and y-coordinates for the center. Thus:

will put the center of a green rectangle at the center of the image.

We still have to define the substitution rule. It is the property “substitution” of each tile, in our case the triangle. Its value is an array of objects; one for each child tile. The most important property of child tiles is “name” with a string as its value which denotes the tile. For the Sierpinsky triangle, each triangle has three child triangles. So we can write:

Here I have put the property “drawGeneration” equal to 1. Thus the program applies the substitution rule just once before drawing, which is useful for creating and checking substitution rules. This code tells the program that a parent triangle gets substituted by three child triangles. If you open this, then you will still see only one triangle; but actually there are three triangles, one written on top of the other. Obviously missing are the relative size of parent and child tiles and the position of child tiles, which are important properties of the substitution rule.

An important property of the tiling or fractal is “inflation”. It gives the ratio of the sizes of parent tiles to child tiles upon substitution. For the Sierpinsky triangle its value is equal to 2. To the objects defining child tiles we have to add the position of their origins relative to the origin of the parent tile. It is the property “origin” with an array as value. The array has coordinates using the same coordinate system as for the shape. Note that lengths are scaled by the “inflation” factor. One child triangle has its left corner at the same place as the parent triangle. Thus its relative origin is at position (0,0) and the “origin” property gets the value []. Another child triangle has its origin shifted half the base of the parent triangle. Taking into account the inflation by a factor of two, its “origin” has value [1]. For the third triangle we do similarly. It is shifted half along the left side of the parent triangle parallel to the second basis vector of the 6-fold coordinate system. Thus its “origin” is at [0,1].

It can be convenient to set the property “maxGeneration” of the Sierpinsky triangle. It determines how many iterations of the substitution rule are applied when opening the JSON file. The default value is 4. Note that a large value requires a lot of computational work and may cause a significant time delay upon opening the file. A small value might be useful while creating the structure and checking out changes. The final version of the file defining the Sierpinsky triangle looks like that:

To see the fractal nature of the Sierpinsky triangle you have to increase the value of the generation to draw in the interface at the right. If you want to go beyond 5 you have also to increase the value for highest generation. Zoom in with the mouse wheel and use mouse drag to pan the image. You can change the color of the triangle if you open the “color of tiles” interface. To save the image open the “output image” interface.

As an exercise you could now write a JSON file that creates a Sierpinsky carpet using squares instead of triangles. Hint: Use for “order” the value four to get orthogonal unit vectors. In the next post I will explore further options of the program.

you can make your own tilings and fractals. You simply have to define them in textfiles using Javascript Object Notation (JSON), which is a small subset of Javascript for defining objects. In this post I will explain JSON. Other details concerning the browser app are discussed in the previous post.

The JSON files should have the extension “*.json”. An object definition begins and ends with curly braces. Thus, a file with the text “{}” defines an empty object without any properties. You add properties with tag and value pairs, separated by a colon and terminated by a comma, except for the last one. The tag has always to be surrounded by quote signs. An example:

{ "name": "Peter", "age": 62, }

creates an object with two properties: “name” having value “Peter”, which is a string, and “age” of value 62, a number. The tag can have spaces in it:

{
"just another thing": "laptop"
}

makes an object with property “just another thing” of value “laptop”.

The value of a property can be a simple value: a number, a string (surrounded by quote signs), or boolean (true or false). Note that you cannot use apostrophes instead of quotes. Further, the value can also be an object or an array. This is crucial because not only a tiling is an object, but also its tiles! Usually, there are many different tiles, thus the tiling gets a property that has an array of the corresponding object definitions as its value.

If your file is not correct JSON, then my program will give you an error message without any information about the error. To find the error, you can use an online JSON checker such as https://jsonchecker.com/. But it is much more practical to use an integrated Javascript checker such as JSLint or JSHint with your editor. I am using sublime text (https://www.sublimetext.com/) with JSHint and JSBeautifier as added tools.

It is fun to find substitution rules for a new tiling, but it is rather tedious to program them. It always takes me a lot of time to create a recursive function for each tile, that either draws its shape or decomposes it into sub-tiles, calling the respective functions. Moreover I have to calculate and enter the coordinates of the tile corners in Cartesian coordinates, which is much boring work.

To simplify future work I have written a universal program that can create tilings using appropriate definitions in a simple text file. Furthermore, it uses more appropriate coordinate systems for the positions of points. It is a browser app and you find it here:

This app can generate many popular fractals too, such as the Sierpinsky triangle. Its interface at its right has buttons with question marks. Help texts pop up, if you place your mouse on them. The sub-menues for the output image and the color of tiles are closed. You can open and close them by clicking on their titles. Near the bottom of the interface you have a choice of predefined tilings and fractals. There you can open your definitions of tilings. Instead of clicking on the “open file with structure data” button, you can also use the “=” key on your keyboard as a convenient shortcut. To get an idea how the definitions are done you can download the definitions of examples. They use the Javascript Object Notation (JSON). I will explain the details in the following posts.

As presented in previous posts, inversion in three touching circles makes a Poincaré disc representation of a hyperbolic plane tiled by a triangle. Adding a fourth circle touching the other three results in a fractal decoration of the hyperbolic disc with discs. Actually, this is an Apollonian gasket. I want to do essentially the same thing, but in three dimensions: Inversion in four circles can generate a Poincaré ball representation of hyperbolic space and an additional fifth sphere could create a fractal packing of spheres in this space. In analogy to the two-dimensional case, I am using four spheres that have their centers on the corners of a tetrahedron and add a fifth sphere at the center of the tetrahedron.

It is important to realize that the limit set of the inversion in these five spheres has to cover completely the surfaces of all the spheres of the sphere packing. We can only get this if the limit set of the inversions in four of them covers the entire surface of the Poincaré ball that they generate. Observing that the space lying outside the inverting spheres does not belong to the limit set, we conclude that the inverting spheres have to cover the entire surface of the Poincaré ball. This can definitely not be achieved with touching spheres, as I naively tried first.

Instead, we need intersecting spheres. To see how large they have to be, we simply look at the surfaces of the tetrahedron that has four spheres at its corners. They are equilateral triangles and should lie inside these spheres. We achieve this if the spheres reach the center of the triangles. This corresponds to an intersection angle of 60 degrees between the spheres, see Figure 1. Multiple inversion in all four spheres creates a Poincaré ball representation of a tiled hyperbolic space. The surface of the ball touches the centers of the sides of the tetrahedron. The four spheres can be seen as curved sides of another tetrahedron. This tetrahedron tiles the hyperbolic space. Its corners lie on the centers of the sides of the tetrahedron that has the spheres at its corners. Thus the tiling tetrahedron is its dual tetrahedron. The spatial angle of its corners vanishes. Note that this is similar to the planar angle at the corners of a triangle made with three touching spheres, which vanishes too. Also, the corners of the tiling tetrahedron lie on the surface of the Poincaré ball. Thus this tetrahedron is an ideal tetrahedron.

The space outside the ball is an inverted image of its inside. On the triangle surfaces we get an inverted Euclidean tiling of equilateral triangles, see Figure 1. This is simply a particular cross section of this inverted hyperbolic space. The limit set of the inversions in the four spheres covers the entire surface of the Poincaré ball, see Figures 2 and 3.

To get a fractal packing of Poincaré balls we add a fifth inverting sphere at the center of the tetrahedron. It too intersects the other spheres at an angle of 60 degrees. This creates new groups of four intersecting spheres, always three of the first four spheres together with the new one. Each of these groups creates by multiple inversion a similar hyperbolic space as presented in the first part of this post. Thus we get touching Poincaré balls covered by the limit set of the inversions. Inversion in all five spheres multiplies them and fills in the gaps between the spheres. Thus the entire hyperbolic space gets covered by Poincaré balls. We see this in Figures 4 and 5. They are planar cross-sections of this sphere packing. Instead of spheres we see circles as their cross-section. The sphere packing appears thus as a fractal packing of circles in these figures.

Thus, five intersecting spheres really can make a fractal packing of spheres in three dimensions similarly as four circles generate an Apollonian gasket. Further, we proceed as in the last post and think of the three-dimensional space as being the surface of a Poincaré half-“hyper”-space representation of a four-dimensional hyperbolic space. Using inversion in a four-dimensional sphere, we can get five intersecting spheres of equal radius. Their centers lie on the corners of a 4-simplex, which is a “hyper”-tetrahedron in four-dimensional space. They generate a Poincaré “hyper”-ball representation of hyperbolic space. The fractal sphere packing then lies in its surface, a spherical three-dimensional space. It has the symmetries of the 4-simplex. Thus the packing of spheres is also a fractal decoration of three-dimensional spherical space. Both views are related by a stereographic projection from the surface of the four dimensional sphere to three-dimensional Euclidean space. Unfortunately, it is not possible to extend this scheme one dimension higher.

In the earlier post “Apollonian gasket as a fractal in tiled hyperbolic space” I have already discussed the Apollonian gasket. There, I began with three touching circles that define an ideal triangle with vanishing corner angles. Multiple inversion at the circles creates a Poincaré disc model of a hyperbolic space. It is tiled by images of the triangle. The limit set of the inversions is exactly the boundary of the disc. Adding a fourth circle ,which touches the other three, gives the Apollonian gasket as the limit set of the inversions in all four circles. Seen as a two-dimensional object, it is a fractal decoration of the tiled hyperbolic disc that resulted from the first three circles. On the other hand, in “Apollonian gasket as a spherical fractal with tetrahedral symmetry” I have shown that the Apollonian gasket is also a fractal decoration of a spherical surface with tetrahedral symmetry. Both are related by a stereographic projection.

This becomes more interesting if we think in three dimensions. We can see the four touching circles as equators of spheres. The plane going through their centers is the boundary of two Poincaré half-space models of three-dimensional hyperbolic space. These models lie on the two sides of the plane. The four spheres are flat planes in the corresponding hyperbolic spaces. Multiple inversion in the spheres creates a periodic tiling of these spaces and a non-periodic tiling of their mutual boundary. The limit set is obviously the Apollonian gasket.

As mentioned before, we can project the Apollonian gasket on the surface of a sphere using the stereographic projection. But this projection only maps two-dimensional surfaces. It would be useful if we could somehow use the stereographic projection for the entire three-dimensional space. Considering only the surface of a sphere, its stereographic projection to a plane gives the same result as an inversion in a mapping sphere, which has the same center as the stereographic projection. The radius of the mapping sphere is then directly related to the position of the plane of projection. Typically, the mapping sphere intersects the projected spherical surface in this plane. The first figure shows an example. The center of projection is the north pole of the sphere and the plane of projection goes through its center. The intersection with the mapping sphere is then the equator of the spherical surface and the radius of the mapping sphere is larger by a factor of the square root of two. Using the inversion in the sphere, we can extend the stereographic projection to the entire three-dimensional space. That is exactly what we need.

We now use an additional inverting sphere to map the four spheres and the hyperbolic model they generate. The center of this sphere lies above the center of the Apollonian gasket. It is closer to the smaller sphere than the three other spheres. Thus, inversion in the additional mapping sphere increases the size of the smaller sphere in comparison to the three larger spheres and it is possible to make that all four spheres get the same size after inversion. As they are touching each other, the distances between their centers are then all the same. Then, the centers of the three spheres lie on the corners of a tetrahedron, see the second figure. The spheres touch in the middle of its edges. A small part of the space inside the tetrahedron lies between the four spheres. It is useful to think that this is a tetrahedron with curved sides, which are parts of the surfaces of the spheres. This tetrahedron has no corners. The black regions show its intersection with the large tetrahedron. Yet, these tetrahedrons are dual to each other. Multiple inversion in the four spheres creates a Poincaré ball model of hyperbolic space. It is tiled by the curved tetrahedron between the four spheres. The surface of the ball model is drawn in the second figure in red color. Note that the tiling tetrahedron has no corners inside the ball or on its surface. Thus it is a “hyperideal” tetrahedron.

The last figure shows the surface of the ball model. The intersection with the tiling tetrahedron is again shown in black and the intersection with the four inverting spheres in dark blue. Note that all points get mapped into the black region. Points requiring more iterations get a lighter shade of blue. Thus the white color approximates the limit set, which is indeed an Apollonian gasket.

We see that using four spheres is the most symmetric way for getting the Apollonian gasket. Actually, this is the starting point for further research. What can we get from inverting spheres at the corners of other (regular) polyhedrons? This gives interesting variations on the Apollonian gasket. Does an additional inverting sphere at the center result in a fractal space filling structure of Poincaré ball models? We need a tiling tetrahedron with real corners to do that. This might also be seen as five four-dimensional inverting spheres at the corners of a four-dimensional simplex, which is sometimes called a “hyper-thetrahedron”. It gives a much more complicated structure than a three-dimensional “Apollonian gasket”. More results in future posts.

While writing together with Theo Schaad the paper “A Quasiperiodic Tiling With 12-Fold Rotational Symmetry and Inflation Factor 1 + Sqrt(3)”, see https://arxiv.org/abs/2102.06046, about the post “Another tiling of dodecagonal symmetry” I found an interesting variation. The Ammann-Beenker tiling of 8-fold rotational symmetry has a similar inflation factor of 1+sqrt(2), see https://tilings.math.uni-bielefeld.de/substitution/ammann-beenker/ . However, the substitution rule for the square is asymmetric and has the same tiles on the border as for the rhomb. Thus, I thought that it might be possible to do something similar for 12-fold rotational symmetry and a factor of 1+sqrt(3). But again, this has been difficult, although rhombs and squares have now the same substitutions at their borders. Note that the substitutions at the border have two different orientations. Thus it is simply not enough to find dissections of the tiles into smaller rhombs, squares and triangles. Instead I had also to take into account the substitutions of the next generation to make them fit across their boundaries as for the earlier tiling.

Trying to have as much rhombs as possible I got these substitution rules:

Here I had to use rhombs of 60 degrees acute angle at the borders and it was not possible to respect the form of the inflated tile. Further, the substitution rule for this rhomb is not mirror symmetric because of triangle tiles that have themselves different substitution rules. The deviation from mirror symmetry is rather small and thus the tiling has only a small chirality. It looks like this:

The tiling has a surprising structure at large scale. You can see it better in a more symmetric image with less colors: