Task 4 of 5

Smooth Out the Bands

Look closely at task 3's exterior and you'll see hard rings: iteration counts are integers, so neighboring pixels jump from shade 6 straight to shade 7. But the kernel knows more than the count — it knows how far past the escape radius z flew on its final step. A barely-escaped z and one that rocketed to |z| = 50 both count the same pass; that overshoot is the missing fraction.

The classic fix is the normalized iteration count: count + 1 − log2(log2|z|). When z barely clears the radius the correction is near 1, when it overshoots hugely it's near 0 — and the bands blend into a continuous ramp. (With |z|² = zr² + zi² in hand, use log2|z| = 0.5 · log2(zr² + zi²) and skip the square root.)

Goal: return a fractional escape value — interior points return exactly 100, escaped points return count + 1 − Math.log2(0.5 * Math.log2(zr² + zi²)).

Requirements

Hint 1 — why z is still usable after the loop

The guard freezes z the moment it escapes, so after the loop zr, zi hold the first value with |z|² ≥ 4 — exactly the overshoot the formula needs. Math.log2 works inside kernels on both backends.

Hint 2 — the ending
if (count < 100) {
  return count + 1
    - Math.log2(0.5 * Math.log2(zr * zr + zi * zi));
}
return 100;

Same idea elsewhere

Fighting quantization with a fractional correction is a graphics evergreen: trilinear blending between mipmap levels, smoothstep edges, ordered dithering. The same normalized-iteration formula runs unchanged in a CUDA kernel or a WGSL fragment shader — it's pure float math.

All tasks in Escape-Time Fractals

  1. Map Pixels to the Complex Plane
  2. The Escape-Time Loop
  3. Paint by Iteration Count
  4. Smooth Out the Bands
  5. Julia Sets: Turn the Dial

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.