 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.
 ###
 
 
-# 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
+# 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?

        xMax:  2.0
        yMin: -2.0
        yMax:  2.0
    # Reset the iteration counts on zoom.
    beforeZoom: () ->
map.zoomIn()
map.zoomIn()

    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 the current # of max 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) ->
    NO_ESCAPE = 0

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