# palette-reel

URL: /docs/generators/palette-reel

A looping colour-palette reveal video — the moving counterpart of palette.

`palette-reel` is the moving version of [`palette`](/docs/generators/palette). The colours
start as thin slivers showing only their name; one at a time a sliver expands into a band that
reveals its configured `details` (hex / oklch / rgb …), holds, then collapses before the next
opens — sweeping every colour and looping seamlessly. It's a **local** generator: no `url`, output
is mp4. Only `colors` is required.

```ts
{
  name: "brand-colors-reel",
  generator: "palette-reel",
  options: {
    colors: [
      { name: "Ink", hex: "#1a1714" },
      { name: "Camel", hex: "#b49a77" },
      { name: "Loden", hex: "#5c5e4c" },
    ],
    details: ["hex", "oklch"],
  },
}
```

The available `details` field ids are `name`, `hex`, `rgb`, `oklch`, and `hsl` — the same set as
the still [`palette`](/docs/generators/palette).

## Config options

Only `colors` is required; everything else has a default. **Reference** is the interactive view;
**TypeScript** is the same shape in code, every option at its default.

**Reference**

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `colors` | `{ name, hex }[]` |  | The colours to reveal (at least one). _(required)_ |
| `colors.name` | `string` |  | Display name shown on the colour's sliver / band. _(required)_ |
| `colors.hex` | `string` |  | Colour value as a hex string like #D7DBDE. _(required)_ |
| `details` | `('name' \| 'hex' \| 'rgb' \| 'oklch' \| 'hsl')[]` | `['hex', 'oklch', 'rgb']` | Fields revealed when a colour expands. The name is always shown, so it's ignored here. |
| `output` | `object` |  | Output frame / encode settings. |
| `output.width` | `number` | `1920` | Output width in CSS px. |
| `output.height` | `number` | `1080` | Output height in CSS px. |
| `output.deviceScaleFactor` | `number` | `1` | Render scale, max 4 — higher captures crisper, then downscales into the video. |
| `output.fps` | `number` | `30` | Output frames per second (max 120). |
| `output.crf` | `number` | `18` | x264 quality, 0–51 (lower = better quality / larger file). |
| `output.fileName` | `string` |  | Output filename. Defaults to "<slug(asset name)>.mp4". |
| `timing` | `object` |  | Reveal timing (milliseconds). |
| `timing.holdMs` | `number` | `2000` | How long each colour stays fully open before handing off to the next (ms). |
| `timing.transitionMs` | `number` | `700` | Crossfade length from one open colour to the next (ms). |
| `timing.bounce` | `boolean` | `true` | Ping-pong the sweep (down the list then back up) so every handoff is between neighbouring bands, avoiding the last→first "pinch" at the loop seam. When false, it wraps directly (last→first): shorter, but crossfades non-adjacent bands at the seam. |
| `timing.easing` | `'linear' \| 'ease-in' \| 'ease-out' \| 'ease-in-out' \| 'ease-out-strong' \| 'ease-in-out-strong'` | `'ease-in-out'` | Easing applied to the crossfade ramp. |
| `timing.durationMs` | `number` |  | Clip length override (ms). Omit to derive (count × (hold + transition)) for a clean loop. |
| `layout` | `object` |  | Sliver arrangement and sizing. |
| `layout.orientation` | `'rows' \| 'columns'` | `'rows'` | Sliver arrangement: horizontal bands (names upright) or full-height vertical strips. |
| `layout.grownFlex` | `number` | `12` | How many times a sliver's share a fully-open band takes (a collapsed sliver is the baseline). Min 1. |
| `layout.minCrossPx` | `number` | `0` | Minimum cross-size of a sliver (px) so its name stays legible. 0 derives it from the height. |
| `layout.nameAlwaysVisible` | `boolean` | `true` | Keep the name fully visible even in a collapsed sliver (else it fades with the band). |
| `layout.background` | `string` | `'#ffffff'` | Backdrop behind the bands (shown in the gap between them). |
| `layout.gap` | `number` | `0` | Gap between bands (px). 0 means the bands abut. |
| `layout.cornerRadius` | `number` | `0` | Band corner radius (px). 0 is square. |
| `text` | `object` |  | Label typography and formatting. |
| `text.uppercase` | `boolean` | `false` | Uppercase the colour names. |
| `text.rgbStyle` | `'labeled' \| 'css' \| 'plain'` | `'labeled'` | RGB string style. |
| `text.oklchStyle` | `'css' \| 'labeled'` | `'css'` | OKLCH string style. |
| `text.fontFile` | `string` |  | Custom font file (woff2 / woff / ttf / otf), served into the render. Omit for a system bold sans. |
| `text.fontWeight` | `number` | `700` | Label font weight (1–1000). |
| `text.fontSize` | `number` |  | Name font size in px. Omit to derive from the frame size. |
| `text.detailFontScale` | `number` | `0.62` | Detail-line font size as a fraction of the name size. |
| `contrast` | `object` |  | Text-colour selection by band contrast. |
| `contrast.textLight` | `string` | `'#ffffff'` | Light text colour, used on dark bands (picked by contrast). |
| `contrast.textDark` | `string` | `'#141414'` | Dark text colour, used on light bands (picked by contrast). |
| `contrast.contrastThreshold` | `number` | `0.5` | Luminance above which the dark text is used (0..1). |

**TypeScript**

```ts
{
  name: "brand-colors-reel",
  generator: "palette-reel",
  options: {
    // --- colours & reveal ---
    colors: [
      { name: "Ink", hex: "#1a1714" },
      { name: "Camel", hex: "#b49a77" },
    ],
    details: ["hex", "oklch", "rgb"],

    // --- output ---
    output: {
      width: 1920,
      height: 1080,
      deviceScaleFactor: 1,
      fps: 30,
      crf: 18,
      // fileName: "brand-colors-reel.mp4", // optional; defaults to <slug(name)>.mp4
    },

    // --- timing (ms) ---
    timing: {
      holdMs: 2000,
      transitionMs: 700,
      bounce: true,
      easing: "ease-in-out",
      // durationMs: 12000,          // optional; derived from count × (hold + transition)
    },

    // --- layout & sizing ---
    layout: {
      orientation: "rows",
      grownFlex: 12,
      minCrossPx: 0,
      nameAlwaysVisible: true,
      background: "#ffffff",
      gap: 0,
      cornerRadius: 0,
    },

    // --- styling ---
    text: {
      uppercase: false,
      rgbStyle: "labeled",
      oklchStyle: "css",
      // fontFile: "./fonts/Brand.woff2", // optional; falls back to a system bold sans
      fontWeight: 700,
      // fontSize: 64,               // optional; derived from the frame size
      detailFontScale: 0.62,
    },

    // --- contrast ---
    contrast: {
      textLight: "#ffffff",
      textDark: "#141414",
      contrastThreshold: 0.5,
    },
  },
}
```

> Every option also has hover docs in `pro-visu.config.ts` — the authoring types are generated
> from the validation schema, so the editor always matches what the tool accepts.

## Examples

### Looping brand reveal

Feed it the palette and the `details` you want on each band; the reel expands one colour at a time,
holds, and collapses — looping seamlessly.

```ts
{
name: "colors-reel",
generator: "palette-reel",
options: {
  colors: [
    { name: "Ink", hex: "#1a1714" },
    { name: "Paper", hex: "#f6f3ed" },
    { name: "Camel", hex: "#b49a77" },
    { name: "Loden", hex: "#5c5e4c" },
    { name: "Cognac", hex: "#8a5a3c" },
  ],
  details: ["hex", "oklch"],
  text: { uppercase: true },
  layout: { background: "#1a1714" },
  contrast: { textLight: "#f6f3ed" },
},
}
```

*Output: The FASHION brand palette as a looping reveal — each colour expands to show its hex + oklch, then collapses.*
