# Smooth Orbit Camera — `orbit-camera@1.0.0`

A camera that glides around your subject with a gentle rise and fall. Loops perfectly.

Circle any object with a calm, cinematic camera move. Set how far away it sits, how high, how many turns per clip and how much it gently bobs or breathes in and out. It is plain math (a position and a look-at point for each moment of the clip), so it works around any subject in any 3D scene.

- Kind: camera; status: published; digest sha256:e54405aabe47ddc8c4115651f25b8316d602d4f5fb0eef2c3630aba48bac3e09
- Role: Camera motion: position and look-at target for any 3D camera over the clip.
- Implementation: js; entry `src/orbit-camera.js`
- Files: https://aipixelwizard.com/registry/orbit-camera/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/orbit-camera.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/orbit-camera/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/orbit-camera/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (demo) — Demo scene only. src/orbit-camera.js has no dependencies.; `apw-runtime@1.0.0` (demo) — Demo harness.

## Exports
- `orbitCamera(progress, { target?, radius?, height?, turns?, startAngle?, heightWave?, heightWaves?, radiusWave?, radiusWaves?, targetLift?, easing? }) => { position, target, angle }` — Camera position and look-at target for a progress value 0…1.
- `applyOrbit(camera, state) => camera` — Applies a state to any camera with position.set() and lookAt() (Three.js cameras).
- `fitRadius(boundingRadius, fovDegrees?, margin?) => number` — Distance that keeps an object of a given size in frame.

## Parameters
- `turns` (integer 1–3, default 1) — Turns per clip
- `direction` (select one of counterclockwise | clockwise, default "counterclockwise") — Direction
- `radius` (number 4–14, default 8.5) — Distance: How far the camera stays from the subject.
- `height` (number 0.3–6, default 2.2) — Height
- `bob` (number 0–1.5, default 0.35) — Rise and fall
- `breathe` (number 0–2, default 0.4) — Move in and out
- `easing` (select one of linear | smooth, default "linear") — Speed: Steady loops seamlessly. Ease in & out starts and ends slowly (no seamless loop).
- `fov` (number 20–70, default 38) — Lens (field of view)
- `accent` (color, default "#7c6cff") — Subject colour
- `background` (color, default "#101016") — Background

## Composition
- Inputs: Clip progress (frame / durationFrames) and a target point.
- Outputs: Camera position [x, y, z] and look-at target [x, y, z].
- Coordinates: Y-up. Angle 0 is on the +Z side of the target, increasing towards +X. Units match your scene.
- Lighting: None. Keep your scene's lights.
- Timing: Driven by progress 0…1, so the same path stretches to any duration.
- Works with: chrome-material, glass-container, bouncing-balls, iridescent-blob
- Pick radius with fitRadius(boundingRadius, fov) to keep the subject framed.

## Output and loop
- 1080×1080, 30 fps, 8 s, procedural
- Seamless loop: yes — Whole turns and whole bob/breathe cycles. Only seamless with the default Steady speed; Ease in & out is not loopable.
- Verified 2026-10-02 in Chrome 151.0.7922.137 headless on Linux 7.0.0-31-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: Import orbitCamera and applyOrbit, compute progress = frame / totalFrames each frame and call applyOrbit(camera, orbitCamera(progress, options)).

Existing project: Replace only your camera update with orbitCamera(progress, options). Pass your subject's centre as target. Leave the rest of the scene untouched.

Reuse ideas:
- Circle the glass box of Bouncing Marbles while the balls fall (see the Bouncing Marbles in a Box example).
- Feed the position into a 2D perspective projection to orbit a canvas particle field.
- Use easing 'smooth' and turns 0.25 for a slow quarter-turn product reveal.

## Limitations
- Does not avoid obstacles; choose a radius that clears your scene.

## 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: Original code written for AI Pixel Wizard in October 2026.

