{
  "schemaVersion": 1,
  "id": "soap-film",
  "version": "1.0.0",
  "status": "published",
  "kind": "object",
  "title": "Soap Film",
  "summary": "A soap film stretched in a wire frame, swirling with real interference colours as it drains. Loops perfectly.",
  "description": "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.",
  "role": "Hero object or abstract background (Three.js 3D): iridescent, physically coloured soap film.",
  "collections": [
    "hypnotic-loops",
    "chrome-glass",
    "backgrounds"
  ],
  "tags": [
    "soap film",
    "soap bubble",
    "iridescent",
    "holographic",
    "thin film",
    "rainbow",
    "minimal surface",
    "bubble wand",
    "macro",
    "satisfying",
    "loop",
    "3d"
  ],
  "synonyms": [
    "soap bubble macro",
    "iridescent film",
    "holographic surface",
    "bubble colours",
    "oil slick colours",
    "thin film interference",
    "rainbow swirl",
    "minimal surface"
  ],
  "useCases": [
    "A dreamy, colourful background for music, beauty and fashion content",
    "A science moment: interference colours, surface tension and minimal surfaces",
    "Abstract macro texture for transitions and title backgrounds"
  ],
  "featured": null,
  "publishedAt": "2026-10-06",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/soap-film.js",
    "exports": [
      {
        "name": "createSoapFilm",
        "signature": "createSoapFilm(renderer, { frame?, wire?, swirl?, drain?, seed? }) => { group, thickness, radius, update(time, loopSeconds), dispose() }",
        "description": "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."
      },
      {
        "name": "createFilmEnvironment",
        "signature": "createFilmEnvironment(renderer, { intensity? }) => { texture, dispose() }",
        "description": "A dark studio with a broad softbox behind the camera, as an environment map: what makes the film show its colours."
      },
      {
        "name": "WIRES",
        "signature": "WIRES: { chrome, brass, copper, black }",
        "description": "Finishes for the wire frame."
      }
    ],
    "reusable": [
      "src/soap-film.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer, geometry and physical thin-film iridescence."
    },
    {
      "id": "chrome-material",
      "version": "1.1.0",
      "scope": "demo",
      "reason": "Renderer setup (filmic tone mapping)."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness."
    }
  ],
  "params": [
    {
      "key": "frame",
      "label": "Frame",
      "type": "select",
      "default": "ring",
      "options": [
        {
          "value": "ring",
          "label": "Bubble wand"
        },
        {
          "value": "saddle",
          "label": "Saddle-bent wire"
        },
        {
          "value": "catenoid",
          "label": "Between two rings"
        }
      ]
    },
    {
      "key": "wire",
      "label": "Wire",
      "type": "select",
      "default": "chrome",
      "options": [
        {
          "value": "chrome",
          "label": "Chrome"
        },
        {
          "value": "brass",
          "label": "Brass"
        },
        {
          "value": "copper",
          "label": "Copper"
        },
        {
          "value": "black",
          "label": "Black"
        }
      ]
    },
    {
      "key": "view",
      "label": "View",
      "type": "select",
      "default": "object",
      "options": [
        {
          "value": "object",
          "label": "Whole frame"
        },
        {
          "value": "macro",
          "label": "Macro close-up"
        }
      ]
    },
    {
      "key": "swirl",
      "label": "Swirl",
      "type": "number",
      "min": 0,
      "max": 1.5,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "drain",
      "label": "Drainage",
      "help": "How quickly the film thins towards the top, where it turns to pale silver.",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.5
    },
    {
      "key": "background",
      "label": "Backdrop",
      "type": "color",
      "default": "#0b0c12"
    },
    {
      "key": "seed",
      "label": "Seed",
      "help": "Changes the swirl pattern.",
      "type": "integer",
      "min": 1,
      "max": 999,
      "step": 1,
      "default": 3
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Bubble wand",
      "params": {}
    },
    {
      "id": "saddle",
      "label": "Saddle in brass, turning",
      "params": {
        "frame": "saddle",
        "wire": "brass",
        "background": "#0a0f1c",
        "seed": 11
      }
    },
    {
      "id": "catenoid",
      "label": "Film between copper rings",
      "params": {
        "frame": "catenoid",
        "wire": "copper",
        "background": "#120b10",
        "seed": 7
      }
    },
    {
      "id": "macro",
      "label": "Macro colour swirl",
      "params": {
        "view": "macro",
        "swirl": 1.3,
        "drain": 0.35,
        "seed": 21
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 12,
    "posterSeconds": 4,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 12,
      "method": "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."
    },
    "seed": {
      "param": "seed",
      "behavior": "Seed picks the swirl pattern: same seed, same film."
    }
  },
  "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.",
    "compatibleWith": [
      "kinetic-title",
      "iridescent-blob",
      "chrome-material"
    ],
    "notes": [
      "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."
    ]
  },
  "reuse": [
    "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."
  ],
  "integration": {
    "newProject": "Set scene.environment to createFilmEnvironment(renderer).texture over a dark background, add createSoapFilm(renderer).group, and call update(time, loopSeconds) each frame.",
    "existingProject": "Copy src/soap-film.js. Use its thickness texture as iridescenceThicknessMap on your own MeshPhysicalMaterial to give any surface the same moving soap colours."
  },
  "performance": {
    "notes": "A small thickness texture redrawn each frame and one transmissive, iridescent surface. Smooth at 1080p on laptop GPUs.",
    "limits": "Transmission renders the scene behind the film a second time. Software WebGL (no GPU) renders slowly."
  },
  "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": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": [
      {
        "name": "three.js",
        "version": "0.185.1",
        "license": "MIT",
        "url": "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.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
