# Lava Lamp — `lava-lamp@1.0.0`

A glowing lava lamp: wax swells off the hot pool, rises through the liquid, cools and sinks back. Loops perfectly.

A classic lava lamp glowing in a dark room: a tapered glass bottle on a turned metal base with a pointed cap, a hot bulb underneath and blobs of wax that swell from the pool at the bottom, rise through the glowing liquid, cool at the top and sink back, merging and splitting on the way. The wax is translucent and brightest near the bulb, with a halo in the liquid round it; the glass reflects and refracts the room. Choose the colours (classic red and amber, violet and blue, lime and teal, rose and gold), the metal of the base (chrome, brass, black or copper), how many blobs ride in the lamp, how hot the bulb burns, and a macro view that puts the camera right up against the glass so the wax fills the frame.

- Kind: object; status: published; digest sha256:dd38a3c2d26cfea59ff7e008c9a47e6c79cc823c747908ba42c04957bc9923ca
- Role: Hero object or background (Three.js 3D): a hypnotic, retro lava lamp for lofi, chill and 70s content.
- Implementation: three; entry `src/lava-lamp.js`
- Files: https://aipixelwizard.com/registry/lava-lamp/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/lava-lamp.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/lava-lamp/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/lava-lamp/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer, physical glass and metal, and the wax's ray-marching shader.; `chrome-material@1.1.0` (demo) — Studio environment light and the contact shadow.; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createLavaLamp({ wax?, liquid?, metal?, blobs?, heat?, seed? }) => { group, height, uniforms, update(time, loopSeconds, camera?, pixelHeight?), dispose() }` — The lamp as a THREE.Group (1 unit = 10 cm, standing on y = 0, 3.98 tall). update(time, loopSeconds) moves the wax; pass the camera so the wax's edge is antialiased at the right size.
- `createGlowBackdrop(glow, wall?) => { mesh, dispose() }` — A dark wall washed with the lamp's glow, in opaque material so the glass can refract it.
- `METALS: { chrome, brass, black, copper }` — Finishes for the base and cap.
- `PALETTES: { classic, violet, lime, rose }` — Suggested wax and liquid colour pairs.

## Parameters
- `palette` (select one of classic | violet | lime | rose, default "classic") — Colours
- `metal` (select one of chrome | brass | black | copper, default "chrome") — Base and cap
- `view` (select one of lamp | macro, default "lamp") — View
- `blobs` (integer 3–10, default 7) — Blobs
- `heat` (number 0.6–1.6, default 1) — Bulb heat: How brightly the bulb lights the wax and the liquid.
- `room` (color, default "#09060c") — Room colour
- `table` (color, default "#15100f") — Table
- `seed` (integer 1–999, default 2) — Seed: Changes the size and path of every blob.

## Composition
- Inputs: Time within the loop; the colours, metal, blob count, bulb heat and view.
- Outputs: A THREE.Group: the base, glass, cap and the wax volume, standing on y = 0 at the origin.
- Coordinates: 1 unit = 10 cm. The lamp is 3.98 tall; the bottle runs from 0.92 to 3.3 and is 1.4 wide at the bottom, 0.72 at the neck.
- Lighting: Physical glass and metal: needs scene.environment. The wax is lit by the bulb inside its own shader; add point lights in the wax colour to wash the base and the table.
- Timing: update(time, loopSeconds) is a pure function of time; frames render in any order.
- Works with: chrome-material, orbit-camera, kinetic-title
- The wax volume is opaque on purpose: three.js transmission refracts only opaque objects, so the glass can show it.
- Pass the camera to update() so the wax's soft edge is antialiased at the right size.

## Output and loop
- 1080×1080, 30 fps, 15 s, procedural
- Seamless loop: yes — Every blob makes a whole number of trips up and down the bottle per loop, with its sideways sway and its breathing in whole cycles, and the pool swells in whole cycles, 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: Set scene.environment (createStudioEnvironment from Chrome Material works), add createGlowBackdrop(...).mesh behind and createLavaLamp().group, and call update(time, loopSeconds, camera, height) each frame.

Existing project: Copy src/lava-lamp.js. Add the group to your scene (scale it to suit), keep what is behind it opaque so the glass can refract it, and call update every frame.

Reuse ideas:
- Lofi stream: Classic red and amber, camera drift on, with a small caption or a music player.
- Retro intro: Violet and blue with a brass base, and a chunky 70s title.
- Full-screen texture: Rose and gold in macro view behind a quote.

## Limitations
- The blobs follow choreographed paths rather than a heat-and-buoyancy simulation: they never stick to the glass or pinch off in different places each time.
- The lamp stands still; only the camera moves.

## 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-lava-lamp (liquid-fluid, MIT), rebuilt for AI Pixel Wizard in October 2026 in WebGL: a modelled lamp with physical glass and metal, translucent ray-marched metaball wax lit by the bulb, a halo in the liquid, four colourways, a macro view, an exact loop and the frame/time contract.

