Version 3. Mandelbrot Metal 3 is available for iPhone, iPhone Duo, and iPad. Explore free with the Infinity Engine and all 156 built-in palettes; choose Pro for expanded creative tools and output. Requires iOS 26 or iPadOS 26 or later.
From a gesture to a completed fractal
Mandelbrot Metal turns a precise viewport into millions of orbit calculations, then into color, optional relief, and a complete image. This walkthrough explains where the CPU and Metal GPU work together—and what changes when you zoom deeply.
1. Preserve the view before rendering it
The viewport contains a complex center, scale, drawable dimensions, fractal family, and creative settings. Pan and pinch update this state. The renderer maps each drawable pixel center to a point in the complex plane. UIKit gestures use points; Metal renders pixels, so the screen scale must be included in the conversion.
At a navigation scale of 1018 points per complex-plane unit on a 3× drawable, the spacing between pixel centers is about 3.33 × 10−19. That tiny number can be stored separately in an ordinary floating-point value, yet adding it to a center near magnitude one can round it away. This is why exponent range and coordinate accuracy are different problems.
Compensated centers retain low-order information. Canonical fixed-point strings preserve deep coordinates in state and bookmark transport. The public Version 3 navigation policy still stops at 1025 points per complex-plane unit; a large internal coordinate container does not by itself qualify a larger rendering range.
The display mapping increases the imaginary coordinate as device y increases downward. Gestures, Julia selection, Coordinate Jump, history, and export must use that same convention to preserve the composition.
2. Choose the arithmetic for the request
| Path | Work performed | Boundary |
|---|---|---|
| Ordinary Metal | Direct orbit evaluation using Float or the established compensated GPU paths. | Finite significant precision limits distinct coordinates. |
| Infinity Engine | CPU double-double reference; Metal Float perturbation with quad-single restart and recovery. | Deep Mandelbrot requests must pass reference, pixel-step, and runtime eligibility. |
| CPU deep fallback | Established double-double orbit evaluation for the requested composition. | Used when the deep Metal path is ineligible or fails completion checks. |
Double-double represents a value with two Doubles; quad-single uses four Float limbs. They are software expansions, not an assertion that the GPU supplies native 64-bit or IEEE binary128 arithmetic for this workload. Some terminal escape and recovery diagnostics reduce expanded values to Float.
One precise reference, many small differences
For deep Mandelbrot views, the CPU computes a reference orbit near the center and uploads each real and imaginary component as four Float limbs. The GPU evolves each nearby pixel’s difference from that orbit:
δzn+1 = 2Znδzn + δzn2 + δc
The full quadratic term remains present. Well-conditioned pixels use the inexpensive Float tier. Growth, cancellation, ambiguous escape, or non-finite values can restart a pixel in quad-single. Rebasing changes the pixel’s relationship to the existing reference; it does not imply rebuilding a CPU orbit for every pixel. Direct quad-single recovery handles an exhausted or invalid reference.
Infinity currently applies to Mandelbrot mode. Julia views use the established rendering policies, and the current Metal Julia parameter has its own Float precision limit. Read the perturbation explanation for the derivation and recovery details.
3. Evaluate escape and build color together
The quadratic recurrence is zn+1 = zn2 + c. Mandelbrot mode starts each orbit at zero and varies c; Julia mode holds c fixed and varies the starting point. The kernel compares squared magnitude with 4, avoiding a square root in the ordinary radius-2 escape test.
Analytic shortcuts can recognize the main cardioid and period-2 bulb in Mandelbrot mode. Otherwise, a black sample means no escape was detected within the finite iteration budget and selected arithmetic. It is not a general proof of membership. Julia mode does not apply the Mandelbrot interior shortcuts.
For an escaped sample, a logarithmic correction produces a fractional escape coordinate. Normalization and the contrast exponent map that value into the palette. Color can come from a procedural branch or a one-row lookup texture. Named and imported gradients are rasterized at the selected width of 256 or 1,024 texels; Metal samples them with linear filtering and clamp-to-edge addressing.
Orbit evaluation and palette coloring share the primary compute pass. There is no persistent escape buffer for instant palette-only recoloring: a palette edit requests a fresh render. See the palette system for the differences between source stops, texture resolution, Exact LUT, and color space.
Supersampling is additional orbit work
Supported ordinary-renderer grids use 1, 4, 9, 16, or 25 subpixel samples. Each sample evaluates an orbit and palette lookup before the pixel’s color average is written. Hysteresis and interaction policy prevent rapid quality switching. The public Infinity path currently requests one sample per pixel; multi-sample Infinity is separate qualification work.
The LUT and output use BGRA8Unorm. Averaging these stored components does not establish scene-linear light or HDR rendering. A larger LUT can retain more variation along a gradient, but it does not add bits to each channel or increase fractal-coordinate precision.
4. Shade a completed height field for 3D Look
With 3D Look enabled, the primary pass also writes an R32Float height field derived from escape information. A one-pixel border surrounds each color region so every pixel has valid neighbors. At one sample per pixel, height normally reuses the color orbit; sample grids without a center sample need a separate center-height evaluation.
A second Metal pass reads the completed left, right, upper, and lower heights. Central differences estimate a surface normal, then ambient, diffuse, and specular terms shade the color. These neighbor operations read a texture; they do not repeat four full neighboring orbits for every output pixel.
A small 3 × 3 CPU probe pattern selects a shared relief iteration budget. The historical 256-step budget is retained where appropriate; when sampled escape information appears later, the budget rises in 256-step increments, bounded by the requested maximum. This helps deep scenes avoid flat lighting. The probe pattern is a heuristic, not an exhaustive proof about every pixel.
Tiles use the full-image mapping for their one-pixel halo, preserving neighbor continuity across seams. CPU fallback follows the same height-neighborhood principle. This is relief shading over a two-dimensional fractal field. It does not create a polygon mesh or change the fractal equation.
5. Keep navigation responsive and completion honest
During a gesture, the last complete image follows the pan or pinch. That transformed image is a preview of the composition, not newly calculated deep detail. After commit, bounded center-out Metal work progressively replaces an initialized preview with completed regions.
Every committed request has a generation identity. A successful but obsolete tile cannot replace a newer view. The full-frame progress indicator completes only after the current image has reached its full presentation path. Input response, first visible refined region, and completed frame are different measurements.
The renderer retains a valid slow frame even if it exceeds the 250 ms diagnostic target. A slow duration alone does not open the failure circuit. A failed or unusable command can select the CPU deep fallback. This keeps performance policy from discarding a correct result merely because a demanding scene took longer.
6. Re-render for capture
Every capture, including Canvas Size, uses a dedicated export render and waits for the assembled image. It does not save a partially refined live texture. The exporter snapshots the composition, recomputes mapping and precision eligibility for the output, and renders 256-pixel tiles using consistent full-image coordinates.
Canvas Size uses drawable dimensions. The 4K, 6K, and 8K presets preserve the canvas aspect ratio with nominal longest edges of 3,840, 5,760, and 7,680 pixels. Custom capture accepts explicit dimensions. Free includes Canvas Size; Pro adds the larger and custom outputs. Both use the same rendering mathematics.
Tiling limits an individual working region, but the final image still occupies memory. A 7,680 × 4,320 BGRA8 color plane alone requires about 126.6 MiB; staging, image conversion, and PNG encoding can add more. This is a storage calculation, not a measured peak-memory figure.
A larger export can use a different pixel step and arithmetic route from the live view. The composition is preserved, but identical pixels across resolutions or devices are not promised. Keep iteration policy and appearance fixed when comparing output sizes.
7. Separate implementation, tests, and measurements
This page reflects the build 582 source reviewed on September 17, 2026. The September 17 automated regression run passed 114 tests in 14 suites, including rendering/state behavior and ownership restoration. Tests cover asserted cases; they do not establish mathematical correctness at every coordinate or performance on every device.
The published physical-device benchmark used build 548 on an iPhone 17 Pro Max with iOS 26.6.1. Ten scenes averaged 203.890 seconds in the previous release and 42.419 seconds in Version 3, a 4.81× aggregate ratio. These historical results are not timings for build 582 or the current lighting implementation. The revised white paper includes the complete method, raw table, and limits.
For a useful comparison, record build, device, OS, drawable size, coordinate units, exact view, iterations, sampling, palette, LUT width, contrast, lighting, and chosen route. Measure reference setup and full presentation, and repeat with controlled order and thermal conditions. Apple’s compute encoder and pixel-format documentation explain the underlying platform contracts.
