{
  "schemaVersion": 1,
  "id": "marble-sorter",
  "version": "1.0.0",
  "status": "published",
  "kind": "object",
  "title": "Marble Sorter",
  "summary": "A glass marble machine that sorts marbles by colour through three channels, then lifts them back to the top. Loops perfectly.",
  "description": "A machine that sorts marbles by colour, forever. Mixed marbles drop from the top into a glass tube that splits into three channels; brass gates flick open to send each colour down its own lane; the lanes rejoin, a conveyor carries the marbles to the foot of a lift and the lift raises them back to the top. Choose the number of marbles, the colours (primary, candy, forest or mono), the trips round the circuit per loop, glass or brass tubes, the backdrop colour and whether the camera sways.",
  "role": "Object (3D, three.js): a mechanical toy-machine loop, endlessly satisfying, for kids' content, games and tech.",
  "collections": [
    "satisfying",
    "hypnotic-loops",
    "product-reveals"
  ],
  "tags": [
    "marble sorter",
    "marble machine",
    "sorting",
    "gates",
    "tubes",
    "glass",
    "mechanical",
    "satisfying",
    "toy",
    "loop",
    "3d"
  ],
  "synonyms": [
    "marble machine",
    "colour sorter",
    "gate sorter",
    "marble conveyor",
    "ball sorter",
    "sorting machine"
  ],
  "useCases": [
    "A toy, game or kids' content scene",
    "A \"sorting\" or automation explainer",
    "A satisfying mechanical loop"
  ],
  "featured": null,
  "publishedAt": "2026-10-05",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/marble-sorter.js",
    "exports": [
      {
        "name": "createMarbleSorter",
        "signature": "createMarbleSorter({ marbles?, palette?, trips?, tubes? }) => { group, setProgress(progress), dispose() }",
        "description": "The board, tubes, gates and marbles, about 13 wide and 8 tall in the xy plane; needs an environment map."
      },
      {
        "name": "PALETTES",
        "signature": "PALETTES: { primary, candy, forest, mono }",
        "description": "The three marble colours."
      }
    ],
    "reusable": [
      "src/marble-sorter.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",
      "help": "Marbles in circulation, in threes.",
      "type": "integer",
      "min": 6,
      "max": 24,
      "step": 3,
      "default": 12
    },
    {
      "key": "palette",
      "label": "Colours",
      "type": "select",
      "default": "primary",
      "options": [
        {
          "value": "primary",
          "label": "Primary"
        },
        {
          "value": "candy",
          "label": "Candy"
        },
        {
          "value": "forest",
          "label": "Forest"
        },
        {
          "value": "mono",
          "label": "Mono"
        }
      ]
    },
    {
      "key": "trips",
      "label": "Trips per loop",
      "type": "integer",
      "min": 1,
      "max": 3,
      "step": 1,
      "default": 1
    },
    {
      "key": "tubes",
      "label": "Tubes",
      "type": "select",
      "default": "glass",
      "options": [
        {
          "value": "glass",
          "label": "Glass"
        },
        {
          "value": "brass",
          "label": "Brass"
        }
      ]
    },
    {
      "key": "background",
      "label": "Backdrop",
      "type": "color",
      "default": "#c9ccd4"
    },
    {
      "key": "orbit",
      "label": "Camera sway",
      "type": "boolean",
      "default": true
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Primary, glass",
      "params": {}
    },
    {
      "id": "candy",
      "label": "Candy, many",
      "params": {
        "palette": "candy",
        "marbles": 21,
        "background": "#e0d8e4"
      }
    },
    {
      "id": "forest",
      "label": "Forest, brass tubes",
      "params": {
        "palette": "forest",
        "tubes": "brass",
        "background": "#d4dccf"
      }
    },
    {
      "id": "fast",
      "label": "Mono, fast",
      "params": {
        "palette": "mono",
        "trips": 3,
        "marbles": 9,
        "background": "#20242c"
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 16,
    "posterSeconds": 4,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 16,
      "method": "Every marble makes a whole number of trips round its closed circuit per loop with the marbles spaced evenly in time, each gate's opening is a function of the marbles passing it, 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, colours, trips, tubes, backdrop and camera sway.",
    "outputs": "A full frame rendered in 3D with a studio environment.",
    "coordinates": "The machine stands in the xy plane (x from −3.5 to 5.5, y from 0 to 6.7) in front of a board at z = −0.7; the camera looks at (2.7, 3.5).",
    "lighting": "A warm key light with a shadow map and the studio environment.",
    "timing": "setProgress(progress) places each marble along its lane's curve and sets the gates; frames render in any order.",
    "compatibleWith": [
      "chrome-material",
      "orbit-camera",
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "Marble colour decides the lane: left, middle or right; the gates open as the marbles for the side lanes pass the split."
    ]
  },
  "reuse": [
    "Toy brand: Primary, glass.",
    "Soft: Candy, many.",
    "Industrial: Forest, brass tubes."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer with a studio environment and a shadow-casting light, add createMarbleSorter().group and call sorter.setProgress(progress) each frame.",
    "existingProject": "Copy src/marble-sorter.js; add the group to your scene with an environment map."
  },
  "performance": {
    "notes": "Tubes of about 25 000 triangles and up to 24 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": [
    "Marbles move at a steady speed along their lane rather than accelerating down slopes.",
    "The gates are decorative: marbles follow their lanes whatever the gates do."
  ],
  "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-sorter (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original studio object: three closed lane curves through glass tubes with marbles sorted by colour, gates opening as the marbles pass and a lift back to the top, on an exact loop.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
