# Transitions

The join's named moves between two scenes, what each draws, its settings and eases, and what the element moves ask of a scene.

A hand-off is the moment one scene becomes the next. The join draws it, not the builders: each move runs frame by frame in headless Chrome from both scenes' real frames, and the video is each scene's own frames with the drawn hand-offs between them. A storyboard scene names its hand-off in `out`, and the last scene's is ignored. With no `out`, it's a cut.

A name is a quick way to say a hand-off and get it drawn, not the limit of what a hand-off can be. Tune a move with `ease`, `dir`, `at`, `color`, and `params`. When no move fits, build the hand-off inside the two scenes (carry an element out, land on the frame the next scene opens with) and cut. The [blocks page](/blocks#handoffs) plays every move here but `cut` and `match`.

## The fields

```json
{ "kind": "roaming-iris", "dur": 0.5, "ease": "in-out", "at": [0.5, 0.42], "params": { "radius": 0.2 }, "notes": "opens on the card" }
```

| Field | Type | Notes |
|---|---|---|
| `kind` | a move | the tables below |
| `dur` | seconds | the overlap; default 0. All but `cut` and `match` take at least 0.1 s |
| `ease` | `linear`, `in`, `out`, `in-out`, `snap`, `expo` | how its progress runs; each move has its own default. `snap` overshoots and settles, `expo` rushes in |
| `dir` | `left`, `right`, `up`, `down` | the way a directed move travels |
| `el` | a `data-el` | required by the element moves |
| `at` | `[x, y]` | a point in the frame, each a share from the top left; default the centre |
| `color` | `#rrggbb` or a palette token | for `dip` and `glow-bloom`; the join reads a token from the style |
| `params` | object | a move's own settings |
| `notes` | string | how the two scenes meet; default `""` |

An overlap plays the end of one scene under the start of the next, so a video runs shorter than its scenes laid end to end, by whole frames. An overlap takes at most half the shorter scene, so no hand-off swallows one.

## Soft moves

| `kind` | Draws | Default ease | `params` | Usual `dur` |
|---|---|---|---|---|
| `cut` | a hard cut | | | 0 |
| `dissolve` | one picture through the other | `in-out` | | 0.3–0.6 |
| `dip` | through `color`, white by default | `in-out` | | 0.15–0.4 |
| `blur-through` | the picture blurs out and the next blurs in | `in-out` | | 0.3–0.5 |
| `glow-bloom` | light blooms at `at` as it dissolves through, in `color`, or the next scene's colour there | `in-out` | | 0.3–0.5 |

## Pushes and reveals

| `kind` | Draws | Default ease | Default `dir` | `params` | Usual `dur` |
|---|---|---|---|---|---|
| `wipe` | a soft edge travels toward `dir` | `in-out` | `left` | | 0.3–0.5 |
| `snap-push` | the next scene shoves the last off, hard edge, small overshoot | `snap` | `up` | | 0.12–0.25 |
| `deck-rise` | the next scene rises as a card; the last sinks back, dims, and peeks above | `out` | `up` | | 0.2–0.35 |
| `hinge-swing` | the next scene swings in like a door hinged on one edge | `out` | `left` | | 0.2–0.35 |
| `thread-ribbon` | a thin wavy line draws across, then widens into a ribbon of the next scene | `in-out` | `right` | `amp` (0.05), `waves` (1.5) | 0.4–0.6 |
| `glare-sweep` | a diagonal band of light sweeps across, the next scene behind it | `out` | | `angle` (30), `width` (0.12) | 0.12–0.3 |
| `flood` | the next scene opens out from the centre | `out` | | | 0.3–0.6 |
| `roaming-iris` | a round window onto the next scene travels from `params.from` to `at` and opens | `in-out` | | `from`, `radius` (0.16) | 0.4–0.6 |

## Depth and force

| `kind` | Draws | Default ease | `params` | Usual `dur` |
|---|---|---|---|---|
| `zoom-through` | the camera pushes into the last frame until the next emerges | `in` | | 0.3–0.6 |
| `point-arrival` | the next scene flies in from a speck at `at` until it fills the frame | `expo` | | 0.35–0.6 |
| `plane-tilt` | the frame tilts back like a card, one white frame, the next swings up flat | `in-out` | `tilt` (78) | 0.4–0.6 |
| `tile-shatter` | the frame breaks into tiles that spin and fall from `at`, the next scene under them | `linear` | `tiles` (14) | 0.4–0.7 |
| `signal-tear` | torn bands and slice shifts glitch one picture into the next | `linear` | | 0.3–0.5 |
| `ripple-warp` | a liquid ripple spreads from `at` and carries the next scene in | `out` | | 0.3–0.5 |

`params.from` defaults to just below the frame, straight under `at`. Both may sit off the frame.

## Element moves

These read a real element's box from the scene's `.boxes.json`, so they need `el`, the `data-el` of something the scene draws. A move that overlaps fails the join when its element is missing, and the error names it.

| `kind` | Draws | Default ease | `params` | Usual `dur` |
|---|---|---|---|---|
| `match` | a cut, or a short dissolve, on a shared element in the same box both sides | `in-out` | | 0 or more |
| `held-object` | the element holds still while the world behind it is swapped | `in-out` | | 0.3–0.5 |
| `mosaic-dive` | the camera dives into one tile of a grid, which becomes the next scene | `in` | `reverse` | 0.35–0.7 |
| `glyph-swallow` | a letter or mark turns and grows past the frame; its fill is the next scene | `in` | `turn` (15) | 0.6–1.1 |
| `lens-portal` | a lens or ring shows the next scene inside it, then the camera rushes through | `in-out` | | 0.4–0.6 |

Each asks something of the two scenes:

- **`match`, `held-object`:** the last scene ends with the element in the box the storyboard gives, and the next starts it there, still. The join reports one that moves more than 8 px across the cut or is missing on one side. After a held object, the next scene owns its exit.
- **`mosaic-dive`:** the last scene ends on the grid with the tile `el`, and the next opens on what that tile showed. With `reverse`, the next scene's tile `el` is what the camera pulls back out of: open on the tile's content and land on the grid.
- **`glyph-swallow`:** end on the mark, still, on a plain ground. The next scene opens on a field of the mark's colour.
- **`lens-portal`:** end with the lens or ring still, roughly square in its box.

## On the command line

`rapid join --transitions` and `rapid video-sheet --transitions` take one per hand-off, `kind[:dur[:dir or el]]`:

```sh
rapid join a.mp4 b.mp4 c.mp4 d.mp4 --out picture.mp4 --transitions "snap-push:0.15:up,cut,glyph-swallow:0.9:mark"
```

A move that carries an ease, a point, a colour, or settings goes as the list in JSON:

```sh
rapid join a.mp4 b.mp4 --out picture.mp4 --style swiss --transitions '[{"kind":"dip","dur":0.2,"color":"accent"}]'
```

## Choosing

- **Plan them as pairs.** The last frame of one scene and the first of the next are designed together. Hold a frame worth seeing through the overlap.
- **Match the scale of the move to the moment.** A quick snap (`snap-push`, `glare-sweep`, `dip`) between two fast scenes, a big move (`glyph-swallow`, `lens-portal`, `mosaic-dive`) where the story turns.
- **Vary them.** One move three times running is a habit, not a hand-off.
- **Count the overlap.** In a video [cut to a track](/docs/music), the beat a hand-off is held to is where the next scene starts: the overlap's first frame.
