{
  "schemaVersion": 1,
  "id": "bouncing-marbles-box",
  "version": "1.0.0",
  "status": "published",
  "kind": "example",
  "title": "Bouncing Marbles in a Box",
  "summary": "Shiny chrome balls pour into a glass box, bounce, roll and settle while the camera glides around.",
  "description": "A complete scene built from four ingredients: real bouncing-ball physics, chrome material with studio lighting, a correctly set-up glass box and a smooth orbit camera. Balls drop in from above, collide, spin and come to rest. Switch to candy-coloured balls or a mix, change the number of balls, the bounciness and how far the camera travels. Use it as-is, or take just the parts you need.",
  "role": "Complete example scene.",
  "collections": [
    "satisfying",
    "chrome-glass",
    "bouncing-falling"
  ],
  "tags": [
    "marbles",
    "balls",
    "chrome",
    "glass",
    "physics",
    "bouncing",
    "falling",
    "box",
    "3d",
    "satisfying",
    "camera",
    "example"
  ],
  "synonyms": [
    "shiny balls bouncing in a box",
    "chrome balls",
    "balls in glass box",
    "glass container",
    "marbles falling",
    "shiny bouncing balls",
    "bouncing balls glass"
  ],
  "useCases": [
    "Satisfying physics clip for social media",
    "Starting point for 'X falls into Y' scenes",
    "Showcase of chrome + glass rendering"
  ],
  "featured": 0,
  "publishedAt": "2026-10-02",
  "uses": [
    {
      "id": "bouncing-balls",
      "version": "1.0.0",
      "role": "Physics: ball positions and spins each frame."
    },
    {
      "id": "chrome-material",
      "version": "1.0.0",
      "role": "Ball surface and studio environment lighting."
    },
    {
      "id": "glass-container",
      "version": "1.0.0",
      "role": "The glass box; inner size matches the physics box."
    },
    {
      "id": "orbit-camera",
      "version": "1.0.0",
      "role": "Camera path around the box."
    }
  ],
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "demo.js",
    "exports": [
      {
        "name": "createDemo",
        "signature": "createDemo({ canvas, width, height, params, fps, duration }) => { renderFrame(info), dispose() }",
        "description": "Builds the scene; the harness calls renderFrame for each frame."
      }
    ],
    "reusable": [],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "WebGL rendering."
    },
    {
      "id": "bouncing-balls",
      "version": "1.0.0",
      "reason": "Physics."
    },
    {
      "id": "chrome-material",
      "version": "1.0.0",
      "reason": "Material and environment."
    },
    {
      "id": "glass-container",
      "version": "1.0.0",
      "reason": "Glass box."
    },
    {
      "id": "orbit-camera",
      "version": "1.0.0",
      "reason": "Camera motion."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "reason": "Harness and export tools."
    }
  ],
  "params": [
    {
      "key": "count",
      "label": "Number of balls",
      "type": "integer",
      "min": 4,
      "max": 40,
      "step": 1,
      "default": 18
    },
    {
      "key": "ballStyle",
      "label": "Ball look",
      "type": "select",
      "default": "chrome",
      "options": [
        {
          "value": "chrome",
          "label": "All chrome"
        },
        {
          "value": "mixed",
          "label": "Chrome + colour"
        },
        {
          "value": "candy",
          "label": "Candy colours"
        }
      ]
    },
    {
      "key": "bounciness",
      "label": "Bounciness",
      "type": "number",
      "min": 0.3,
      "max": 0.9,
      "step": 0.01,
      "default": 0.68
    },
    {
      "key": "seed",
      "label": "Variation",
      "type": "integer",
      "min": 1,
      "max": 9999,
      "step": 1,
      "default": 4
    },
    {
      "key": "turns",
      "label": "Camera travel",
      "help": "0 keeps the camera still; 1 is a full circle with an eased start and stop.",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.35
    },
    {
      "key": "cameraDistance",
      "label": "Camera distance",
      "type": "number",
      "min": 8,
      "max": 18,
      "step": 0.1,
      "default": 11.5
    },
    {
      "key": "cameraHeight",
      "label": "Camera height",
      "type": "number",
      "min": 1,
      "max": 9,
      "step": 0.1,
      "default": 5.2
    },
    {
      "key": "glassTint",
      "label": "Glass tint",
      "type": "color",
      "default": "#e8fbff"
    },
    {
      "key": "lightWarmth",
      "label": "Light warmth",
      "type": "number",
      "min": -1,
      "max": 1,
      "step": 0.05,
      "default": 0.1
    },
    {
      "key": "background",
      "label": "Background",
      "type": "color",
      "default": "#0b0b10"
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Chrome pour",
      "params": {}
    },
    {
      "id": "candy",
      "label": "Candy pile",
      "params": {
        "ballStyle": "candy",
        "count": 30,
        "bounciness": 0.5
      }
    },
    {
      "id": "still",
      "label": "Still camera, bouncy",
      "params": {
        "turns": 0,
        "bounciness": 0.86,
        "count": 10,
        "cameraHeight": 3
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 8,
    "posterSeconds": 3.5,
    "alpha": false,
    "motion": "simulation",
    "loop": {
      "seamless": false,
      "method": "Balls fall and settle once."
    },
    "seed": {
      "param": "seed",
      "behavior": "Same seed and settings give identical motion."
    }
  },
  "composition": {
    "inputs": "Parameters only.",
    "outputs": "A rendered Three.js scene.",
    "coordinates": "Y-up. Glass box inner volume 4 × 3 × 4 with floor at y = 0 — identical to the physics box.",
    "lighting": "Studio environment from chrome-material; tone mapped.",
    "timing": "Physics baked for the clip's fps/duration; camera driven by progress.",
    "compatibleWith": [],
    "notes": [
      "Ready to run: all four ingredients are tested together in this example."
    ]
  },
  "reuse": [
    "Swap the balls for other shapes (keep the physics positions) — dice, candies or logos.",
    "Keep the glass box and physics, drop the orbit and use a fixed hero camera.",
    "Recolour with ballStyle candy for a playful version."
  ],
  "integration": {
    "newProject": "Download the pack, run npm install, then `npm run serve` and open bouncing-marbles-box/1.0.0/index.html. Export a video with `npm run render -- bouncing-marbles-box/1.0.0`.",
    "existingProject": "Treat demo.js as a reference for wiring the ingredients together. Copy the ingredient source files into your project and reproduce the assembly in your own scene code; do not replace your project with this demo."
  },
  "performance": {
    "notes": "Glass transmission adds one render pass. Physics bake ~50 ms at load.",
    "limits": "40 balls max in this example."
  },
  "limitations": [
    "Balls do not reflect each other (environment-map reflections only).",
    "No caustics through the glass."
  ],
  "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": "Original code written for AI Pixel Wizard in October 2026.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-02",
      "notes": "Initial release."
    }
  ]
}
