{
  "schemaVersion": 1,
  "id": "marble-run",
  "version": "1.0.0",
  "status": "published",
  "kind": "object",
  "title": "Marble Run",
  "summary": "Glass marbles rolling round a wooden roller-coaster track, speeding down slopes and slowing over crests. Loops perfectly.",
  "description": "Glossy glass marbles rolling round a wooden track that rises and falls in hills and swings through bends, held up on slender posts. The track is a closed circuit, so the marbles never stop: each one accelerates down every slope and slows over every crest, with its speed worked out from the height of the track, and together they make exactly one lap of the circuit per loop. Choose the number of marbles, the track layout (gentle, rollercoaster or figure), the wood (oak, walnut or birch), the laps per loop, the backdrop colour and whether the camera sways.",
  "role": "Object (3D, three.js): a satisfying toy-physics scene for kids' content, games and playful product videos.",
  "collections": [
    "satisfying",
    "bouncing-falling",
    "product-reveals"
  ],
  "tags": [
    "marble run",
    "marbles",
    "roller coaster",
    "track",
    "wood",
    "rolling",
    "toy",
    "gravity",
    "satisfying",
    "studio",
    "loop",
    "3d"
  ],
  "synonyms": [
    "marble track",
    "rolling balls",
    "marble coaster",
    "ball run",
    "toy track",
    "gravity track"
  ],
  "useCases": [
    "A toy, game or kids' brand scene",
    "A satisfying rolling-ball loop",
    "A physics-flavoured background"
  ],
  "featured": null,
  "publishedAt": "2026-10-06",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/marble-run.js",
    "exports": [
      {
        "name": "createMarbleRun",
        "signature": "createMarbleRun({ marbles?, track?, wood?, laps? }) => { group, setProgress(progress), dispose() }",
        "description": "The wooden circuit with its posts and marbles, about 12 across, resting on y = 0; needs an environment map."
      },
      {
        "name": "TRACKS",
        "signature": "TRACKS: { gentle, rollercoaster, figure }",
        "description": "Layouts: radius and the hills and bends round the circuit."
      },
      {
        "name": "MARBLES",
        "signature": "MARBLES: string[]",
        "description": "Marble colours."
      }
    ],
    "reusable": [
      "src/marble-run.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": "marbles",
      "label": "Marbles",
      "type": "integer",
      "min": 1,
      "max": 6,
      "step": 1,
      "default": 4
    },
    {
      "key": "track",
      "label": "Track",
      "type": "select",
      "default": "rollercoaster",
      "options": [
        {
          "value": "gentle",
          "label": "Gentle"
        },
        {
          "value": "rollercoaster",
          "label": "Rollercoaster"
        },
        {
          "value": "figure",
          "label": "Figure"
        }
      ]
    },
    {
      "key": "wood",
      "label": "Wood",
      "type": "select",
      "default": "oak",
      "options": [
        {
          "value": "oak",
          "label": "Oak"
        },
        {
          "value": "walnut",
          "label": "Walnut"
        },
        {
          "value": "birch",
          "label": "Birch"
        }
      ]
    },
    {
      "key": "laps",
      "label": "Laps per loop",
      "type": "integer",
      "min": 1,
      "max": 2,
      "step": 1,
      "default": 1
    },
    {
      "key": "background",
      "label": "Backdrop",
      "type": "color",
      "default": "#d6d2cc"
    },
    {
      "key": "orbit",
      "label": "Camera sway",
      "type": "boolean",
      "default": true
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Oak rollercoaster",
      "params": {}
    },
    {
      "id": "gentle",
      "label": "Birch, gentle",
      "params": {
        "track": "gentle",
        "wood": "birch",
        "marbles": 3,
        "background": "#d8dce2"
      }
    },
    {
      "id": "walnut",
      "label": "Walnut, six marbles",
      "params": {
        "wood": "walnut",
        "marbles": 6,
        "background": "#2a2420"
      }
    },
    {
      "id": "figure",
      "label": "Figure, two laps",
      "params": {
        "track": "figure",
        "laps": 2,
        "marbles": 5,
        "background": "#cfd6d0"
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 16,
    "posterSeconds": 4,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 16,
      "method": "The marbles make a whole number of laps of the closed circuit per loop with their speed set by the track's height, each marble turns a whole number of times per lap and the camera sway is a sine of the loop, so the last frame meets the first."
    },
    "seed": null
  },
  "composition": {
    "inputs": "Time within the loop; the marbles, track, wood, laps, backdrop and camera sway.",
    "outputs": "A full frame rendered in 3D with a studio environment.",
    "coordinates": "The circuit is centred on the origin, 9–12 across, rising to about 6.5 above the floor; marbles of radius 0.3 roll in the trough; the camera looks down at 29°.",
    "lighting": "A warm key light with a shadow map, a shadow-catching floor and the studio environment.",
    "timing": "setProgress(progress) places each marble from a precomputed time table of the circuit; frames render in any order.",
    "compatibleWith": [
      "chrome-material",
      "orbit-camera",
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "The marbles' speed follows sqrt(top height − height), tabulated once; marbles spaced evenly in loop time."
    ]
  },
  "reuse": [
    "Toy brand: Oak rollercoaster.",
    "Soft: Birch, gentle.",
    "Busy: Walnut, six marbles."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer with a studio environment and a shadow-casting light, add createMarbleRun().group and call run.setProgress(progress) each frame.",
    "existingProject": "Copy src/marble-run.js; add the group to your scene with an environment map."
  },
  "performance": {
    "notes": "One track mesh of about 3 600 triangles, 28 posts and up to six marbles with a shadow map: about a millisecond a frame on an integrated Intel GPU.",
    "limits": "Software WebGL renders in about two seconds a frame."
  },
  "limitations": [
    "The marbles' speed follows height but is not a rigid-body simulation: they do not leave the track.",
    "Marbles do not collide with each other."
  ],
  "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/objects-scenes/marble-run (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original studio object: a closed wooden roller-coaster circuit swept from a space curve, with marbles whose speed is tabulated from the track's height, on an exact loop.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
