# Motion Design System

**A template for defining how a brand behaves in time.**

A brand guideline says how things look. This document says how they *move* —
so that every video, animation, and transition made by anyone (or any agent)
on your team feels like it came from one hand.

Worked example values throughout are from the NoInfra creative system.
Replace them with yours; keep the structure.

**Contents**

1. [Intent](#1-intent)
2. [Timing](#2-timing)
3. [Choreography](#3-choreography)
4. [Signature moments](#4-signature-moments)
5. [Sound & voice](#5-sound--voice)
6. [Composition & formats](#6-composition--formats)
7. [Governance](#7-governance)
8. [Implementation notes](#8-implementation-notes)

---

## 1. Intent

Name the mood before any number exists. Use axes that vary **independently**
— if two of yours always move together, you have one axis and a synonym.
Each axis must decide a different set of rules further down this document.

| Axis | Position | Reading | Governs |
| --- | --- | --- | --- |
| restrained ↔ energetic | 0.28 | Confident, never urgent. The product is the news; the motion is not. | §2 durations |
| sparse ↔ dense | 0.24 | One thing enters at a time. A stagger is a decision, not a default. | §3 choreography |
| mechanical ↔ organic | 0.68 | Everything eases; nothing floats. | §2 easing |

**Test your axes:** describe motion that is high on one and low on another.
If you cannot picture *restrained but dense* — many small things, moving
calmly — the axes are not independent.

**One intent per asset type.** A product launch and a feature explainer are
not the same mood:

| Asset type | Intent |
| --- | --- |
| Launch | Proof over spectacle. The product does the talking. |
| Tutorial | Calm and legible. Nothing moves that isn't being explained. |
| Ad | One idea, stated fast, held long enough to read. |

**The never-list.** Write down what this brand's motion never does, e.g.:
never bounces for delight, never scales text on entry, never moves two focal
elements at once, never uses linear for anything that isn't progress.

---

## 2. Timing

### Duration roles

Three durations, for everything. The names are **roles**, not sizes — the
question is never "how many milliseconds", it is "which of the three things
is happening":

```ts
motion: {
  shortMs: 160,   // a state changes: a row activates, a value ticks
  mediumMs: 360,  // an element enters or leaves
  longMs: 720,    // a scene changes
}
```

- Pick your three by feel, then don't round them to 200/400/800 — slightly
  odd numbers are recognisably *yours* and stop people reaching for defaults.
- The moment there is a fourth duration there are nine, and every timing
  becomes a fresh decision made under deadline.

### Easing set

A closed set of curves, with usage rules:

```ts
easeOut:   "cubic-bezier(0.22, 1, 0.36, 1)",   // anything arriving
easeInOut: "cubic-bezier(0.65, 0, 0.35, 1)",   // moving between two known states
// linear                                       // progress rails and counters ONLY
```

- **Arriving** uses `easeOut` — it launches fast and settles, so it reads as
  "arrived" rather than "stopped".
- **Between two known states** uses `easeInOut`.
- **Nothing floats**: no curve slower than easeInOut, no long tails.
- Overshoot is a spice: at most once per video.

### Frames, not milliseconds

Renderers think in frames. The tokens compile, per spec:

```ts
const f = (ms: number) => Math.round((ms / 1000) * spec.fps);
// at 30fps: shortMs = 5 frames · mediumMs = 11 · longMs = 22
```

The token is the decision; the frame count is derived. A scene never
hardcodes a duration.

### Stagger

A stagger between siblings is `shortMs`, never more. Longer reads as a
sequence of separate events, which is a choreography decision (§3), not a
stagger.

---

## 3. Choreography

How elements share the frame in time.

- **Entry grammar.** Elements arrive with a 16px offset + fade, over
  `mediumMs`, on `easeOut`. Never scale text on entry — scaling type makes it
  briefly unreadable and reads as a template.
- **Exit grammar.** Leaving is faster than arriving — `shortMs` — because an
  exit should never compete with what arrives next.
- **Direction has meaning.** Things enter from where they conceptually come
  from: server responses from the right, user actions from below, system
  chrome from the top.
- **One thing at a time.** Only one element enters per beat unless the group
  is a deliberate stagger.
- **Hierarchy of motion.** The focal element moves last and settles longest;
  supporting elements clear the way first.

---

## 4. Signature moments

The recurring sequences that make separate videos feel like one brand.
Specify them exactly; they are the brand's handwriting.

- **The logo reveal.** Define the exact sequence (ours: logo enters on
  `easeOut`, holds one beat, tagline lands — the *beat* is the signature; the
  tagline must not arrive with the logo).
- **The end frame.** Identical in every video: hold the last product state
  for `longMs` → hard cut to the plate (no dissolve) → logo, beat, tagline →
  hold ≥ `longMs` before the final frame. CTA label and URL are fixed strings,
  never improvised.
- **The transition family.** One way scenes change (ours: a `longMs`
  crossfade with a 16px push in the direction of the story). Not three ways.
  One.

---

## 5. Sound & voice

The brand should sound like one person, whoever made the video.

- **One narration voice.** Provider and voice ID pinned: `_______` (fill in).
  The voice is a token like a color is.
- **Pacing.** Narration is written per scene and read verbatim. If a line
  cannot be said comfortably in one breath inside the scene's duration, the
  scene is too short or the line is too long — catch it by reading, before
  animating.
- **Music policy.** When music is allowed, at what level relative to VO, and
  the genre/energy range that matches §1 intent.
- **SFX policy.** Default: none. Interface sounds only when the product
  itself makes them.
- **Mix levels.** VO target, music bed target, and the ducking rule, stated
  in dB so exports are consistent.

---

## 6. Composition & formats

### Export specs

Declared once; safe areas decide layout, not the other way round.

| Spec | Size | FPS | Duration | Safe area (t/r/b/l) |
| --- | --- | --- | --- | --- |
| Launch landscape | 1920×1080 | 30 | 45s | 72 / 96 / 92 / 96 |
| Short-form vertical | 1080×1920 | 30 | 30s | 160 / 72 / 220 / 72 |
| Feed square | 1080×1080 | 30 | 20s | 72 / 72 / 96 / 72 |

### Why those safe areas exist

The vertical numbers are not aesthetic: **the platform draws UI over your
video.** The bottom ~220px is the caption block and progress bar; the right
edge is the like/comment/share rail; the top ~160px is platform chrome.
Collect the current overlays from each target platform *before* designing
the first frame.

### Attention

Eyes land upper-left-of-centre first and fall to the caption zone second.
Put the focal element where attention already goes; don't spend motion
dragging attention somewhere unnatural.

### Whitespace

One focal point per frame; most of the frame deliberately empty. A video
frame is on screen for seconds — if two things compete, neither is read.
The frame is not a poster.

### Captions

Lower third, inside the safe area, high contrast, on at default (most
short-form is watched muted). Captions are part of composition, not a
post-export afterthought.

### The 9:16-inside-16:9 rule

Design the landscape master with the story inside the centre 9:16 column —
title, proof, CTA stacked in the column, air on the wings. The vertical
version becomes a crop instead of a re-layout. One master, every channel.

---

## 7. Governance

What lets other people — and agents — use the system without you in the room.

### Copy guardrails, as data

```ts
copyGuardrails: {
  requiredPublicBranding: ["NoInfra"],
  allowedLegalBranding: ["KriyAI, Inc."],
  avoidTerms: ["generic AI platform", "AI employee"],
  unsupportedClaims: [
    "revenue claims", "customer count claims",
    "guaranteed uptime claims", "unverified pricing",
  ],
}
```

Written as data rather than prose so a generating agent has no room to
drift: the only legal moves are the ones the system exports.

### Review checklist

Run before any export ships:

- [ ] No brand drift (names, marks, colors, type).
- [ ] No unsupported claims.
- [ ] CTA routes to the intended path.
- [ ] Screens use current design tokens.
- [ ] Captions readable inside every target's safe area.
- [ ] Export dimensions match the selected channel.
- [ ] Motion uses only §2's durations and curves.
- [ ] The end frame matches §4 exactly.

### Naming & versioning

Storyboards, briefs and renders live in version control beside the product.
A storyboard is reviewable in a pull request; `_final_FINAL.mov` is a smell
the system exists to eliminate.

### Reduced motion

Every surface honors `prefers-reduced-motion`: entries become appearances,
loops become stills. State it here so no one relitigates it per project.

---

## 8. Implementation notes

This template is tool-agnostic. Ours is implemented as five TypeScript files
in the product repo (`theme.ts`, `specs.ts`, `storyboards.ts`, `brief.ts`,
`components.tsx`) rendered with Remotion, with anime.js for fine motion and
Figma as the source of truth for frame design. Storyboards are typed objects
— scene kind, duration, headline, caption, visual direction, narration — so
they can be diffed, reviewed, and handed to a renderer or an agent without
ambiguity.

Build order, if you are starting from zero: §2 timing (everything imports
it) → §6 specs (dimensions decide layout) → storyboards → components → brief.

---

*From "How I make brand videos in code" —
https://www.hribhav.co/experiments/brand-videos-in-code*
