# Orrery — Animated Data Visualization for React

> A machined dotted interactive globe: deterministic ice-white land dots on a matte graphite sphere with a whisper of rim atmosphere. Drag it with heavy desk-globe momentum and the idle spin clutches back in slowly; connection arcs launch in seeded bursts, travel with comet heads, and land with surface pulses. Camera-facing marker labels, hero-edge camera offset, one cool arc accent. Original OGL implementation — instanced point dots, DPR-clamped, offscreen-paused, full GL teardown, reduced-motion-safe.

- Page: https://crucibleui.com/components/data/orrery
- Category: [data](https://crucibleui.com/components/data)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: ogl
- npm dependencies: ogl

## Install

```bash
npx shadcn@latest add @crucible/orrery
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/orrery`.

## Props (`OrreryProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `arcs?` | `OrreryArc[]` | `a built-in set of 12 intercontinental routes` | Pool of routes the seeded scheduler launches arcs from, in bursts. |
| `markers?` | `OrreryMarker[]` | `[]` | Pinned locations with optional camera-facing labels. |
| `rotationSpeed?` | `number` | `1` | Idle rotation speed multiplier. 0 disables the idle spin. |
| `tilt?` | `number` | `23` | Spin-axis tilt in degrees, like a desk globe on its stand. |
| `momentum?` | `number` | `1` | Throw weight: scales the velocity carried after a drag release. Higher feels heavier — more carry from the same flick. |
| `friction?` | `number` | `1` | How quickly a thrown spin bleeds off. Higher stops sooner; lower glides longer. The idle spin always re-engages via a slow clutch. |
| `arcFrequency?` | `number` | `1` | Arc burst rate multiplier. 0 effectively disables new arcs. |
| `arcAltitude?` | `number` | `1` | Arc apex height multiplier (scaled by route distance). |
| `arcSpeed?` | `number` | `1` | Arc travel speed multiplier. |
| `dotDensity?` | `number` | `1` | Surface dot density multiplier (≈11k samples at 1). Clamped 0.25–3. |
| `oceanDots?` | `number` | `0.16` | Opacity of the faint ocean stipple, 0–1. 0 renders land only. |
| `atmosphere?` | `number` | `1` | Atmosphere rim + halo intensity. 0 disables. |
| `cameraDistance?` | `number` | `3.6` | Camera distance in globe radii — smaller fills the frame more (below ~2.8 the sphere overflows the canvas, useful for extreme crops). |
| `cameraOffset?` | `[number, number]` | `[0, 0]` | Globe offset from frame center in globe radii, `[x right, y up]` — lets the globe sit half-cropped at a hero's edge. |
| `colors?` | `OrreryColors` | `matte graphite / ice white / cool blue accent` | Palette overrides. |
| `size?` | `number` | — | Square size in px. Omit to fill the parent (give the parent a size). |
| `dpr?` | `number` | `1.5` | devicePixelRatio ceiling; effective DPR is `min(devicePixelRatio, dpr)`. |
| `paused?` | `boolean` | `false` | Freeze all animation, holding the current frame. Drag still works. |
| `label?` | `string` | — | Accessible alt text. When set, the component is exposed as `role="img"` with this label and becomes focusable — arrow keys turn the globe (Shift for bigger steps). Otherwise it is `aria-hidden` decoration. |
| `seed?` | `number` | `7` | Seed for the deterministic dot dither and arc scheduler. |
| `className?` | `string` | — | Extra classes for the wrapper. |

## Usage example

```tsx
"use client";

import * as React from "react";

import { Orrery } from "@/components/crucible/orrery";

const MARKERS = [
  { lat: 51.51, lng: -0.13, label: "London" },
  { lat: 37.77, lng: -122.42, label: "San Francisco" },
  { lat: 35.68, lng: 139.69, label: "Tokyo" },
  { lat: 1.35, lng: 103.82, label: "Singapore" },
];

export default function OrreryDemo() {
  return (
    <div className="relative isolate flex h-full min-h-[360px] w-full items-center justify-center overflow-hidden rounded-xl bg-neutral-950">
      <Orrery
        className="absolute left-1/2 top-1/2 -translate-x-1/2 translate-y-[-18%]"
        size={640}
        markers={MARKERS}
        label="Rotating dotted globe with connection arcs between world cities"
      />
      {/* Caption pinned to the stage top so marker chips riding the globe's
          upper silhouette never pass through it. */}
      <div className="pointer-events-none absolute inset-x-0 top-7 z-10 text-center">
        <p className="text-sm font-medium text-white/85">Drag the globe</p>
        <p className="mx-auto mt-1 max-w-xs text-xs leading-relaxed text-white/45">
          Throw it — the heavy momentum spins down, then the idle rotation
          clutches back in. Arcs launch in seeded bursts.
        </p>
      </div>
    </div>
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through. Pauses offscreen and on hidden tabs, clamps device pixel ratio, and tears down its GL/canvas context on unmount.
