Task 4 of 5
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.)
count + 1 − Math.log2(0.5 * Math.log2(zr² + zi²)).count < 100100 exactly — no correction for points that never escapedThe 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.
if (count < 100) {
return count + 1
- Math.log2(0.5 * Math.log2(zr * zr + zi * zi));
}
return 100;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.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.