{
  "schemaVersion": 1,
  "id": "caustic-dance",
  "version": "1.0.0",
  "status": "published",
  "kind": "background",
  "title": "Caustic Dance",
  "summary": "The bright, shifting net of light on the floor of a swimming pool, sliding over wobbling tiles. Loops perfectly.",
  "description": "The dancing net of sunlight on the floor of a pool: ripples on the surface bend the light into a lattice of thin bright lines that slides, splits and rejoins over blue tiles, which themselves wobble as the water bends them. Choose the water (turquoise, deep blue, lagoon or sunset), tiles on or off, the size of the net, its brightness and how thin its lines are, and how much the tiles wobble.",
  "role": "Background (WebGL shader, full frame): a calm, bright water backdrop for summer, wellness, travel and title content.",
  "collections": [
    "backgrounds",
    "satisfying",
    "hypnotic-loops"
  ],
  "tags": [
    "caustics",
    "pool",
    "water",
    "light",
    "ripples",
    "tiles",
    "summer",
    "underwater",
    "calm",
    "loop",
    "background"
  ],
  "synonyms": [
    "pool light",
    "water caustics",
    "underwater light",
    "swimming pool",
    "rippling light",
    "water reflections"
  ],
  "useCases": [
    "A summer, travel or hotel title background",
    "A wellness, spa or meditation backdrop",
    "A calm lower-third or lyric-video background"
  ],
  "featured": null,
  "publishedAt": "2026-10-06",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/caustic-dance.js",
    "exports": [
      {
        "name": "createCausticPool",
        "signature": "createCausticPool({ water?, tiles?, scale?, brightness?, wobble?, sharpness? }) => { scene, camera, setSize(width, height), update(time, loopSeconds), dispose() }",
        "description": "The pool floor and its light as one full-screen shader; render its scene with its camera, ideally through createBloomPass."
      },
      {
        "name": "createBloomPass",
        "signature": "createBloomPass(renderer, { width, height, glow?, exposure?, grain? }) => { render(scene, camera, output?), dispose() }",
        "description": "A small bloom with filmic tone mapping."
      },
      {
        "name": "WATERS",
        "signature": "WATERS: { turquoise, deep, lagoon, sunset }",
        "description": "Water tints (sRGB hex)."
      }
    ],
    "reusable": [
      "src/caustic-dance.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer, geometry and shader materials."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness."
    }
  ],
  "params": [
    {
      "key": "water",
      "label": "Water",
      "type": "select",
      "default": "turquoise",
      "options": [
        {
          "value": "turquoise",
          "label": "Turquoise"
        },
        {
          "value": "deep",
          "label": "Deep blue"
        },
        {
          "value": "lagoon",
          "label": "Lagoon green"
        },
        {
          "value": "sunset",
          "label": "Sunset"
        }
      ]
    },
    {
      "key": "tiles",
      "label": "Tiles",
      "type": "boolean",
      "default": true
    },
    {
      "key": "scale",
      "label": "Net size",
      "type": "number",
      "min": 0.5,
      "max": 2,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "brightness",
      "label": "Brightness",
      "type": "number",
      "min": 0.4,
      "max": 1.8,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "sharpness",
      "label": "Line sharpness",
      "help": "Higher makes thinner lines.",
      "type": "number",
      "min": 4,
      "max": 14,
      "step": 0.5,
      "default": 11
    },
    {
      "key": "wobble",
      "label": "Tile wobble",
      "type": "number",
      "min": 0,
      "max": 2,
      "step": 0.05,
      "default": 1
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Turquoise pool tiles",
      "params": {}
    },
    {
      "id": "deep",
      "label": "Deep blue, fine lines",
      "params": {
        "water": "deep",
        "sharpness": 11,
        "scale": 0.8
      }
    },
    {
      "id": "lagoon",
      "label": "Lagoon, no tiles",
      "params": {
        "water": "lagoon",
        "tiles": false,
        "scale": 1.4
      }
    },
    {
      "id": "sunset",
      "label": "Sunset water",
      "params": {
        "water": "sunset",
        "brightness": 1.3
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 12,
    "posterSeconds": 3,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 12,
      "method": "Every layer of the light and the wobble of the tiles are folded sines whose frequencies are whole numbers of cycles per loop, so the last frame meets the first."
    },
    "seed": null
  },
  "composition": {
    "inputs": "Time within the loop; the water, tiles, net size, brightness, line sharpness and tile wobble.",
    "outputs": "A full frame, drawn by one shader on a full-screen quad.",
    "coordinates": "Square-ish tiles about 1/7 of the frame high, seen from straight above.",
    "lighting": "Self-lit: the water's colour for the floor, the light's colour added where the caustic lines are; the bloom adds the glare.",
    "timing": "update(time, loopSeconds) sets the loop phase; frames render in any order.",
    "compatibleWith": [
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "The caustic is five rounds of warping the point by folded sines and summing 1/distance, two layers at different scales; its time factors are integers so it repeats exactly."
    ]
  },
  "reuse": [
    "Summer or travel background: Turquoise pool tiles.",
    "Spa or wellness loop: Lagoon, no tiles.",
    "Evening title: Sunset water."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer, createCausticPool() and createBloomPass(renderer, { width, height }); call pool.setSize(width, height), then each frame pool.update(time, loopSeconds) and bloom.render(pool.scene, pool.camera).",
    "existingProject": "Copy src/caustic-dance.js; render pool.scene with pool.camera into a render target (or through the bloom pass) as a backdrop."
  },
  "performance": {
    "notes": "One full-screen pass with ten warp rounds per pixel and a small bloom. A few milliseconds a frame at 1080p on an integrated Intel GPU.",
    "limits": "Software WebGL renders in about a second a frame."
  },
  "limitations": [
    "The pattern is a stylised formula, not a ray-traced caustic: it does not respond to an actual surface.",
    "The floor is flat and seen from straight above."
  ],
  "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/light-atmosphere/caustic-dance (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original shader: a folded-sine caustic with whole-cycle time factors, wobbling pool tiles and an exact loop.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
