{
  "schemaVersion": 1,
  "id": "emulsion-separate",
  "version": "1.0.0",
  "status": "published",
  "kind": "object",
  "title": "Emulsion Separate",
  "summary": "A glass of oil on water: glossy beads rise, flatten into the oil and send ripples across the boundary while water beads sink. Loops perfectly.",
  "description": "A tall glass holding a layer of oil on top of water, forever separating. Glossy beads of oil are born at the bottom, swell, race up through the water and flatten into the oil layer, each merge sending a ring of ripples across the boundary between the liquids, while beads of coloured water sink through the oil and merge into the water below. Choose the number of beads, the colours (amber, rose or neon), how many trips each bead makes per loop, the strength of the ripples, the seed, the backdrop colour and whether the camera sways.",
  "role": "Object (3D, three.js): a soothing liquid-science loop, like a lava lamp seen through a glass of oil and water.",
  "collections": [
    "satisfying",
    "chrome-glass",
    "hypnotic-loops"
  ],
  "tags": [
    "oil and water",
    "emulsion",
    "droplets",
    "glass",
    "liquid",
    "separation",
    "ripples",
    "lava lamp",
    "studio",
    "loop",
    "3d"
  ],
  "synonyms": [
    "oil and water",
    "liquid separation",
    "droplet merge",
    "lava lamp",
    "glass of liquid",
    "immiscible liquids"
  ],
  "useCases": [
    "A calm, satisfying liquid loop",
    "A chemistry or physics explainer",
    "A wellness, drinks or cosmetics backdrop"
  ],
  "featured": null,
  "publishedAt": "2026-10-05",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/emulsion-separate.js",
    "exports": [
      {
        "name": "createEmulsion",
        "signature": "createEmulsion({ beads?, palette?, cycles?, ripples?, seed? }) => { group, setProgress(progress), dispose() }",
        "description": "The glass of oil and water with its beads and rippling boundary, base on y = 0; needs an environment map."
      },
      {
        "name": "PALETTES",
        "signature": "PALETTES: { amber, rose, neon }",
        "description": "Colours for the two liquids and their beads."
      }
    ],
    "reusable": [
      "src/emulsion-separate.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer, geometry and shader materials."
    },
    {
      "id": "chrome-material",
      "version": "1.1.0",
      "scope": "demo",
      "reason": "Studio environment light, backdrop and contact shadow."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness."
    }
  ],
  "params": [
    {
      "key": "beads",
      "label": "Beads",
      "type": "integer",
      "min": 6,
      "max": 40,
      "step": 1,
      "default": 20
    },
    {
      "key": "palette",
      "label": "Colours",
      "type": "select",
      "default": "amber",
      "options": [
        {
          "value": "amber",
          "label": "Amber and blue"
        },
        {
          "value": "rose",
          "label": "Rose and mint"
        },
        {
          "value": "neon",
          "label": "Neon"
        }
      ]
    },
    {
      "key": "cycles",
      "label": "Trips per loop",
      "type": "integer",
      "min": 1,
      "max": 3,
      "step": 1,
      "default": 1
    },
    {
      "key": "ripples",
      "label": "Ripples",
      "type": "number",
      "min": 0,
      "max": 2,
      "step": 0.1,
      "default": 1
    },
    {
      "key": "seed",
      "label": "Seed",
      "type": "integer",
      "min": 1,
      "max": 999,
      "step": 1,
      "default": 5
    },
    {
      "key": "background",
      "label": "Backdrop",
      "type": "color",
      "default": "#17202c"
    },
    {
      "key": "orbit",
      "label": "Camera sway",
      "type": "boolean",
      "default": true
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Amber on blue",
      "params": {}
    },
    {
      "id": "rose",
      "label": "Rose and mint, many",
      "params": {
        "palette": "rose",
        "beads": 32,
        "background": "#2a1a26"
      }
    },
    {
      "id": "neon",
      "label": "Neon, two trips",
      "params": {
        "palette": "neon",
        "cycles": 2,
        "background": "#10102a"
      }
    },
    {
      "id": "calm",
      "label": "Few beads, big ripples",
      "params": {
        "beads": 8,
        "ripples": 1.8,
        "seed": 14
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 16,
    "posterSeconds": 4,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 16,
      "method": "Every bead's position, size and squash is a function of the loop phase and its fixed offset, and each bead makes a whole number of trips per loop; ripples are functions of the time since a merge, so the last frame meets the first."
    },
    "seed": {
      "param": "seed",
      "behavior": "Seed places the beads and gives them their sizes and wobble: same seed, same glass."
    }
  },
  "composition": {
    "inputs": "Time within the loop; the beads, colours, trips, ripples, seed, backdrop and camera sway.",
    "outputs": "A full frame rendered in 3D with a studio environment.",
    "coordinates": "The glass is centred on the y axis with radius 1.7 and height 6.2; the boundary between the liquids sits at y = 2.6; the camera looks at the middle of the glass.",
    "lighting": "A warm key light with a shadow map and the studio environment.",
    "timing": "setProgress(progress) places every bead and rewrites the boundary; frames render in any order.",
    "compatibleWith": [
      "chrome-material",
      "orbit-camera",
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "Liquids and glass are transparent without depth writes; beads are opaque with a clearcoat."
    ]
  },
  "reuse": [
    "Calm loop: Amber on blue.",
    "Pastel cosmetic: Rose and mint, many.",
    "Party: Neon, two trips."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer with a studio environment, add createEmulsion().group and call glass.setProgress(progress) each frame.",
    "existingProject": "Copy src/emulsion-separate.js; add the group to your scene with an environment map and a shadow-casting light."
  },
  "performance": {
    "notes": "Two instanced bead sets and a boundary mesh of about 3 500 vertices rewritten each frame: a couple of milliseconds a frame on an integrated Intel GPU.",
    "limits": "Software WebGL renders in about two seconds a frame."
  },
  "limitations": [
    "Beads follow fixed paths; they do not collide or merge with each other.",
    "The liquids are tinted transparent bodies, not refractive."
  ],
  "license": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": []
  },
  "provenance": "Adapted from the owner's threejs-sketches/physics-forces/emulsion-separate (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original studio object: periodic beads rising and sinking through two tinted liquids, with ring ripples on the boundary from each merge, on an exact loop.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
