# The Escape-Time Loop

*Task 2 of 5 · [Escape-Time Fractals](https://gpu.rocks/learn/escape-time-fractals-0de4764c.md) · GPU.js Learn*

The Mandelbrot set asks one question at every point `c`: start
`z = 0` and repeat `z → z² + c` — does `z` stay near the
origin forever, or fly off to infinity? Points that stay bounded are *in* the set;
for the rest, the interesting number is **how many iterations** they survived.

Two facts make this computable. Once `|z| > 2`, escape is guaranteed — so
we can stop watching. And we cap the loop at 100 passes: anything still bounded by then we
declare "inside". With `z = zr + zi·i`, one step is
`zr² − zi² + cr` for the new real part and `2·zr·zi + ci` for the new
imaginary part.

## Figures

- **iterate z² + c and watch: settle in, or fly off — the count is the answer**

## Goal

**Goal:** iterate `z → z² + c` up to 100 times, but only
while `zr² + zi² < 4`, and return how many iterations actually ran.

## Requirements

- Start at `zr = 0, zi = 0, count = 0` (already wired up)
- Loop a fixed 100 times, guarding each pass with `zr² + zi² < 4`
- Inside the guard: update z via a temporary — `zr` is read by both formulas
- Return `count`: 100 means "never escaped", small means "escaped fast"

## Hint 1 — the shape of the loop

gpu.js's WebGL backend needs a fixed loop bound, so instead of breaking out
we guard the body:

```js
for (let i = 0; i < 100; i++) {
  if (zr * zr + zi * zi < 4) {
    // …step and count…
  }
}
```

After escape the guard fails on every remaining pass, so z freezes and count stops.

## Hint 2 — don't clobber zr

Both formulas read the *old* `zr`, so stash the new real part
first:

```js
const zrNext = zr * zr - zi * zi + cr;
zi = 2 * zr * zi + ci;
zr = zrNext;
count = count + 1;
```

## Same idea elsewhere

Data-dependent loops like this are where *divergence* lives: in CUDA and
ROCm, threads of a warp that escape early still march in lockstep with their slowest
neighbor, so a tile renders at the speed of its deepest pixel. WGSL and Metal shading
language allow exactly this kind of bounded loop in fragment and compute stages.

## Starter code

```js
// z → z² + c, over and over. Count how long z stays near the origin.
const gpu = new GPU({ mode });

const mandelbrot = gpu.createKernel(function (xMin, yMin, step) {
  const x = this.thread.x;
  const y = this.thread.y;
  const cr = xMin + x * step;
  const ci = yMin + y * step;
  let zr = 0;
  let zi = 0;
  let count = 0;
  // TODO: loop 100 times; on each pass, ONLY while zr² + zi² < 4:
  //   new real part:      zr² - zi² + cr   (stash it in a temporary!)
  //   new imaginary part: 2 * zr * zi + ci
  //   and add 1 to count.
  return count;
}, { output: [64, 64] });

const counts = await mandelbrot(-2.2, -1.6, 3.2 / 64);
console.log('c = 0, deep inside the set:', counts[32][44]);
console.log('far corner, escapes at once:', counts[0][0]);
```

---

Interactive version: https://gpu.rocks/learn/escape-time-fractals-0de4764c/2

[Previous task](https://gpu.rocks/learn/escape-time-fractals-0de4764c/1.md) · [Next task](https://gpu.rocks/learn/escape-time-fractals-0de4764c/3.md)
