     # Give up, assume no escape.
     if i == maxIterations then return NO_ESCAPE
 
-    # Using the iteration count as escape time is simple but produces bands of color.
-    if not USE_SMOOTH_ESCAPE_TIMES then return i
 
-    # We can compute a fractional escape time using the modulus in addition to i.
+    if USE_SMOOTH_ESCAPE_TIMES
+        # Compute a fractional escape time using the modulus in addition to i.
 
-    # A few more iterations reduces the normalization error.
-    extraIterations = 4
-    for j in [0...extraIterations]
-        x2 = z_x * z_x
-        y2 = z_y * z_y
-        z_y = 2 * z_x * z_y + c_y
-        z_x = x2 - y2 + c_x
+        # A few more iterations reduces the normalization error.
+        extraIterations = 4
+        for j in [0...extraIterations]
+            x2 = z_x * z_x
+            y2 = z_y * z_y
+            z_y = 2 * z_x * z_y + c_y
+            z_x = x2 - y2 + c_x
 
-    norm = z_x * z_x + z_y * z_y
-    modulus = Math.sqrt norm
-    normalized = i + extraIterations - Math.log(Math.log(modulus)) / Math.log(2)
+        norm = z_x * z_x + z_y * z_y
+        modulus = Math.sqrt norm
+        escapeTime = i + extraIterations - Math.log(Math.log(modulus)) / Math.log(2)
 
-    # Ensure the value is >= 1.
-    normalized = Math.max(1, normalized);
-
-    return normalized
+        # Ensure the value is >= 1.
+        escapeTime = Math.max(1, escapeTime)
+    else
+        # Using the iteration count as escape time is simple but produces bands of color.
+        escapeTime = i
 
 
+    incrementEscapeTime escapeTime
+    return escapeTime
 
-MAX_ITERATIONS = 100
-BLACK = "#000000"
+
+# Given the current distribution of escape times, return the smallest escape time
+# such that larger ones account for only MAX_ITERATIONS_ERROR % of the distribution.
+# I.e., we could have used this smaller value of maxIterations without
+# any significant visual difference.
+recalibrateMaxIterations = () ->
+    # Convert raw pixel counts into a percent distribution.
+    percents = {}
+    sum = 0
+    sum += count for time, count of escapeTimeDistribution
+
+    # Sum escape time %s in reverse order until they exceed err.
+    times = _.map(_.keys(escapeTimeDistribution), Math.round)
+
+    index = times.length - 1
+    totalError = 0
+    while index >= 0 and totalError / sum * 100 < MAX_ITERATIONS_ERROR
+        time = times[index]
+        if time
+            totalError += escapeTimeDistribution[time]
+        index--
+
+    console.log "Was: " + times[times.length - 1] + " ; Now: " + time
+    return time
 
 
 # Return a color to indicate the escape time at a point.
 getColor = (x, y) ->
-    value = getEscapeValue(x, y, MAX_ITERATIONS)
+    value = getEscapeValue(x, y)
     if value == 0 then return BLACK
 
     value = Math.sqrt(value - 1)

    return (c_x + 1) * (c_x + 1) + c_y * c_y < 1 / 16.0


# Iterate until we leave the ball of radius 2, up to a given max # of iterations.
# If we escape after k iterations, return an escape time value (approx. k).
# If we never escape, return 0.
getEscapeValue = (c_x, c_y, maxIterations) ->
    NO_ESCAPE = 0

    # Bypass the escape time computation if we can determine algebraically that

Mandelbrot.coffee

Render the Mandelbrot set using a CanvasTileMap.

Optionally use a simple method to compute smoothly varying escape times
to produce smooth gradients instead of discrete bands of color.
http://linas.org/art-gallery/escape/escape.html

Question: How should we increase the maxIterations limit as we zoom in?
- At a given zoom level, let's keep track of the distribution of (rounded)
  escape times.
- If we choose maxIterations too large, then we'll spend a lot of computations
  for a very small % of pixels.
- If we choose it too small, a significant % of pixels will be colored black
  that shouldn't be.
- So, let's consider the error
  Err = % of pixels on the canvas that are black but shouldn't be.
- We'd like the smallest maxIterations such that e.g. Err < 0.1%.
- So for each zoom, we'll recalibrate maxIterations to this error level,
  then multiply it by a factor determined heuristically.
###


# Start with this value of maxIterations at zoom 0.
INITIAL_MAX_ITERATIONS = 50
maxIterations = INITIAL_MAX_ITERATIONS

# Use a heuristic to increase maxIterations as we zoom in: for each zoom
# first scale maxIterations by this factor...
MAX_ITERATIONS_SCALE_FACTOR = 1.5

# ...then re-calibrate it by lowering it to the lowest value we can without
# losing colors with a % of canvas pixels greater than this % threshold.
MAX_ITERATIONS_ERROR = 0.1


BLACK = "#000000"

# Use (nearly-)continuous escape times instead of discrete integer values?
USE_SMOOTH_ESCAPE_TIMES = true


# Keep a count of each rounded escape time value (excluding 0) at a given zoom level.
escapeTimeDistribution = {}
incrementEscapeTime = (time) ->
    time = Math.round time
    escapeTimeDistribution[time] = (escapeTimeDistribution[time] || 0) + 1


# Is a complex point c inside the main cardioid?
