Every color in a Mandelbrot picture is an answer to one question: how many steps did this point take to run away? This chapter shows what that count is, why it makes stripes, and the small piece of logarithm algebra that turns the stripes into silk.
Common mix-up: the colors are not part of the Mandelbrot set. The set is only the black region. Every color outside it is a choice the programmer made about how to show one number per pixel: how quickly that point escaped.
Take a pixel. It sits at some point in the complex plane, and that point is a number, c. Start with z = 0 and apply the rule z → z² + c over and over: z₁ = c, z₂ = c² + c, and so on (Chapter 8 walks through the rule itself). The sequence of z values is the point's orbit. Either it stays near zero forever, and c belongs to the Mandelbrot set, or it eventually grows without limit, and c does not.
A computer cannot wait forever, so it asks a cheaper question. It counts the steps until |z|, the distance from zero, passes a fixed radius. That count is the escape time, written n. If the orbit is still inside the radius after some maximum number of steps, the program gives up and calls the point "inside". This is the escape-time algorithm, and nearly every Mandelbrot picture you have ever seen was drawn with it.
So a fractal image is really a chart of one number per pixel. Points far from the set escape in one or two steps. Points close to its edge can circle for hundreds or thousands of steps before they break away. The color is a look-up: count in, color out. Change the look-up and the same numbers give a completely different picture, which is the subject of the next chapter.
The radius is called the bailout radius, R. The smallest safe choice is 2, for a reason worth seeing once: it is the point of no return.
Suppose at some step |z| > 2 and |z| ≥ |c|. The next value is z² + c, and the triangle inequality says its size is at least |z|² − |c|. Since |c| ≤ |z|, that is at least |z|² − |z| = |z|(|z| − 1). Because |z| > 2, the factor |z| − 1 is bigger than 1, so the next |z| is strictly larger than this one, and by a factor that itself keeps growing. The orbit can never come back.
The condition |z| ≥ |c| is always met here. Every point of the Mandelbrot set has |c| ≤ 2, so for points near the set, passing 2 means passing |c| too. And if |c| > 2, the very first step gives z₁ = c, which is already outside the circle; that point escapes at step 1. Either way, the first time |z| goes past 2, the answer is settled.
That makes 2 the smallest bailout that never gives a wrong answer. Any bigger radius is also correct; it just takes a few more steps to reach. Later in this chapter, that "few more steps" turns out to be a bargain.
| Stored per pixel | What it is | Used for |
|---|---|---|
| n | The escape step, a whole number | Classic banded coloring |
| |zₙ| | How far past the radius the orbit landed on that step | The fractional part that smooths the bands |
| inside? | Whether the orbit was still bounded at the iteration limit | Painting the set itself, usually black |
| orbit statistics | Smallest |z| reached, final |z|, average angle and so on | Interior coloring and orbit traps (Chapter 17) |
Color each pixel by its count and the picture splits into rings. All the points that escape in exactly 5 steps form one ring, all the points that take 6 form the next one in, and so on toward the set. Each ring gets one color. Where two rings meet, the color jumps.
That is not a bug in the program. It is what whole numbers do. The count can only be 5 or 6, never 5.4, so neighboring pixels whose orbits were nearly identical, one escaping just before step 6 and one just after, are forced into different classes. The boundary between rings is a level set: a contour line, exactly like the lines on a hiking map, except that the map has only whole-number heights.
Far from the set the rings are wide, smooth curves. Near the set they crowd together, because a tiny move in c can change the orbit's fate by many steps. In the seahorse valleys and spirals the rings pile up so tightly that, with a palette that changes quickly, they shimmer into noise.
Banding is sometimes exactly what you want: the rings are honest, and the early printed pictures of the set used them to good effect. But they put fake edges into a picture whose real structure is smooth everywhere outside the set. The next few sections show how to recover that smoothness from information the program already had and threw away.
Imagine the Mandelbrot set as a charged metal object. The electric potential around it is a smooth function, G(c), that is zero on the set and grows outward. Adrien Douady and John Hubbard used this potential, through a map named after Lucjan Böttcher, in their 1982 proof that the set is connected. Its level curves, the equipotentials, are smooth closed loops that wrap the set ever more tightly as G shrinks.
The band edges of an escape-time picture are rough copies of those loops. A band edge is where |zₙ| = R exactly for some n, and for large R that curve is very nearly the equipotential G = (ln R)/2ⁿ. As R grows, the match improves. With R = 2, the edges are visibly kinked near the set; with R = 1,000,000 they are almost exactly the equipotential lines. That fact is the whole reason the smoothing formula below works.
If 2 is enough to decide whether a point escapes, why would anyone use more? Because the program is not just deciding; it is measuring. The escape step n is a coarse measurement. How far past the radius the orbit landed is a fine one, and it is only trustworthy when the orbit is already far out, where the "+ c" in z² + c hardly matters.
Raising R costs surprisingly little. Out past the circle, each step roughly squares |z|. Squaring is fast: 2 becomes 4, then 16, then 256, then 65,536. To go from a radius of 2 to a radius of a million takes only about four more steps, whatever the point. Those four steps are added to every escaping pixel equally, so the picture's structure doesn't change; only the measurement gets better.
The same squaring sets a hard ceiling. After about ten squarings beyond 2, |z| is around 2¹⁰²⁴, which is past the largest number a 64-bit floating-point value can hold. Programs usually compare |z|² against R² to avoid a square root, so with R = 10⁶ they compare against 10¹², which is still nowhere near trouble. A bailout of 10¹⁰⁰ would also work; there is simply no visible gain beyond about 256.
| Bailout R | Test (|z|² > R²) | Extra steps vs R = 2 | What smooth coloring looks like |
|---|---|---|---|
| 2 | |z|² > 4 | 0 | Correct escape, but faint seams remain at every band edge |
| 4 | |z|² > 16 | 1 | Seams much weaker; visible in flat gradients |
| 256 | |z|² > 65,536 | 3 | Seams far below one palette step; the usual choice |
| 10⁶ | |z|² > 10¹² | about 4.3 | Indistinguishable from exact |
Both pictures come from one calculation. The left one is colored by the whole-number count n. The right one adds a fraction of the smooth correction; slide it from 0 to 1 and watch the bands melt. Change the bailout and the whole set is recomputed, so you can see the seams that R = 2 leaves behind. Click either picture to zoom in on that spot. Changing the palette, the cycle length or the slider only recolors; it never recomputes an orbit.
Try this: choose the grey ramp and R = 2, set the cycle length short, and look closely at the right-hand picture. Faint ridges sit exactly where the left picture's bands meet. Switch to R = 256 and they vanish. "Stretched" mapping uses (ν ⁄ limit)^γ instead of cycling, so raising the iteration limit changes the colors: one reason most renderers prefer cycling.
The fix for banding is to add a fraction to n that says how far through its band each pixel is. The program already has what it needs: on the step where the orbit escaped, it knows |zₙ|, how far past the radius it landed. A pixel that only just escaped on step 6 lands barely past R. A pixel that very nearly escaped on step 5 lands far past it, at almost R². That spread, from R to R², is the band's width, measured from the inside. Here is how to turn it into a number between 0 and 1, one step at a time.
Once |z| is large, z² is enormous compared with c. If |z| is 1,000, z² has size 1,000,000, and adding a c of size 2 or less changes it by a fraction of a millionth. So, to excellent accuracy, each step simply squares the size.
A logarithm turns powers into multiples: ln(x²) = 2 ln x. So the natural log of the size doubles every step. It is still growing very fast, but now it grows in a simple, predictable way.
Doubling is itself a power: something that doubles every step is 2 to the power of the step number. Take a base-2 logarithm of it and you get the step number back. So log₂ of ln|z| goes up by exactly 1 each step, like a clock ticking once per iteration. That is the whole reason for the "log of a log": the first log undoes the squaring, the second undoes the doubling, and what is left is linear in the step count.
Now compare two ways of describing the same orbit. Stop at step n and you have the pair (n, log₂ ln|zₙ|). Stop one step later and you have (n + 1, log₂ ln|zₙ| + 1). Both numbers went up by one. Their difference did not change. So n − log₂(ln|zₙ|) is the same no matter which step you happened to stop on. It depends on c alone, and it changes smoothly as c moves.
That is exactly what fixes the bands. At a band edge, one pixel stops on step 6 and its neighbor on step 5, but both report the same value of n − log₂(ln|zₙ|), so there is no jump. Adding 1 keeps the result close to n and positive, and gives the standard formula.
The correction 1 − log₂(ln|zₙ|) is the "fractional part" the instruments in this chapter show. With R = 2 it lies between about 0.16 and 1.53. With a bigger radius it turns negative, because n itself is bigger: every extra step the larger radius costs adds one to n and one to the clock, and the two cancel. That is a pleasant property. Apart from the small errors of step 1, ν comes out the same whatever bailout you choose, so a palette tuned at one radius still fits at another.
A version that stays between n − 1 and n. Dividing by the bailout before the second log gives μ = n − log₂(ln|zₙ| ⁄ ln R). Because R < |zₙ| ≤ about R² (a little more, since c is added), the ratio ln|zₙ| ⁄ ln R lies between 1 and 2, so its log₂ lies between 0 and 1, and μ lies between n − 1 and n. ν − μ is the constant 1 − log₂(ln R), the same for every pixel, so either formula gives the same picture with the palette shifted. The trade: μ is tidy within each band, while ν stays put when you change R.
Other powers. For z^d + c, each step raises the size to the power d instead of 2, so the second logarithm has base d: ν = n + 1 − log_d(ln|zₙ|). The Multibrot pictures in Chapter 10 use this. For Burning Ship and Tricorn, which also square the size, the base stays 2.
Where it really comes from. The potential from the bands section can be computed as G(c) = lim 2⁻ⁿ ln|zₙ|. Take log₂ of the approximation G ≈ 2⁻ⁿ ln|zₙ| and you get log₂ G ≈ log₂(ln|zₙ|) − n. Rearranged, ν ≈ 1 − log₂ G(c). The smooth count is just the negative logarithm of the potential: its level curves are the equipotentials, and that is why the colors follow smooth loops around the set.
Why not just interpolate the count? You could blend each pixel's count with its neighbors', but that blurs real detail and depends on the image resolution. The log-log formula uses only that pixel's own orbit, so it is exact to the accuracy of step 1, at any zoom.
Click anywhere outside the black region. The program follows that point's orbit, draws it on the picture, and shows each step's size. The top chart is |z| on a log scale: watch it hover, then climb past the dashed bailout line. The bottom chart is log₂(ln|z|), the clock from step 3; once the orbit is far out, its dots line up on a staircase rising exactly one per step. The readouts then walk through the formula to the exact palette entry and color of that one pixel. The pale bars after escape are steps the renderer never needs; they are computed here only to show the clock keeps ticking.
Palette position = (ν ⁄ 20) mod 1, times 256 entries. The picture uses that same rule for every pixel, so the swatch is exactly the color under the crosshair.
| Step k | zₖ | |zₖ| | ln|zₖ| | log₂(ln|zₖ|) |
|---|
The derivation rested on one approximation: that far out, a step squares the size and the "+ c" doesn't matter. How far out is far enough? The error that c introduces at the escape step is at most about |c| ⁄ |zₙ|² as a fraction of the size. With R = 2, |zₙ| can be as small as 2 and |c| as large as 2, so the error can be a large fraction, and the clock in step 3 ticks unevenly on the first beat. That shows up as faint creases in the smooth picture, exactly where the old band edges were.
With R = 256 the same error is at most about 2 ⁄ 65,536, around three thousandths of a percent, and the creases fall far below anything a palette can show. That is why 256 is a common default, and why the seam readouts in the slice below collapse as you raise R. Four extra iterations per pixel to make a picture seamless is a trade any renderer will take.
There is a second, quieter benefit. Some coloring methods that come later, such as the distance estimate in Chapter 17, also rely on the orbit being far out when it stops. A large bailout makes all of them more accurate at once.
This samples one straight line of points, one per screen pixel, and plots both counts along it. The grey staircase is n; the colored curve is ν. Every step of the staircase is a band edge in the pictures above. The program also computes every point a second time with a huge bailout, 10¹⁰, as an "exact" reference, and measures how far this bailout's ν is from it. Inside a band that error changes smoothly; where n jumps, any sudden change in the error is a seam you could see. A perfect formula would show none.
On the real axis right of the cusp at c = 0.25, the counts climb like π ⁄ √(c − 0.25) as you approach the set: that is the narrow neck where orbits linger longest. Compare the worst seam at R = 2, about a tenth of a count, with R = 256, around a millionth.
Points inside the set never escape, so they have no escape time. The traditional answer is to paint them black, and it is a good one: the set reads as a solid silhouette and all the attention goes to the edge. But the orbits inside are not idle. Most of them settle down onto a repeating cycle, and the program has watched them do it for the whole iteration limit. That gives several honest things to show.
There is also a practical reason to care about the inside: it is where all the time goes. An outside pixel stops when it escapes; an inside pixel runs the full iteration limit. Renderers save most of that by skipping two regions that can be tested with a formula, the main cardioid and the period-2 disk, and by periodicity checking: remember z every so often, and if the orbit lands on the remembered value again, it is in a cycle and will never escape. The instruments on this page do both.
The main cardioid is the set of c for which z² + c has an attracting fixed point. Writing q = (x − ¼)² + y² for c = x + iy, the point is inside the cardioid when q(q + x − ¼) ≤ ¼y². The period-2 disk is simply the disk of radius ¼ around −1: (x + 1)² + y² ≤ 1⁄16. Together these two regions cover most of the set's area in a whole-set view, so testing them first can make the first picture several times faster.
Neither test helps with the smaller bulbs, which is where periodicity checking takes over. The checking must use a tolerance a little smaller than the pixel size: too tight and nearly-periodic orbits are never caught; too loose and a slowly escaping point near the edge is wrongly declared inside.
A smooth count ν is a real number, typically somewhere from 1 to a few thousand. A palette is a strip of colors, usually stored as a table of 256 or 1,024 entries. The last step is to map one onto the other, and there are only a few sensible ways to do it.
Cycling. Divide ν by a cycle length P, keep the fractional part, and use it as a position along the palette: index = (ν ⁄ P + offset) mod 1. The palette repeats every P counts. Because it never runs off the end, it works at any zoom and for any iteration limit, and it is what most renderers do. A short P shows fine structure but turns to noise near the set, where ν changes by many counts from one pixel to the next. A long P is calm but can look flat. The offset slides every color along the palette at once; animate it and you have palette cycling, which the next chapter takes apart.
Stretching. Divide ν by the iteration limit to get a number from 0 to 1 and use the palette once. It sounds tidy, but almost every pixel has a small count compared with the limit, so the whole picture lands in the first sliver of the palette. Raising the limit to see more detail then shifts every color. Applying an exponent γ less than 1, (ν ⁄ limit)^γ, spreads out the small values; a logarithm does the same more strongly. The fully adaptive version of this idea is histogram coloring, also in the next chapter.
The same word, gamma, means something else in the next step, and both matter. Screens do not emit light in proportion to the number stored in a pixel. The sRGB standard that nearly every screen follows uses a curve that is close to a power of 2.2, so a pixel value of 128, half of 255, produces only about 22 percent of full brightness. This is display gamma. It matters whenever colors are mixed: blending two palette entries, or averaging several samples inside one pixel for antialiasing, should be done on the linear light values, not on the stored numbers.
The edge of the set is infinitely detailed, so a single sample per pixel aliases: thin filaments flicker in and out as you move. The fix is to take several samples inside each pixel and average. The order matters. Averaging the counts first and then looking up one color produces a color that none of the samples had, often from a different part of the palette entirely. Look up each sample's color first, convert to linear light, average, and convert back.
A small check of why linear light matters: a fine checkerboard of black and white pixels, seen from across the room, looks like a grey whose stored sRGB value is about 188, not 128. Average in stored values and antialiased edges come out too dark. JellyDazzle's fractal patterns take their colors from the app's live palette, and the next chapter shows how that palette is blended.
| Mapping | Rule | Good at | Watch out for |
|---|---|---|---|
| Cyclic | frac(ν ⁄ P + offset) | Any zoom, any limit; animates | Noise near the set when P is short |
| Linear stretch | ν ⁄ limit | Simple to explain | Uses a sliver of the palette; colors move when the limit changes |
| Power stretch | (ν ⁄ limit)^γ | Spreads the common small counts | Still tied to the limit |
| Log stretch | ln(1 + ν) ⁄ ln(1 + limit) | Deep zooms with a huge range of counts | Compresses the outer bands |
| Histogram | Fraction of pixels with a smaller count | Uses the whole palette, always | Colors change as the view changes (Chapter 16) |