Fractal Coloring

When I was awed and inspired by the Mandelbrot set like everybody else and began fractal plotting in the early 1980s, my computer was an Apple III with a Pascal compiler and the coloring routine was quite simple as there were only two colors, black and white, available. Because fractal plotting was such a novel idea back then, I was genuinely excited every time I saw an output fractal finally emerged on the little computer screen after days of computation, even though the picture with a zebra pattern was usually boring by today's standard; e.g., see Figure 3.

Fast forward 30+ years and I now own two computers that were incomparably cheaper to purchase than the Apple III. Each of them is equipped with a fast "Pentium" processor and several gigabytes of RAM and churns out what I used to get after days of runtime within minutes or hours instead. The modern computers are also capable of showing a whopping 224 = 16,777,216 so-called "24-bit" colors instead of only 2 1 or two "1-bit" colors and coloring was no longer simple.



Figure 1.  Fractals in 24-bit Colors




Shown below is the basic coloring method I have been using for about fifteen years to plot colorful fractals like the ones shown above. Since it is intended as a supplement to
Stories about Fractal Plotting in Sekino's Fractal Gallery, I use such ideas as a Julia fractal and the divergence scheme freely without going into much detail. Also, a "fractal" is loosely defined without using technical terms such as the "Hausdorff-Besicovitch dimension" as an object that is "self-similar," i.e., a large part of it contains smaller parts that resemble the large part in some way

According to the American Academy of Ophthalmology, good human eyes can distinguish "only" up to 10 million colors out of the 16 million+ colors, so I expect that the 24-bit colors would stay with us for a long time without a substantial change regardless of the progress in computer technologies. ..... J. Sekno, 2015.


§ 1   Canvases and Iteration Arrays

Because a
canvas and an iteration array, which is short for an array of the "least iteration indices," over a canvas play central roles in fractal coloring, let's briefly review what they are using an example that involves several essential ideas in fractal plotting.

Example 1. Suppose we wish to plot a
Julia fractal of the dynamical system

(1.1)   zn+1 = fp(zn) = p(1 - zn)(1 + zn) zn  with p = (1.18, 0.376)

on a canvas R comprising 730 × 400 pixels. Thus, if we write imax = 730 and jmax = 400, then the canvas R is a rectangular region in the i j-plane decomposed into imax × jmax pixels with pixel coordinates (i, j) in such a way that the upper left and lower right pixels are (0, 0) and (imax - 1, jmax - 1) = (729, 399), respectively; see Figure 2 that depicts the canvas (except that the pixels are oversized for emphasis).


Figure 2.  Canvas   Figure 3.  The Julia Fractal in 1-bit Colors

To plot a Julia fractal on the canvas R, we superimpose R over a rectangular region in the complex plane (xy-plane) bounded by real numbers xmin = -1.4, xmax = 1.4, ymin = -0.77 and ymax = 0.77 as in Figure 2, and choose, for each pixel (i, j) in R, a unique complex number z = (x, y) that belongs to the pixel by the
conversion formula. The complex number thus chosen is called the complex number representing the pixel (i, j).

To compute an iteration array, which will be colored later and converted to a visual fractal, let M = 500, θ = 2, k = 2 and ε = 10-6.

Step 1. Iterate the dynamical system (1.1) for every pixel (i, j) in R using the complex number representing it as the initial value z0 at most M times and compute a nonnegative integer

(1.2)  d(i, j) = the least (or first) iteration index m < M such that zm satisfies |zm| > θ,

if such m exists; and
d(i, j) = M, otherwise. Here, M is the maximum number of iterations, which is intended for thwarting the computer to get trapped in an infinite loop.

Step 2. Let A be the subarea of the canvas R comprising the pixels (i, j) with d(i, j) = M in Step 1. Iterate the dynamical system (1.1) for every pixel (i, j) in A using the complex number representing it as the initial value z0 at most M times and compute

(1.3)  d(i, j) = the least (or first) iteration index m < M such that zm satisfies |zm+k - zm| < ε,

if such m exists; and d(i, j) = M, otherwise. The boundary of A is an approximation of the so-called Julia set depicted on the canvas R, which is the source of "chaos" in the dynamical system (1.1).

(1.2) and (1.3) are respectively called the divergence scheme with the threshold value θ and the convergence scheme with the period index k for computing the array of the least iteration indices d(i, j) over the canvas R, or an iteration array for short. The words "divergence" and "convergence" come from the fact that (1.2) and (1.3) are related to the divergence and convergence of the orbit of z0; see § 2 and § 4 of
Stories about Fractal Plotting.

If we color each pixel (i, j) black or white depending upon the parity (evenness or oddness) of d(i, j), we get the fractal of Figure 3 called "(Camouflaged) Seahorse Couple." We'll color it later using the 24-bit colors (and uncover them from the camouflage) to get fractals like the ones shown in Figure 7 and Figure 8.


§ 2   The Color Cube
Mathematically speaking, the 24-bit colors are packed in a cube, which we call the color cube. As shown in the figure on the left, the color cube is given by the RGB coordinate system with the R-, G- and B-axes representing all shades of the three primary colors of red, green and blue, respectively. The R-axis, for example, consists of 28 = 256 shades of red, namely,

black = (0, 0, 0),   (1, 0, 0),   (2, 0, 0),   (3, 0, 0),   · · ·,   (255, 0, 0) = red,

listed from dark to light that appear to change continuously to human eyes; likewise for the G- and B-axes with green = (0, 255, 0) and blue = (0, 0, 255).

This is because the color cube is structured like
Rubik's cube comprising 256 × 256 × 256 small subcubes (instead of 3 × 3 × 3 subcubes) and each color is a small subcube of the color cube rather than a point in the 3D space. It is similar to a canvas comprising pixels that are rectangles instead of points. Thus, the color cube is a solid cube rather than a discrete set of points with integer components.

Even though colors are not points, there are some analogies between the color cube and the Cartesian 3D space with x-, y-, z-axes. For example, each color in the color cube is uniquely represented as an ordered triple (R, G, B) of integers between 0 and 255, called bytes and it makes the total number of colors in the color cube 256 3 = 224 = 16,777,216 as we have seen. Also, each color can be written uniquely as the sum of the aforementioned shades of the primary colors. For example,

    yellow = (255, 255, 0) = (255, 0, 0) + (0, 255, 0) = red + green,
    purple = (255, 0, 255) = (255, 0, 0) + (0, 0, 255) = red + blue,
    white = (255, 255, 255) = (255, 255, 0) + (0, 0, 255) = yellow + blue = red + green + blue.

Even though a color is not a point, we'll still treat it like a "point" when we talk about it. For example, black = (0, 0, 0) is the "origin" of the color coordinate system and white = (255, 255, 255) is the "vertex" of the color cube opposite from the origin (black). In general, a color is darker if it is closer to black and lighter if it is closer to white. In fact, we can darken a color by a (componentwise) scalar multiplication (followed by rounding the components to the nearest bytes) of the color by a scalar strictly between 0 and 1. For example, we have:

   0.7 × orange = 0.7 × (255, 128, 0) = (179, 90, 0) = a brownish color;
   0.7 × white = 0.7 × (255, 255, 255) = (179, 179, 179) = a grayish color.


Line Segment in the Color Cube:  Suppose C1 = (R1, G1, B1) and C2 = (R2, G2, B2) are two colors in the color cube. Then by the (directed) line segment from C1 to C2, we mean the set of all colors C = (R, G, B) satisfying the parametric equations

(2.1)   R = (1 - t) × R1 + t × R2,   G = (1 - t) × G1 + t × G2,   B = (1 - t) × B1 + t × B2,

where the parameter t satisfies 0 < t < 1 and each multiplication is followed by rounding the product to the nearest byte. Note that if  t = 0,  C = C1,  and if  t = 1,  C = C2,  showing that C moves along the line segment by gradually changing its color from C1 to C2  as "time" t progresses from 0 to 1. Here is an example.



Figure 4.  Line Segment from Red to Purple




§ 3   Coloring Iteration Arrays by 24-bit Colors

Compared to computing an iteration array, coloring it to get a visual image, possibly a fractal, usually takes a miniscule part in terms of the computer's runtime, but it is reminiscent of painting a newly built house and can improve or ruin the entire effort. We can also make different images from the same iteration array by using different colorings. For example, Figure 5 shows that "Flock of Owls" and "Racing Elephants" come from the same iteration array. Note: We don't distinguish two images if one is given by a rotation or a vertical or horizontal flipping of the other.



Figure 5.  "Flock of Owls (Middle)" and "Racing Elephants (Right)"




Suppose m = d(i, j) is an iteration array generated by a dynamical system on a canvas R comprising pixels (i, j). Our coloring scheme has two parts: We first decompose the canvas R into a finite number of subareas and then paint each subarea by a single color like black or by a combination of gradual color changes such as from red to purple and from purple to blue as shown in the image of
Figure 1 on the left. The canvas for the Julia fractal of Example 1 has a natural decomposition into two subareas, A and its complement separated by the Julia set; see Step 2. The decomposition may have just one subarea namely the canvas itself as is the case in "Racing Elephants" of Figure 5.

Suppose A is a subarea of R and C1 and C2 are two distinct colors connected by the line segment
(2.1). Our object is to color A using a continuous color change from C1 to C2, and we will accomplish it by finding an appropriate "coloring function" from the iteration array m = d(i, j) over A to the line segment joining C1 and C2. The color C assigned to d(i, j) by the function is the color we use to paint the pixel (i, j) in A.

Let m1 and m2 be the minimum and maximum values of the integers m = d(i, j) over A, respectively, and normalize m to the range between 0 and 1 by setting

(3.1)   s = (m - m1)/(m2 - m1).

Then set

(3.2)   t = s

in
(2.1). Observe that if m = m1 then t = s = 0, so, C = C1,  and similarly, if  m = m2, then  C = C2,  showing that as m = d(i, j) increases from m1 to m2, the corresponding color C changes from C1 to C2 along the line segment (2.1). Thus, (3.2) together with (3.1) gives us a linear coloring function from the iteration array m = d(i, j) over A into the line segment joining C1 and C2; see Figure 6 shown below.

More generally, if f is any continuous function with the interval [0, 1] as its domain and range, then

(3.3)   t = f(s)

together with (3.1) provides us with a continuous coloring function from m on A into the line segment joining the two colors C1 and C2. Figure 6 shows six such functions including (3.2): The diagram on the left comprises the graphs of  f(s) = 1 - (s - 1)2,   f(s) = s  and  f(s) = s2 and the functions in the diagram on the right are given by replacing s in the functions on the left diagram by  1 - s  to reverse the coloring direction.



Figure 6.  Linear and Quadratic Coloring Functions with Red and Dark Brown


Note: The m- and s-axes may have different scales.


For example, if C1 = dark brown and C2 = red as shown in Figure 6, then the linear coloring function t = s paints the complement of the subarea A in
Example 1 like the background of "Seahorse Couple" in Figure 7. If the quadratic coloring functionf(s) = 1 - (s - 1)2 is used instead, the background gets more reddish, which is indicated by the purple lines in the left diagram, and if the quadratic coloring functionf(s) = s2 is used, then red is less emphasized.

Note that f in (3.3) does not have to be a monotonically increasing or decreasing function, and it is in fact a fun project for the reader to program a computer to extend the basic functions in Figure 6 and generate periodic coloring functions such as the ones shown in Figure 7. The idea of functions and graphs belonging to precalculus works well here. Applying the periodic coloring functions with an appropriate frequency of ups and downs on the subarea J in Example 1, we get "Seahorse Couple" of Figure 7. We can find an appropriate frequency by rather instructive trial and error computer experiments.



Figure 7.  Periodic Coloring Functions with Yellow and Brown


In fact, the idea of the periodic coloring functions can be modified to involve more than two colors, Figure 8 shows two such examples.



Figure 8.  Periodic Coloring Functions with Blue, Yellow, Orange and Red


For more examples, see
Sekino's Fractal Gallery.