# Candle Flame — `candle-flame@1.0.0`

A candle burning in the dark: a living flame, glowing wax, a melted crater, one gust and it recovers. Loops perfectly.

A candle burning in a dark room, close in: a wax pillar (or a tall taper, or a cluster of three) with a melted crater and drips down its side, a wick with its ember, a brass dish, and a flame that licks and sways, blue at the foot, a bright yellow body, an orange edge and a dark cone round the wick. The wax glows from within near the top and the flame lights the dish, the table and the air round it, flickering with it. Once in the loop a gust bends the flame over and it recovers. Choose the candles, the wax colour, the flame (warm, gold, classic or rose), how restless the flame is, how strong the gust, the glow and the exposure.

- Kind: object; status: published; digest sha256:e785a6beabf9f59176d0cf8d466cf87671ff9f444f79a6c0c3d02daef20dc0c3
- Role: Hero object (Three.js 3D): an intimate, atmospheric candle for moods, memorials, holidays and storytelling.
- Implementation: three; entry `src/candle-flame.js`
- Files: https://aipixelwizard.com/registry/candle-flame/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/candle-flame.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/candle-flame/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/candle-flame/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer, geometry, point lights and the flame shader.; `chrome-material@1.1.0` (demo) — Studio environment light and the room backdrop.; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createCandles({ layout?, wax?, flame?, flicker?, gust?, seed? }) => { group, height, dish, update(time, loopSeconds, camera), dispose() }` — The candle(s) with their dish, wax, wick, flames, glows and point lights as a THREE.Group (1 unit = 10 cm, dish on y = 0). update turns the flames to face the camera and poses the flicker and the gust.
- `createBloomPass(renderer, { width, height, glow?, exposure?, grain? }) => { render(scene, camera, output?), dispose() }` — Draws the scene into a half-float target, blurs its bright parts at three scales and composites them with filmic tone mapping and a fixed grain.
- `LAYOUTS: { pillar, taper, cluster }` — The candle arrangements.
- `FLAMES: { warm, gold, candle, rose }` — Flame colour sets: inner, middle, outer and glow.

## Parameters
- `layout` (select one of pillar | taper | cluster, default "pillar") — Candles
- `flame` (select one of warm | gold | candle | rose, default "warm") — Flame
- `wax` (color, default "#f1e6cf") — Wax
- `flicker` (number 0.3–1.8, default 1) — Restlessness
- `gust` (number 0–1.5, default 1) — Gust: How far the one gust in the loop bends the flame.
- `glow` (number 0.4–2, default 1) — Glow
- `exposure` (number 0.6–1.8, default 1.15) — Exposure
- `room` (color, default "#0a0706") — Room
- `table` (color, default "#2a190f") — Table
- `seed` (integer 1–999, default 3) — Seed: Changes the rim, the drips and the flicker's pattern.

## Composition
- Inputs: Time within the loop; the camera (the flames turn to face it); the layout, wax, flame, flicker and gust.
- Outputs: A THREE.Group of candles, dish, flames, glows and point lights, drawn with createBloomPass for the glow.
- Coordinates: 1 unit = 10 cm. A pillar is 3 wide and 5 tall; the flame is 3.1 tall; the dish stands on y = 0.
- Lighting: The flame lights everything through a point light in its colour (with shadows from the tallest); the wax carries its own warm emission near the top. Needs scene.environment only for the brass.
- Timing: update(time, loopSeconds, camera) is a pure function of time; frames render in any order.
- Works with: kinetic-title, stop-motion-feel, dither-animation
- The flame is a procedural billboard, additive and HDR: draw it with a bloom (createBloomPass) so it glows.
- Only the first candle's light casts shadows, to keep it fast.

## Output and loop
- 1080×1080, 30 fps, 12 s, procedural
- Seamless loop: yes — The flame's tongues are waves running up it with a whole number of cycles per loop, its brightness and height flicker in whole cycles, and the gust is a single bump well inside 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: Add createCandles().group over a table, call update(time, loopSeconds, camera) and render through createBloomPass(renderer, { width, height }).render(scene, camera).

Existing project: Copy src/candle-flame.js. Add the group to your scene and draw it with your own bloom (the flame is HDR); call update every frame.

Reuse ideas:
- Cosy story: Pillar candle behind a handwritten-style title.
- Remembrance: Golden taper, slow, with a name and a date.
- Birthday or holiday: Three candles, camera drift on.

## Limitations
- The flame is a 2D billboard turned to the camera: it does not have a real volume and looks flat from a very low angle above.
- The wax does not melt over the loop; the crater and drips are fixed.

## 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/webgpu-candle-flame (objects-scenes, MIT), rebuilt for AI Pixel Wizard in October 2026 in WebGL: a procedural flame shader (blue foot, dark cone, tongues running up it in whole loop cycles), a modelled candle with a melted crater, drips, a wick and a brass dish, glowing wax, a flickering light that casts shadows, a gust, a bloom and the frame/time contract.

