# Disco Ball — `disco-ball@1.0.0`

A mirror ball turning in a dark room, throwing hundreds of coloured squares of light across the walls and floor. Loops perfectly.

A mirror ball made of hundreds of tiny tilted tiles turning in a dark room, lit by coloured spotlights that circle above it. Every tile flashes as it turns past a light and shows a miniature of the room in its mirror; every tile throws its own square of light across the walls, floor and ceiling, so the room fills with moving dots; the beams of the lights show in a haze. Choose the colour of the lights (warm white, rainbow, blue and violet, or gold), the number of rows of tiles, how many spotlights, the haze, how many turns the ball makes in a loop, the brightness, the sparkle of the tiles and how far the camera sways.

- Kind: background; status: published; digest sha256:1629195265ea42e5f9cb06fc8434711412476e556d2942c5946002d3dd743f76
- Role: Background (WebGL shader, full frame): a party, music and nightlife backdrop.
- Implementation: three; entry `src/disco-ball.js`
- Files: https://aipixelwizard.com/registry/disco-ball/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/disco-ball.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/disco-ball/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/disco-ball/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer and the full-screen shader.; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `createDiscoBall({ palette?, rows?, spots?, haze?, turns?, brightness?, sparkle?, sway?, seed? }) => { scene, camera, material, setSize(width, height), update(time, loopSeconds), dispose() }` — The whole room as one full-screen shader: render its scene with its camera after setSize and update, ideally through createBloomPass.
- `createBloomPass(renderer, { width, height, glow?, exposure?, grain? }) => { render(scene, camera, output?), dispose() }` — A small bloom with filmic tone mapping: draws a scene to the canvas with its glow.
- `PALETTES: { classic, rainbow, blue, gold }` — Five light colours for the spotlights, in order.

## Parameters
- `palette` (select one of classic | rainbow | blue | gold, default "classic") — Lights
- `rows` (integer 12–36, default 32) — Rows of tiles: More rows mean smaller tiles and more, smaller dots.
- `spots` (integer 1–5, default 4) — Spotlights
- `haze` (number 0–1.6, default 0.7) — Haze
- `turns` (integer 1–3, default 1) — Turns per loop
- `brightness` (number 0.5–1.6, default 1) — Brightness
- `sparkle` (number 0–1.6, default 1) — Sparkle
- `sway` (number 0–1.5, default 1) — Camera sway
- `seed` (integer 1–999, default 3) — Seed

## Composition
- Inputs: Time within the loop; the colour of the lights, rows of tiles, spotlights, haze, turns, brightness, sparkle and camera sway.
- Outputs: A full frame, drawn by one shader on a full-screen quad.
- Coordinates: World units of about a metre: the ball (radius 0.5) hangs at the origin of a box room 8.4 wide, 4.4 tall and 10.5 deep; the camera is 3.6 away.
- Lighting: The spotlights circle at height 2.45; each tile reflects them: the light dots on the walls are solved in closed form from the half-way vector between the light and the point, seen from the ball.
- Timing: update(time, loopSeconds) sets the loop phase and the ball's angle; frames render in any order.
- Works with: kinetic-title, number-reveal
- Tiles are rings of equal height, as many to a ring as fit, each with a tilt of its own; a dull tile in seven throws no light.
- Nothing is simulated: a point of the room is lit by a spot only if the half-way vector between the directions to the light and to the point falls on a tile.

## Output and loop
- 1080×1080, 30 fps, 12 s, procedural
- Seamless loop: yes — The ball turns a whole number of times per loop and the spotlights circle once, their power pulses in whole cycles and the camera sways in a sine of the loop, so the last frame meets the first.
- Verified 2026-10-06 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (WebGL 2 via SwiftShader (software)); deterministic: yes; loop check: Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255.

## Use it
New project: Create a WebGLRenderer, createDiscoBall() and createBloomPass(renderer, { width, height }); call disco.setSize(width, height), then each frame disco.update(time, loopSeconds) and bloom.render(disco.scene, disco.camera).

Existing project: Copy src/disco-ball.js; render disco.scene with disco.camera into a render target (or through the bloom pass) as the backdrop of your scene.

Reuse ideas:
- Party or music video: Rainbow party behind a kinetic title.
- Club night promo: Blue club, slow.
- Retro fashion or new year: Gold night.

## Limitations
- The tile footprints on the walls are idealised squares: there is no scattering or falloff of the reflected beams beyond a simple distance fade.
- The room is a plain dark box: a dance floor, speakers or people are not drawn.

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.
- three.js 0.185.1: MIT (https://github.com/mrdoob/three.js)

Provenance: Adapted from the owner's threejs-sketches/light-atmosphere/webgpu-disco-ball (MIT) and rebuilt for AI Pixel Wizard in October 2026 as an original shader: a mirror ball of tilted tiles in rings, spots solved in closed form from half-way vectors, the tiles' own mirror images of the room, an exact loop and the frame/time contract.

