{
  "schemaVersion": 1,
  "id": "abacus-cascade",
  "version": "1.0.0",
  "status": "published",
  "kind": "object",
  "title": "Abacus Cascade",
  "summary": "A wooden counting frame whose glossy beads slide along their rods in rolling waves. Loops perfectly.",
  "description": "A wooden abacus with brass rods and glossy beads. On every rod the beads leave one end one after another, click across and come back, each rod a little out of step with the one above, so a ripple runs down the frame and back again. Choose the number of rods and beads, the bead colours (rainbow, deep, pastel or bone), the wood, how far each rod lags the last, how many crossings a loop holds, the backdrop and whether the camera drifts.",
  "role": "Object (3D, three.js): a satisfying mechanical hero object for counting, maths, finance and rhythm content.",
  "collections": [
    "satisfying",
    "hypnotic-loops",
    "product-reveals"
  ],
  "tags": [
    "abacus",
    "beads",
    "counting",
    "wave",
    "wooden",
    "mechanical",
    "satisfying",
    "maths",
    "loop",
    "3d"
  ],
  "synonyms": [
    "counting frame",
    "bead frame",
    "soroban",
    "bead wave",
    "sliding beads",
    "abacus animation"
  ],
  "useCases": [
    "A satisfying backdrop for finance, maths and education content",
    "A rhythm or cascade metaphor in a title scene",
    "A tactile product-style loop"
  ],
  "featured": null,
  "publishedAt": "2026-10-06",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/abacus-cascade.js",
    "exports": [
      {
        "name": "createAbacus",
        "signature": "createAbacus({ rods?, beads?, palette?, wood?, wave?, cycles? }) => { group, setProgress(progress), dispose() }",
        "description": "The frame, rods and instanced beads as a group; add it to a scene with an environment map and a shadow-casting light."
      },
      {
        "name": "PALETTES",
        "signature": "PALETTES: { rainbow, deep, pastel, bone }",
        "description": "Bead colours (sRGB hex)."
      }
    ],
    "reusable": [
      "src/abacus-cascade.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer, geometry and physical 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": "rods",
      "label": "Rods",
      "type": "integer",
      "min": 3,
      "max": 9,
      "step": 1,
      "default": 6
    },
    {
      "key": "beads",
      "label": "Beads per rod",
      "type": "integer",
      "min": 4,
      "max": 10,
      "step": 1,
      "default": 7
    },
    {
      "key": "palette",
      "label": "Beads",
      "type": "select",
      "default": "rainbow",
      "options": [
        {
          "value": "rainbow",
          "label": "Rainbow"
        },
        {
          "value": "deep",
          "label": "Deep jewel"
        },
        {
          "value": "pastel",
          "label": "Pastel"
        },
        {
          "value": "bone",
          "label": "Bone"
        }
      ]
    },
    {
      "key": "wood",
      "label": "Wood",
      "type": "select",
      "default": "walnut",
      "options": [
        {
          "value": "walnut",
          "label": "Walnut"
        },
        {
          "value": "oak",
          "label": "Oak"
        },
        {
          "value": "ebony",
          "label": "Ebony"
        }
      ]
    },
    {
      "key": "wave",
      "label": "Wave",
      "help": "How far each rod lags the one above it.",
      "type": "number",
      "min": 0,
      "max": 2,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "cycles",
      "label": "Crossings",
      "help": "How many times the beads cross the frame in one loop.",
      "type": "integer",
      "min": 1,
      "max": 3,
      "step": 1,
      "default": 2
    },
    {
      "key": "background",
      "label": "Backdrop",
      "type": "color",
      "default": "#1b2a3a"
    },
    {
      "key": "orbit",
      "label": "Camera drift",
      "type": "boolean",
      "default": true
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Rainbow beads, walnut",
      "params": {}
    },
    {
      "id": "bone",
      "label": "Bone beads, ebony",
      "params": {
        "palette": "bone",
        "wood": "ebony",
        "background": "#2a2420"
      }
    },
    {
      "id": "pastel",
      "label": "Pastel, oak, tall",
      "params": {
        "palette": "pastel",
        "wood": "oak",
        "rods": 8,
        "background": "#2a3340"
      }
    },
    {
      "id": "slow",
      "label": "One slow wave",
      "params": {
        "cycles": 1,
        "wave": 1.6,
        "palette": "deep"
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 12,
    "posterSeconds": 3,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 12,
      "method": "Every bead's place is a smoothstep of a sine that goes round a whole number of times per loop, and the camera drift is a sine of the loop, so the last frame meets the first."
    },
    "seed": null
  },
  "composition": {
    "inputs": "Time within the loop; the rods, beads, colours, wood, wave, crossings, backdrop and camera drift.",
    "outputs": "A full frame rendered in 3D with soft shadows and a studio environment.",
    "coordinates": "The frame is 8 wide and as tall as its rods need, centred on the origin and lifted 3.2 above the floor; beads run along x.",
    "lighting": "A warm key light with a shadow map and the studio environment for the glossy clear-coated beads.",
    "timing": "setProgress(progress) sets every bead; frames render in any order.",
    "compatibleWith": [
      "chrome-material",
      "orbit-camera",
      "kinetic-title",
      "number-reveal"
    ],
    "notes": [
      "On each rod bead i crosses when the rod's sine passes a threshold that grows along the rod, so beads never overlap: x(i+1) − x(i) ≥ one bead width.",
      "Beads are one instanced mesh with per-instance colour."
    ]
  },
  "reuse": [
    "Finance or maths backdrop: Rainbow beads, walnut.",
    "Calm product loop: Bone beads, ebony.",
    "Rhythm title: One slow wave."
  ],
  "integration": {
    "newProject": "Create a WebGLRenderer with shadows and a studio environment (see chrome-material), add createAbacus().group and call abacus.setProgress(progress) each frame.",
    "existingProject": "Copy src/abacus-cascade.js; add abacus.group to your scene with an environment map and a shadow-casting light."
  },
  "performance": {
    "notes": "About 50 instanced beads and a handful of boxes, one shadow map, anti-aliasing. A few milliseconds a frame on an integrated Intel GPU.",
    "limits": "Software WebGL renders in a second or two a frame."
  },
  "limitations": [
    "The beads slide without friction or collision physics: order is guaranteed by the thresholds, not simulated.",
    "The beads are plain flattened spheres without a drilled bore."
  ],
  "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/mechanical-kinetic/abacus-cascade (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original object: threshold-ordered bead waves on an exact loop, clear-coated instanced beads and a brass-and-wood frame.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-05",
      "notes": "Initial release."
    }
  ]
}
