# Cinematic Lens Flare — `lens-flare@1.0.0`

A cinematic lens flare drawn over any frame: glow, starburst, anamorphic streak, rainbow halo and ghosts that move with the light.

A lens flare as a finishing layer. Place a light anywhere in the frame (or just outside it) and the flare follows: a bright core and glow, a starburst from the aperture blades, an anamorphic streak, a rainbow halo, a chain of ghosts reflected through the centre of the frame, lens dirt that lights up near the source and a veil of glare. Four styles (cinematic, anamorphic blue streak, sun, soft), any light colour, and a light that drifts on a loop, sweeps across or stays put. On black it is ready to drop on a clip with Screen blending.

- Kind: effect; status: published; digest sha256:b2385edbafe5221be26b053fe7e9bf0516e74fc04545a44c04ecab653c82a347
- Role: Full-frame finishing effect (Canvas 2D): a lens flare layered over a scene.
- Implementation: canvas2d; entry `src/lens-flare.js`
- Files: https://aipixelwizard.com/registry/lens-flare/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/lens-flare.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/lens-flare/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/lens-flare/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createLensFlare({ style?, tint?, ghosts?, rays?, size?, dirt?, grain?, seed? }) => { style, draw(ctx, { x, y, width, height, intensity? }) }` — Draws the flare over whatever is on the canvas, for a light at (x, y) in pixels. Uses additive blending: draw it last.
- `lightPath(progress, kind = 'drift') => { x, y }` — Light position in fractions of the frame: 'drift' (a figure-of-eight that loops exactly), 'sweep' (left to right) or 'static'.
- `STYLES: Record<'cinematic' | 'anamorphic' | 'sun' | 'soft', object>` — Which flare elements each style uses and how strong they are.

## Parameters
- `style` (select one of cinematic | anamorphic | sun | soft, default "cinematic") — Flare style
- `tint` (color, default "#ffd29a") — Light colour
- `path` (select one of drift | sweep | static, default "drift") — Light movement: Drift loops seamlessly; Sweep crosses the frame once.
- `scene` (select one of dusk | space | black, default "dusk") — Backdrop: Black is for laying the flare over your own footage with Screen blending.
- `intensity` (number 0.3–1.5, default 1) — Brightness
- `size` (number 0.5–1.6, default 1) — Size
- `ghosts` (integer 0–12, default 7) — Ghosts: Reflections along the line through the centre of the frame.
- `rays` (integer 6–24, default 12) — Starburst rays
- `dirt` (number 0–1, default 0.5) — Lens dirt
- `seed` (integer 1–9999, default 7) — Variation

## Composition
- Inputs: A canvas with your scene already drawn, and the light position in pixels (it may be off-frame).
- Outputs: Adds light onto the canvas ('lighter' compositing) plus a faint dither grain over the frame.
- Coordinates: Pixels of the canvas you draw on; ghosts mirror the light through the canvas centre.
- Lighting: Additive; on black the result can be blended over footage with Screen or Add.
- Timing: draw() is a pure function of the light position; lightPath(progress) gives a position per frame.
- Works with: gradient-background, kinetic-title, particle-tunnel, number-reveal
- To flare a 3D scene, project your light's 3D position to the screen (vector.project(camera)) and draw onto a 2D canvas laid over the WebGL one.
- Fade intensity down when the light is hidden behind something.

## Output and loop
- 1080×1080, 30 fps, 8 s, procedural
- Seamless loop: yes — With Drift (the default) the light follows a closed figure-of-eight with whole-number frequencies, and the flare is a pure function of the light position, so the last frame meets the first. Sweep and Still do not loop.
- Verified 2026-10-05 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (Canvas 2D); deterministic: yes; loop check: Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255.

## Use it
New project: Draw your background on a canvas, then call createLensFlare().draw(ctx, { x, y, width, height }) every frame, with x and y from lightPath(frame / totalFrames).

Existing project: Copy src/lens-flare.js. After your scene is drawn each frame, draw the flare onto a 2D canvas over it at the light's screen position.

Reuse ideas:
- Title reveal: sweep the light behind Kinetic Title Reveal for a cinematic title card.
- Overlay for footage: render it on black and blend it over a clip with Screen in any editor.
- Product hero: pin the light to a product's highlight in a 3D scene by projecting its position each frame.

## Limitations
- A drawn approximation of a lens, not optical ray tracing: ghosts follow the classic axis through the frame centre.
- It does not detect occlusion: lower intensity yourself when the light goes behind something.

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.

Provenance: Adapted from the owner's threejs-sketches/light-atmosphere/webgpu-lens-flare (MIT), rebuilt for AI Pixel Wizard in October 2026 as a Canvas 2D finishing layer (no WebGPU needed) with flare styles, lens dirt, dithering and the frame/time contract.

