# Julia Morph — `julia-morph@1.0.0`

The Julia set of z² + c morphing as c circles the origin: dendrites, spirals, dust and lace in one loop. Loops perfectly.

The Julia set of the iteration z → z² + c, with c travelling once round a circle in the complex plane. The set grows, pinches into dust, thickens into dendrites, curls into spirals and blooms into lace: a whole family of shapes in one smooth morph that returns exactly to where it began. Three looks share the same mathematics: stained glass (the inside coloured by how close the orbit came to a circle, leaded by the edge), neon filaments (the set drawn as glowing lines by its distance estimate) and smooth bands of escape time. Choose the look, the colours (gold, ice, ember or rainbow), the orbit of c, the zoom, how far the view sways, the brightness, the detail and where on its circle c starts.

- Kind: background; status: published; digest sha256:53b51a409b593075dd0887232e67d1eed1524b22cca0d4e834be200c7e8754bd
- Role: Background (WebGL shader, full frame): a hypnotic, mathematical motion loop for science, music and title content.
- Implementation: three; entry `src/julia-morph.js`
- Files: https://aipixelwizard.com/registry/julia-morph/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/julia-morph.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/julia-morph/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/julia-morph/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer, the full-screen shader and the bloom targets.; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createJuliaMorph({ style?, palette?, radius?, zoom?, spin?, brightness?, detail?, start? }) => { scene, camera, material, setSize(width, height), update(time, loopSeconds), dispose() }` — The whole fractal as one full-screen shader: render its scene with its camera after setSize and update, ideally through createBloomPass.
- `createBloomPass(renderer, { width, height, glow?, exposure?, grain? }) => { render(scene, camera, output?), dispose() }` — A small bloom with filmic tone mapping: draws a scene to the canvas with its glow.
- `PALETTES: { ice, ember, neon, gold }` — Cosine gradients: colour = a + b·cos(2π(c·h + d)).
- `STYLES: ['neon', 'smooth', 'stained']` — The three looks.

## Parameters
- `style` (select one of neon | smooth | stained, default "stained") — Look
- `palette` (select one of ice | ember | neon | gold, default "gold") — Colours
- `radius` (number 0.6–0.9, default 0.7885) — Orbit of c: The distance of c from the origin: about 0.79 gives the famous family of shapes.
- `zoom` (number 0.7–2, default 1.2) — Zoom
- `spin` (number 0–1.6, default 1) — Sway
- `brightness` (number 0.5–1.6, default 1) — Brightness
- `detail` (integer 60–256, default 160) — Detail: Iterations per pixel: more shows finer filaments and costs more.
- `start` (number 0–1, default 0.3) — Starting point: Where round its circle c starts, as a fraction of a turn.

## Composition
- Inputs: Time within the loop; the look, colours, orbit of c, zoom, sway, brightness, detail and starting point.
- Outputs: A full frame, drawn by one iterating shader on a full-screen quad.
- Coordinates: The complex plane, about 2.2 units across at zoom 1.2, centred on the origin; c = r·(cos a, sin a) with a going once round per loop.
- Lighting: Self-lit: the colours are the fractal's; the bloom adds the glow of the neon look.
- Timing: update(time, loopSeconds) sets the loop phase; frames render in any order.
- Works with: kinetic-title, number-reveal
- Each pixel iterates z → z² + c with its derivative dz → 2z·dz to up to 256 steps; escaping gives the smooth count ν = n + 1 − log₂ log₂|z| and the distance estimate d = ½·|z|·ln|z| / |dz|, which also antialiases the edges.
- At |c| near 0.79 the set passes through dendrites, spirals, discs and Cantor dust as c goes round.

## Output and loop
- 1080×1080, 30 fps, 12 s, procedural
- Seamless loop: yes — c goes exactly once round its circle per loop, the colours cycle once, and the view's breathing and sway are sines of the loop, so the last frame meets the first.
- Verified 2026-10-06 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (WebGL 2 via SwiftShader (software)); deterministic: yes; loop check: Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255.

## Use it
New project: Create a WebGLRenderer, createJuliaMorph() and createBloomPass(renderer, { width, height }); call julia.setSize(width, height), then each frame julia.update(time, loopSeconds) and bloom.render(julia.scene, julia.camera).

Existing project: Copy src/julia-morph.js; render julia.scene with julia.camera into a render target (or through the bloom pass) as the backdrop of your scene.

Reuse ideas:
- Hypnotic music or meditation loop: Stained glass, gold.
- Sci-fi or tech title: Neon filaments, ice.
- Maths explainer on fractals: Rainbow dragon.

## Limitations
- Single-precision floats limit the zoom: deep zooms into the filaments blur and band.
- The neon look is much costlier to encode than the stained look (fast-morphing fine filaments).

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.
- three.js 0.185.1: MIT (https://github.com/mrdoob/three.js)

Provenance: Adapted from the owner's threejs-sketches/geometry-math/webgpu-julia-morph (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original shader: c exactly once round a circle, a distance-estimate neon look, an orbit-trap stained-glass look, smooth escape bands, an exact loop and the frame/time contract.

