# Soap Film — `soap-film@1.0.0`

A soap film stretched in a wire frame, swirling with real interference colours as it drains. Loops perfectly.

A film of soapy water held in a wire frame in a dark studio, glowing with the interference colours of a film a few hundred nanometres thick: golden and magenta bands where it is thick at the bottom, swirling teal and violet as the liquid flows, thinning to a pale silver at the top. The film is see-through, ripples gently, and its colours shift as the frame turns. Choose the frame (a bubble wand, a wire bent into a saddle, or two rings holding a catenoid), the wire's metal, a close-up macro view where the colours fill the screen, how much the film swirls and drains, and the backdrop.

- Kind: object; status: published; digest sha256:aba05979ce403521646543d8535c5c3a719e583a06c3e4389e5c88c4b89e83f3
- Role: Hero object or abstract background (Three.js 3D): iridescent, physically coloured soap film.
- Implementation: three; entry `src/soap-film.js`
- Files: https://aipixelwizard.com/registry/soap-film/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/soap-film.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/soap-film/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/soap-film/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer, geometry and physical thin-film iridescence.; `chrome-material@1.1.0` (demo) — Renderer setup (filmic tone mapping).; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createSoapFilm(renderer, { frame?, wire?, swirl?, drain?, seed? }) => { group, thickness, radius, update(time, loopSeconds), dispose() }` — The film in its frame as a THREE.Group. update(time, loopSeconds) redraws the film's thickness field and ripples it; the colours come from three.js thin-film iridescence.
- `createFilmEnvironment(renderer, { intensity? }) => { texture, dispose() }` — A dark studio with a broad softbox behind the camera, as an environment map: what makes the film show its colours.
- `WIRES: { chrome, brass, copper, black }` — Finishes for the wire frame.

## Parameters
- `frame` (select one of ring | saddle | catenoid, default "ring") — Frame
- `wire` (select one of chrome | brass | copper | black, default "chrome") — Wire
- `view` (select one of object | macro, default "object") — View
- `swirl` (number 0–1.5, default 1) — Swirl
- `drain` (number 0–1, default 0.5) — Drainage: How quickly the film thins towards the top, where it turns to pale silver.
- `background` (color, default "#0b0c12") — Backdrop
- `seed` (integer 1–999, default 3) — Seed: Changes the swirl pattern.

## Composition
- Inputs: Time within the loop; the frame, wire, swirl, drainage and seed.
- Outputs: A THREE.Group: the film and its wire frame, centred on the origin (the wand's handle hangs below).
- Coordinates: The frame's radius is 1.2; the film faces +z (the catenoid's axis is vertical).
- Lighting: The film's colours are reflections: use createFilmEnvironment (or any environment with a broad bright area in front) as scene.environment, over a dark background.
- Timing: update(time, loopSeconds) is a pure function of time; frames render in any order.
- Works with: kinetic-title, iridescent-blob, chrome-material
- The colours are physical: three.js thin-film iridescence with a thickness from 0 to 1,050 nm and the refractive index of water.
- The thickness field is exposed (film.thickness) to colour other surfaces the same way.

## Output and loop
- 1080×1080, 30 fps, 12 s, procedural
- Seamless loop: yes — The swirl's noise offsets travel round circles a whole number of times per loop, the ripples and the frame's sway or turn are 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 |Δ| 1 of 255.

## Use it
New project: Set scene.environment to createFilmEnvironment(renderer).texture over a dark background, add createSoapFilm(renderer).group, and call update(time, loopSeconds) each frame.

Existing project: Copy src/soap-film.js. Use its thickness texture as iridescenceThicknessMap on your own MeshPhysicalMaterial to give any surface the same moving soap colours.

Reuse ideas:
- Beauty or fashion: Macro colour swirl full screen behind a product name.
- Science explainer: the Saddle preset while you talk about minimal surfaces.
- Music visual: the Bubble wand, slowed, behind lyrics.

## Limitations
- The flow is shaped noise, not a fluid simulation: the film never bursts.
- Colours depend on the environment: in a dull environment the film is nearly invisible, as real soap films are.

## 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/minimal-surface (MIT), rebuilt for AI Pixel Wizard in October 2026 as a soap film with physical thin-film colours: a draining, swirling thickness field, three wire frames (a wand, a saddle and a catenoid), a film-lighting environment, a macro view, and the frame/time contract.

