---
name: motion-design
description: Professional motion graphics for video content — animations, transitions, title sequences, and time-based visual design for video production.
license: MIT
metadata:
  author: editframe
  version: "2.0"
---

# Motion Design

Motion serves communication, not decoration. Every animation must guide attention and express intent.

## Core concepts

1. Intent — what the viewer should feel determines material and personality.
2. Physics model — material, weight, and force determine timing, deformation, and easing.
3. Attention flow — one focus at a time. Sequence everything.
4. Systematic iteration — broad strokes, then easing, then secondary motion, then polish.

## Core rules

1. One focus at a time. Do not animate unrelated elements at the same time.
2. Intent first. Every animation serves the message.
3. Material consistency. Elements of the same material move in the same way.
4. Exits are faster than entrances (about 30–40% shorter).
5. Respect physics unless the style is intentionally not physical.

## Video-specific notes

- Work in frames, not only milliseconds (24fps, 30fps, 60fps).
- Consider composition duration and pacing for the full sequence.
- Think about viewer distance (mobile vs TV vs cinema).
- Plan for audio sync when it applies.
- Account for export format constraints.

## Editframe mapping

| Motion concept | Editframe mechanism |
|---|---|
| Easing / physics | CSS `animation-timing-function` + `@keyframes` |
| Stagger | `ef-text split="word"` + `--ef-word-index` |
| Progress-driven | `--ef-progress` (0–1, updates every frame) |
| Per-frame procedural | `addFrameTask` on a timegroup |
| Exit timing | `--ef-transition-out-start` |
| Scene overlap | `overlap="1s"` on a parent `ef-timegroup[mode="sequence"]` |

`addFrameTask` callbacks must be a pure function of `ownCurrentTimeMs`. Do not use `Date.now()` or `Math.random()`. Renders must be deterministic.


## Editframe implementation

# Implementing Motion in Editframe

The motion design principles in this skill apply directly to Editframe's composition system. This reference maps each concept to the specific Editframe tools that implement it.

---

## Easing and Physics → CSS `animation-timing-function`

The material physics model translates directly to CSS easing curves. Apply them to the `animation` shorthand on any element.

```html
<!-- Glass: clean entrance, minimal overshoot -->
<ef-text style="animation: 400ms title-enter both; animation-timing-function: cubic-bezier(0, 0.55, 0.45, 1)">
  Professional Title
</ef-text>

<!-- Rubber: bouncy entrance for playful brands -->
<ef-text style="animation: 600ms logo-bounce both; animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55)">
  Fun Brand
</ef-text>

@keyframes title-enter {
  from { transform: translateY(20px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

@keyframes logo-bounce {
  from { transform: scale(0.8); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}
```

Add a new section after the material reference table:

---

## Rhythm Through Variation

**Anti-pattern:** All animations using identical duration and easing creates mechanical, monotonous motion.

**Rule:** Within any scene, vary at least ONE of: duration, easing, or delay pattern.

**For keyboard/command-driven products (Linear, Raycast, etc.):**
- Primary actions: 200-300ms, sharp easing (`cubic-bezier(0.55, 0, 1, 0.45)`) — feels responsive
- State transitions: 400-500ms, smooth easing — shows the system working
- Reveals/entrances: 500-600ms, gentle easing — gives content room to land

```html
<!-- Wrong: monotonous -->
<ef-text style="animation: 0.6s enter ease-out">Action</ef-text>
<ef-text style="animation: 0.6s enter ease-out">Result</ef-text>

<!-- Right: rhythm through variation -->
<ef-text style="animation: 0.25s snap cubic-bezier(0.55, 0, 1, 0.45)">⌘K</ef-text>
<ef-text style="animation: 0.5s reveal ease-out 0.15s">Issue created</ef-text>
```

---

## Stagger → `ef-text` `split` and `--ef-word-index`

The attention sequencing principle — one focus at a time — is implemented through text splitting and CSS variable stagger delays.

```html
<!-- Word-by-word reveal with stagger -->
<ef-text
  split="word"
  class="text-white text-4xl"
  style="animation: 0.5s word-in both; animation-delay: calc(var(--ef-word-index) * 80ms)"
>Your message builds word by word</ef-text>

<!-- Character-by-character (typewriter style) -->
<ef-text
  split="char"
  class="text-white text-3xl font-mono"
  style="animation: 0.1s char-in both; animation-delay: calc(var(--ef-char-index) * 40ms)"
>LOADING...</ef-text>

<!-- Line-by-line with organic variation using --ef-seed -->
<ef-text
  split="line"
  class="text-white text-2xl"
  style="animation: 0.6s line-in both; animation-delay: calc(var(--ef-line-index) * 150ms); animation-timing-function: cubic-bezier(0, 0.55, calc(0.45 + var(--ef-seed) * 0.1), 1)"
>First line
Second line
Third line</ef-text>

@keyframes word-in  { from { transform: translateY(18px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes char-in  { from { opacity: 0; }                               to { opacity: 1; }              }
@keyframes line-in  { from { transform: translateY(12px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
```

**Available CSS variables on split elements:**

- `--ef-word-index` — 0-based index of this word in its parent
- `--ef-char-index` — 0-based index of this character
- `--ef-line-index` — 0-based index of this line
- `--ef-stagger-offset` — total number of siblings (for inverse stagger: `calc((var(--ef-stagger-offset) - var(--ef-word-index)) * 80ms)`)
- `--ef-seed` — stable random value per element, useful for organic variation

---

## Progress-Driven Animation → `--ef-progress`

`--ef-progress` updates every frame to the current playback position (0–1) of its timegroup. This drives any CSS property as a continuous function of time.

```html
<!-- Bar that fills with time -->
<ef-timegroup mode="fixed" duration="10s" class="w-full h-2 bg-slate-700">
  <div class="h-full bg-blue-400" style="width: calc(var(--ef-progress) * 100%)"></div>
</ef-timegroup>

<!-- Color that shifts from cool to warm -->
<ef-timegroup mode="fixed" duration="8s" class="w-full h-full"
  style="background: hsl(calc(220 - var(--ef-progress) * 150), 70%, 50%)">
</ef-timegroup>

<!-- Counter that counts up -->
<ef-timegroup mode="fixed" duration="5s" id="counter-scene">
  <div id="count" class="text-white text-6xl font-bold">0</div>
</ef-timegroup>
<script>
  const scene = document.getElementById('counter-scene');
  const count = document.getElementById('count');
  scene.addFrameTask((ownCurrentTimeMs, durationMs) => {
    const progress = ownCurrentTimeMs / durationMs;
    count.textContent = Math.floor(progress * 1000000).toLocaleString();
  });
</script>
```

**Other available time variables:**

- `--ef-duration` — element's total duration as a CSS time value (e.g., `"8s"`)
- `--ef-transition-duration` — overlap duration for scene transitions
- `--ef-transition-out-start` — when fade-out should start (use as `animation-delay` for exits)

---

## Per-Frame Procedural Animation → `addFrameTask`

For animations that can't be expressed with CSS — particle systems, generative graphics, data visualization, physics simulations — `addFrameTask` runs a callback every frame with the current time.

```html
<ef-timegroup mode="fixed" duration="6s" id="scene" class="w-full h-full bg-slate-900">
  <canvas id="canvas" class="absolute inset-0 size-full"></canvas>
</ef-timegroup>

<script>
  const scene = document.getElementById('scene');
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  scene.addFrameTask((ownCurrentTimeMs, durationMs) => {
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    const progress = ownCurrentTimeMs / durationMs;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Example: lines connecting to a center point, growing with progress
    const cx = canvas.width / 2;
    const cy = canvas.height / 2;
    const count = Math.floor(progress * 60);

    for (let i = 0; i < count; i++) {
      const angle = (i / 60) * Math.PI * 2;
      const radius = 200 + Math.sin(i * 0.5 + ownCurrentTimeMs * 0.001) * 40;
      ctx.beginPath();
      ctx.moveTo(cx, cy);
      ctx.lineTo(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius);
      ctx.strokeStyle = `rgba(99, 179, 237, ${0.2 + progress * 0.5})`;
      ctx.lineWidth = 1;
      ctx.stroke();
    }
  });
</script>
```

**Key rules for `addFrameTask`:**

- The callback receives `(ownCurrentTimeMs, durationMs)` — local time, not global
- Always resize the canvas inside the callback (`canvas.width = canvas.offsetWidth`) — this clears it
- The callback runs on every frame during rendering, so it must be a pure function of `ownCurrentTimeMs`
- No `Date.now()`, `Math.random()`, or any non-deterministic values — renders must be reproducible

---

## Overlapping Attention Choreography → `overlap` and CSS Delays

Sequence elements with partially overlapping animations to create natural rhythm. The `overlap` attribute on `ef-timegroup` creates shared time between adjacent scenes; staggered `animation-delay` sequences elements within a scene.

```html
<!-- Within-scene sequencing: logo → headline → subhead -->
<ef-timegroup mode="contain" duration="4s" class="absolute w-full h-full">
  <!-- Logo arrives first -->
  <ef-image src="logo.png" class="absolute top-8 left-8 w-32"
    style="animation: 0.5s enter-down both 0s"></ef-image>

  <!-- Headline starts before logo finishes (overlap 60%) -->
  <ef-text class="absolute top-1/3 left-8 text-white text-5xl font-bold"
    style="animation: 0.6s enter-up both 0.3s"></ef-text>

  <!-- Subhead follows headline -->
  <ef-text class="absolute top-1/2 left-8 text-white/70 text-2xl"
    style="animation: 0.5s enter-up both 0.7s">Supporting text</ef-text>
</ef-timegroup>

@keyframes enter-down { from { transform: translateY(-16px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes enter-up   { from { transform: translateY(16px);  opacity: 0; } to { transform: translateY(0); opacity: 1; } }
```

**Between-scene sequencing using `overlap`:**

```html
<!-- 1s overlap between scenes creates shared time for crossfade -->
<ef-timegroup mode="sequence" overlap="1s">
  <ef-timegroup mode="contain" class="absolute w-full h-full"
    style="animation: 1s fade-out var(--ef-transition-out-start) both">
    <!-- Scene A content -->
  </ef-timegroup>
  <ef-timegroup mode="contain" class="absolute w-full h-full"
    style="animation: 1s fade-in both">
    <!-- Scene B content -->
  </ef-timegroup>
</ef-timegroup>
```

---

## Exit Animations → `--ef-transition-out-start`

Exits should be shorter than entrances (30–40% shorter). Use `--ef-transition-out-start` to trigger exit animations at precisely the right moment, regardless of scene duration.

```html
<ef-timegroup mode="contain" duration="6s" class="absolute w-full h-full"
  style="animation: 1s fade-out var(--ef-transition-out-start) both">

  <!-- Elements exit before the scene ends (staggered out) -->
  <ef-text class="absolute bottom-8 text-white text-4xl"
    style="animation: 0.4s exit-down var(--ef-transition-out-start) both">Headline</ef-text>
  <ef-text class="absolute bottom-4 text-white/70 text-xl"
    style="animation: 0.4s exit-down calc(var(--ef-transition-out-start) - 0.1s) both">Subhead</ef-text>
</ef-timegroup>

@keyframes exit-down  { from { transform: translateY(0); opacity: 1; } to { transform: translateY(16px); opacity: 0; } }
@keyframes fade-out   { from { opacity: 1; } to { opacity: 0; } }
```

`--ef-transition-out-start` is set automatically when `overlap` is used on the parent sequence. For scenes without a sequence parent, it equals `--ef-duration - <overlap>`.

---

## React Implementation

In React, apply animations as inline `style` props:

```tsx
import { Timegroup, Text } from "@editframe/react";

// Word stagger
<Text
  split="word"
  className="text-white text-4xl font-bold"
  style={{
    animation: "0.5s word-in both",
    animationDelay: "calc(var(--ef-word-index) * 80ms)"
  }}
>
  Your message here
</Text>

// Progress-driven via addFrameTask on ref
import { useRef, useEffect } from "react";

const ProgressBar = () => {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const bar = el.querySelector('.bar') as HTMLElement;
    el.addFrameTask((t, d) => {
      bar.style.width = `${(t / d) * 100}%`;
    });
  }, []);
  return (
    <Timegroup ref={ref} mode="fixed" duration="5s" className="w-full h-2 bg-slate-700">
      <div className="bar h-full bg-blue-400 transition-none" />
    </Timegroup>
  );
};
```

## Intent

# Intent → Strategy

## Core Concept

**Message + Emotion → Motion Characteristics**

Every animation starts with intent. Before choosing timing or easing, determine what the viewer should feel and remember.

## The Intent Framework

### 1. Extract the Core Message

What's the single most important thing?

**Good intent statements:**
- "User action succeeded, continue with confidence"
- "This content is important, pay attention"
- "Loading is happening, please wait briefly"
- "These items are related and sequential"

**Bad intent statements:**
- "Make it look cool" (no communication goal)
- "Add some animation" (no purpose)
- "Fade in the elements" (mechanism, not intent)

### 2. Determine Target Emotion

The emotion directly maps to motion characteristics:

| Emotion | Timing | Easing | Material | Exaggeration |
|---------|--------|--------|----------|--------------|
| Playful | Fast (250ms) | Bounce | Rubber | High (120%) |
| Confident | Medium (400ms) | Smooth | Metal/Glass | Low (102%) |
| Calm | Slow (800ms) | Gentle | Paper/Wood | Minimal (101%) |
| Urgent | Very fast (200ms) | Sharp | Stone/Metal | None |
| Premium | Slow (600ms) | Fluid | Leather/Glass | Subtle (103%) |
| Friendly | Medium (350ms) | Slight bounce | Plastic | Moderate (105%) |

### 3. Context Modifiers

**Viewing Context:**
- Social media (mobile): Fast (200-400ms), attention-grabbing
- Explainer video: Medium (500-800ms), clear and readable
- Cinematic: Slow (1000-1600ms), dramatic
- Ads: Fast to medium, hook within 3 seconds

**Content Type:**
- Tutorial: Slower, more explanatory
- Entertainment: Fast-paced, energetic
- Documentary: Medium, measured
- Promotional: Fast, exciting

**Viewing Frequency:**
- One-time narrative: Can be longer, more detailed
- Looping content: Must loop seamlessly, can't become annoying
- Repeated branding: Very brief, memorable
- Background ambient: Subtle, non-distracting

### 4. Communication vs Decoration Test

**Ask:** If I remove this animation, does the message weaken?

**Communication** (keep):
- Directs viewer attention to key information
- Shows relationships between concepts
- Emphasizes important moments
- Guides narrative flow
- Clarifies transitions between ideas

**Decoration** (remove):
- Makes things "look pretty" without purpose
- Distracts from core message
- Adds time without adding clarity
- Becomes tiresome on repeated viewing

## Mapping Intent to Physics Model

Once intent is clear, it determines material selection:

**Intent:** User succeeded, feel confident and rewarded
→ **Emotion:** Confident + slight celebration
→ **Material:** Glass with slight rubber (mostly rigid, tiny bounce)
→ **Physics:** 400ms, 5% overshoot, ease-out

**Intent:** Loading in progress, maintain attention without anxiety
→ **Emotion:** Calm, patient
→ **Material:** Liquid (continuous, flowing)
→ **Physics:** 1400ms loop, ease-in-out, smooth

**Intent:** Error occurred, immediate attention needed
→ **Emotion:** Urgent, alerting
→ **Material:** Metal (sharp, immediate)
→ **Physics:** 400ms, sharp shake, no bounce

## Output Format

Before implementing any animation, write:

```
Intent: [What should viewer remember/feel/do?]
Emotion: [Target feeling]
Material: [Physical metaphor]
Exaggeration: [Subtle/Moderate/High]
Context: [Platform, frequency, user state]

Result: [Concrete motion characteristics]
```

**Example:**

```
Intent: Emphasize key statistic in explainer video
Emotion: Confident, clear
Material: Glass with 5% rubber bounce
Exaggeration: Subtle (103%)
Context: Explainer video, one-time viewing, general audience

Result:
- Duration: 600ms (18 frames at 30fps)
- Scale: 1 → 1.03 → 1
- Position: Slide in from right (60px)
- Easing: ease-out
```

## Common Intent Patterns for Video

### Narrative Flow Intents

**"Introducing new section"**
→ Clear transition (500-800ms), wipe or dissolve
→ Material: Paper or glass (clean, professional)

**"Key point emphasis"**
→ Scale + position (400-600ms), draws eye
→ Material: Stone (substantial) or metal (sharp)

**"Supporting information"**
→ Subtle fade-in (300-400ms), doesn't steal focus
→ Material: Paper (light, secondary)

### Brand/Title Intents

**"Logo reveal"**
→ Memorable entrance (800-1200ms), can be playful or serious
→ Material: Rubber (playful) or glass (professional)

**"Title card"**
→ Clear, readable (600-1000ms), establishes hierarchy
→ Material: Depends on brand personality

**"End card/CTA"**
→ Attention-grabbing (500-800ms), clear next action
→ Material: Metal (urgent) or glass (confident)

### Transition Intents

**"Scene change, different context"**
→ Clear break (400-600ms), wipe or cut
→ Material: Metal (sharp) or stone (definitive)

**"Scene change, same context"**
→ Smooth flow (600-800ms), dissolve or fade
→ Material: Liquid (flowing) or paper (gentle)

**"Time passage"**
→ Dissolve or clock-like (800-1200ms)
→ Material: Liquid (smooth, continuous)

## Anti-Patterns

### Intent Drift

Starting with clear intent but adding motion that contradicts it:

**Intent:** "Quick confirmation without disrupting workflow"
**Bad implementation:** 2-second celebration animation with particles

The implementation violated the intent (quick, non-disruptive).

### Multiple Simultaneous Intents

Trying to communicate several things at once:

**Bad:** Sidebar slides in WHILE content fades in WHILE header animates
**Result:** Viewer doesn't know where to look, misses all three messages

**Fix:** Sequence them. One intent at a time.

### Implied vs Stated Intent

**Implied:** "Make the button feel more premium"
**Stated:** "User should feel confident their payment is secure"

Always state intent explicitly. Implied intents drift toward decoration.

## Physics model

# Physics Model

## Core Concept

**Material + Weight + Force = Motion Profile**

Objects have physical properties that determine how they move. Express these through timing, deformation, and easing curves.

## Quick Reference

### Context Base Durations

- **Social media (mobile)**: 200-400ms (fast-paced, attention-grabbing)
- **Explainer videos**: 500-800ms (clear, readable)
- **Cinematic**: 1000-1600ms (dramatic, luxurious)
- **Transitions**: 300-600ms (context-dependent)

### Frame Rate Considerations

At **24fps**: 1 frame = ~42ms (round to multiples of 42ms)  
At **30fps**: 1 frame = ~33ms (round to multiples of 33ms)  
At **60fps**: 1 frame = ~17ms (round to multiples of 17ms)

Use frame-aligned timing for smooth motion.

### Weight Multipliers

- **Heavy**: 1.5-2.0× base duration (large text blocks, full-screen graphics)
- **Medium**: 1.0× base duration (title cards, standard elements)
- **Light**: 0.5-0.7× base duration (small icons, decorative elements)

### Duration Calculation Formula

```
Duration = Material.base × WeightMultiplier × DistanceFactor

Example:
Paper title card (800ms base) × Medium weight (1.0×) × 200px movement (2.0×)
= 800 × 1.0 × 2.0 = 1600ms

At 30fps: 1600ms ÷ 33ms = ~48 frames
At 24fps: 1600ms ÷ 42ms = ~38 frames
```

## Material Properties (Source of Truth)

Every animation chooses a material metaphor. The material determines all motion characteristics.

### Complete Material Matrix

| Material | Base Duration | Deformation | Bounce | Friction | Density |
|----------|---------------|-------------|---------|----------|---------|
| Feather  | 2000ms        | 80%         | 0%      | Low      | Very low |
| Paper    | 800ms         | 30-40%      | 10%     | Medium   | Low |
| Leather  | 500ms         | 20-30%      | 15%     | High     | Medium |
| Rubber   | 600ms         | 60-80%      | 80%     | High     | Medium |
| Wood     | 500ms         | 5-10%       | 20%     | Medium   | Medium |
| Plastic  | 350ms         | 10-20%      | 30%     | Low      | Medium |
| Glass    | 400ms         | 0%          | 25%     | Low      | Medium-high |
| Metal    | 300ms         | 0-5%        | 5%      | Medium   | High |
| Stone    | 600ms         | 0%          | 5%      | High     | Very high |
| Liquid   | 1400ms        | 100%        | 0%      | Variable | Low |

### Deriving Motion from Material

**Material determines timing:**
```
Playful logo (rubber):
  Duration: 600ms (rubber base)
  Deformation: scaleY(0.7) scaleX(1.3)
  Bounce: Returns past resting point by 80%
  Use: Fun brands, celebration moments
  
Professional title card (glass):
  Duration: 400ms (glass base)
  Deformation: None (rigid)
  Bounce: Minimal overshoot (5%)
  Use: Corporate, technical content
```

**Consistency rule:** All elements of the same material move similarly throughout the composition.

## Weight Scaling

Weight multiplies base duration and affects deformation:

### Weight Categories

**Light (0.5-0.7× base):**
- Small tooltips, badges, chips
- Fast start, floaty motion
- Minimal deformation (barely compresses)
- Example: Paper badge = 800ms × 0.6 = 480ms

**Medium (1.0× base):**
- Standard UI elements, cards, buttons
- Balanced motion
- Standard material deformation
- Example: Paper card = 800ms × 1.0 = 800ms

**Heavy (1.5-2.0× base):**
- Large modals, full-page transitions
- Slow start (inertia), momentum carries
- Increased deformation (more impact)
- Example: Paper modal = 800ms × 1.8 = 1440ms

### Volume Conservation

When objects deform, volume stays constant:

```
Volume = Width × Height = constant

Normal:    scaleX(1.0)  × scaleY(1.0)  = 1.0
Squashed:  scaleX(1.25) × scaleY(0.8)  = 1.0
Stretched: scaleX(0.85) × scaleY(1.3)  = 1.1 (close enough)
```

Compress one axis → expand the other proportionally.

## Force Applied (Easing)

Easing curves represent forces acting on objects:

### Gravity

**Falling (ease-in - accelerating):**
```
Object falls, gravity pulls harder over time
cubic-bezier(0.55, 0, 1, 0.45)
```

**Rising (ease-out - decelerating):**
```
Object thrown upward, gravity slows it
cubic-bezier(0, 0.55, 0.45, 1)
```

### Entrances vs Exits

**Entrance (ease-out):**
Object enters viewport, needs to decelerate to stop
```
Starting with momentum, slowing to rest
0% → fast → slow → 100%
```

**Exit (ease-in):**
Object accelerates away, doesn't need to stop
```
Starting at rest, speeding up to leave
0% → slow → fast → 100%
```

**Within-screen (ease-in-out):**
Object moves from A to B on screen, must start and stop
```
Starting at rest, accelerating, then decelerating
0% → slow → fast → slow → 100%
```

### Spring Physics

**For elastic materials (rubber, plastic):**

```javascript
// Tight spring (professional UI)
stiffness: 300
damping: 30
= Quick snap, 1-2 oscillations

// Loose spring (playful UI)  
stiffness: 100
damping: 15
= Gentle motion, 3-4 oscillations

// Critically damped (precise)
stiffness: 200
damping: 28 (2 × √stiffness)
= Fastest approach without overshoot
```

Maps to cubic-bezier:
```
Tight spring:   cubic-bezier(0.68, -0.1, 0.265, 1.1)
Loose spring:   cubic-bezier(0.68, -0.55, 0.265, 1.55)
Critically damped: cubic-bezier(0.36, 0, 0.66, 1)
```

## Distance Scaling

Duration scales with distance traveled:

```
Base distance: 100px
Base duration: Material.base

Distance factor = actualDistance / 100px

Final duration = Material.base × Weight × DistanceFactor
```

**Example:**
```
Paper card (800ms base)
Medium weight (1.0×)
Moving 250px (2.5×)
= 800 × 1.0 × 2.5 = 2000ms
```

**Practical limits:**
- Don't scale linearly beyond 3× distance (feels too slow)
- Use √distance for very long movements
- Example: 400px → use √4 = 2× instead of 4×

## Friction and Drag

**High friction (rough surfaces):**
```
Slow, constant deceleration
Longer timing
cubic-bezier(0.4, 0.1, 0.6, 0.9)
Use for: Dragging, scrubbing, sliders
```

**Low friction (smooth surfaces):**
```
Maintains speed, sharp stop
Faster timing
cubic-bezier(0.1, 0.7, 0.3, 1)
Use for: Swipes, throws, momentum scroll
```

## Bounce Dynamics

**Realistic bounce sequence:**
```
Drop from height H, duration D

Bounce 1: H × 100%, D × 100%
Bounce 2: H × 70%,  D × 70%
Bounce 3: H × 49%,  D × 49%
Bounce 4: H × 34%,  D × 34%
```

Each bounce is ~70% of previous height and duration.

**Simplified UI bounce (single overshoot):**
```javascript
// Button scale bounce
element.animate([
  { transform: 'scale(0)' },
  { transform: 'scale(1.08)' },  // 8% overshoot
  { transform: 'scale(1)' }
], {
  duration: 400,
  easing: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
});
```

## Inertia and Momentum

**Heavy objects resist changes:**

```
Phase 1: Overcome inertia (slow start)
  Duration: 40% of total
  Easing: Slow at start
  
Phase 2: Momentum builds (constant speed)
  Duration: 30% of total
  Easing: Linear
  
Phase 3: Decelerate to stop (slow end)
  Duration: 30% of total
  Easing: Slow at end
```

**Direction changes require momentum decay:**
```
Moving right → stop → move left

Cannot change instantly. Must:
1. Decelerate to stop (200ms, ease-in)
2. Pause (50-100ms, shows momentum lost)
3. Accelerate new direction (250ms, ease-out)
```

## Implementation Pattern

**Determine material, weight, distance:**
```
1. Material (from intent): Glass
2. Weight: Medium (standard button)
3. Distance: 50px
4. Force: Gravity (falling in)

Calculation:
Base: 400ms (glass)
Weight: 1.0× (medium)
Distance: 0.5× (50px vs 100px base)
= 400 × 1.0 × 0.5 = 200ms

Easing: ease-in (falling)
Deformation: 0% (glass doesn't compress)
Bounce: 25% overshoot

Result:
duration: 200ms
transform: translateY(-50px) → translateY(0) → translateY(-12.5px) → translateY(0)
easing: cubic-bezier(0.55, 0, 1, 0.45) for fall, then bounce
```

## Common Animation Patterns for Video

### Title Card Entrance (400-600ms, ease-out)
```
At 30fps (12-18 frames):
opacity: 0 → 1
translateY: 40px → 0
scale: 0.95 → 1
```

### Title Card Exit (300-400ms, ease-in, 30% faster)
```
At 30fps (9-12 frames):
opacity: 1 → 0
translateY: 0 → -30px
```

### Logo Animation (800ms, bounce)
```
At 30fps (24 frames):
scale: 0 → 1.15 → 0.95 → 1
rotation: 0 → 5 → -3 → 0
easing: bounce/elastic
```

### Scene Transition - Wipe (500ms)
```
At 30fps (15 frames):
clipPath: inset(0 0 0 100%) → inset(0 0 0 0%)
easing: ease-in-out
```

### Scene Transition - Cross-dissolve (800ms)
```
At 30fps (24 frames):
Outgoing scene opacity: 1 → 0
Incoming scene opacity: 0 → 1
Overlap: 100% (simultaneous)
```

### Text Reveal - Typewriter
```
Per character: 50ms (1-2 frames at 30fps)
Total for 20 characters: ~1000ms
```

### Emphasis Pulse (600ms)
```
At 30fps (18 frames):
scale: 1 → 1.1 → 1
opacity: 1 → 1 → 1 (maintain)
Used for: Drawing attention to key information
```

## Common Physics Errors

**Error: Ignoring weight**
```
Bad: Full-screen graphic animates in 300ms (too fast for size)
Fix: Apply heavy multiplier: 300ms × 1.8 = 540ms
```

**Error: Violating volume conservation**
```
Bad: scaleX(1.5) scaleY(1.5) on squash (volume increases unnaturally)
Fix: scaleX(1.5) scaleY(0.67) (volume stays constant)
```

**Error: Wrong easing direction**
```
Bad: Title entrance with ease-in (feels like accelerating into wall)
Fix: Title entrance with ease-out (decelerating to rest)
```

**Error: Inconsistent material**
```
Bad: Title card has paper timing (800ms) but metal deformation (2%)
Fix: Use paper deformation (35%) to match timing
```

**Error: Not frame-aligned**
```
Bad: 347ms animation at 30fps (10.4 frames - fractional)
Fix: 330ms (10 frames) or 363ms (11 frames) - whole frame counts
```

## Attention flow

# Attention Flow

## Core Concept

**One Focus at a Time**

Human attention is sequential, not parallel. Motion competes for focus. Never animate unrelated elements simultaneously.

## Quick Reference: Stagger Delays

| Unit | Delay | Use Case |
|------|-------|----------|
| Character | 30-50ms | Text reveals, typing effects |
| Word | 80-120ms | Headline emphasis |
| Line | 200-300ms | Paragraph reveals |
| List item | 50-80ms | Navigation, bullet lists |
| Card | 100-150ms | Grid layouts, galleries |
| Section | 400ms+ | Page sections, major blocks |

## The Attention Rule

**At any given moment, only ONE thing should be moving (or a group moving as a single conceptual unit).**

### Bad (Simultaneous)
```
0ms: Title slides in (500ms)
0ms: Subtitle fades in (500ms)
0ms: Logo animates (500ms)
```
Result: Viewer doesn't know where to look, misses all three.

### Good (Sequential)
```
0ms:   Logo animates (400ms)
200ms: Title slides in (400ms) ← starts before logo finishes
400ms: Subtitle fades in (300ms) ← starts before title finishes
```
Result: Clear attention path, overlapping reduces total time but maintains sequence.

## Stagger: Rhythm Across Elements

**Stagger = delay between identical animations**

Creates rhythm and guides reading order without overwhelming attention.

### Stagger Timing by Granularity

| Unit | Delay | Use Case |
|------|-------|----------|
| Character | 30-50ms | Text reveals, typing effects |
| Word | 80-120ms | Headline emphasis |
| Line | 200-300ms | Paragraph reveals |
| List item | 50-80ms | Navigation, bullet lists |
| Card | 100-150ms | Grid layouts, galleries |
| Section | 400ms+ | Page sections, major blocks |

### Calculating Total Duration

```
Total = (NumItems - 1) × StaggerDelay + ItemDuration

Example: 5 cards, 120ms stagger, 300ms animation
Total = (5-1) × 120 + 300 = 780ms
```

**Keep total under 2 seconds** for UI. Beyond that feels slow.

## Stagger Patterns

### Sequential (Linear)
```
Item 1: 0ms
Item 2: 100ms
Item 3: 200ms
Item 4: 300ms
```
Most common. Reads naturally (top→bottom, left→right).

### Cascading (Accelerating)
```
Item 1: 0ms
Item 2: 80ms   (80ms after previous)
Item 3: 140ms  (60ms after previous)
Item 4: 180ms  (40ms after previous)
```
Builds momentum, energetic feel. Use for dramatic reveals.

### Wave (Center-out)
```
Item 1: 100ms  (center)
Item 2: 50ms   (one step out)
Item 3: 150ms  (one step out)
Item 4: 0ms    (edge)
Item 5: 200ms  (edge)
```
Focuses attention on center first, reveals context. Use when center is most important.

### Decelerating (Slowing)
```
Item 1: 0ms
Item 2: 50ms   (50ms after previous)
Item 3: 120ms  (70ms after previous)
Item 4: 220ms  (100ms after previous)
```
Gentle arrival, emphasizes final items. Use for settling into place.

## Reading Order

**Respect natural reading patterns unless intentionally disrupting:**

### Western Reading (Left → Right, Top → Bottom)
```
Grid animation order:
[1] [2] [3]
[4] [5] [6]
[7] [8] [9]
```

### Alternative Orders

**Diagonal (energetic):**
```
[1] [2] [4]
[3] [5] [7]
[6] [8] [9]
```

**Column-by-column (technical/data):**
```
[1] [4] [7]
[2] [5] [8]
[3] [6] [9]
```

**Center-out (focal emphasis):**
```
[5] [2] [4]
[7] [1] [3]
[9] [6] [8]
```

## Overlapping vs Sequential

### Full Sequential (Clear but Slow)
```
Item 1: 0-300ms
Item 2: 300-600ms    ← starts when Item 1 ends
Item 3: 600-900ms
Total: 900ms
```

### Overlapping (Faster, Still Clear)
```
Item 1: 0-300ms
Item 2: 120-420ms    ← starts at 40% of Item 1
Item 3: 240-540ms
Total: 540ms (40% faster)
```

**Optimal overlap: 30-50%** of item duration.

Too much overlap → feels simultaneous, loses sequence.
No overlap → feels sluggish, artificially delayed.

## Grouping: Treating Multiple as One

**Exception to "one focus" rule:** Elements that form a single conceptual unit can move together.

### Valid Grouping
```
Title card contains:
- Main headline
- Subheadline
- Decorative line

All animate together as one unit.
```

### Invalid Grouping (Should be Separate)
```
Bad: Animating title AND background graphic together
→ These aren't a conceptual unit
→ Should be sequenced (background first, then title)
```

**Test:** Would a viewer naturally perceive these as one element or separate elements?

## Attention Choreography Process

### 1. List All Elements
Identify everything that will move.

### 2. Prioritize by Importance
What must viewer see first, second, third?

### 3. Determine Groups
Which elements are conceptual units?

### 4. Assign Sequence
Create timeline respecting priority.

### 5. Add Stagger Within Groups
Apply appropriate delays for rhythm.

### Example: Explainer Video Opening

**Elements:**
- Brand logo
- Video title
- Subtitle text
- 4 key statistics
- Background graphic

**Priority:**
1. Logo (establishes brand)
2. Title (main message)
3. Subtitle (supports title)
4. Statistics (supporting data)
5. Background (context)

**Timeline at 30fps:**
```
0ms:     Logo animates (600ms / 18 frames)
300ms:   Title slides in (400ms / 12 frames) ← 50% overlap
500ms:   Subtitle fades in (300ms / 9 frames)
700ms:   Stat 1 (300ms / 9 frames)
800ms:   Stat 2 (300ms / 9 frames) ← 100ms stagger
900ms:   Stat 3 (300ms / 9 frames)
1000ms:  Stat 4 (300ms / 9 frames)
1300ms:  Background subtle fade (600ms / 18 frames) ← ambient

Total: 1900ms (~57 frames)
```

## Background Motion Exception

**Ambient/background motion can run during foreground focus:**

```
Foreground: Title animates (400ms) ← PRIMARY FOCUS
Background: Particle drift (continuous) ← ambient, not competing
```

**Requirements for background motion:**
- Much slower than foreground (or continuous loop)
- Very subtle (barely noticeable, 20-30% opacity)
- Clearly secondary (out of focus, low contrast, or peripheral)
- Enhances without distracting
- Never competes with primary narrative elements

## Anti-Patterns

### Simultaneous Unrelated Motion
```
Bad:
- Main title appears
- Background changes
- Lower third animates
All at same time

Fix: Sequence them. Background → wait 200ms → Title → wait 400ms → Lower third
```

### Too Many Staggers
```
Bad: 20 text lines with 200ms stagger = 4000ms (4 seconds!)
Result: Feels sluggish, viewer loses interest

Fix: 
- Reduce stagger to 80ms (1600ms total)
- Or: Group into 3 sets of lines, stagger sets not lines
- Or: Only stagger first 5 lines, remaining fade in together
```

### Ignoring Reading Order
```
Bad: Grid animates right-to-left (against Western reading)
Result: Feels backwards, unnatural

Fix: Animate in reading order unless intentionally surprising
```

### False Grouping
```
Bad: Entire page (50+ elements) animates as one unit
Result: Overwhelming, no guided attention

Fix: Break into logical groups, sequence the groups
```

## Implementation Example

**Text line reveal with stagger (30fps):**

```javascript
// In video composition at 30fps (33ms per frame)
const lines = ['Line 1', 'Line 2', 'Line 3', 'Line 4'];
const staggerFrames = 6;  // 6 frames = ~200ms at 30fps
const durationFrames = 9; // 9 frames = ~300ms at 30fps

lines.forEach((line, i) => {
  const startFrame = i * staggerFrames;
  const endFrame = startFrame + durationFrames;
  
  // Create keyframes for this line
  animateLine(line, {
    startFrame: startFrame,
    endFrame: endFrame,
    easing: 'ease-out'
  });
});

// Total duration: (4-1) × 6 + 9 = 27 frames (~900ms at 30fps)
```

## Attention Budget

**Users have limited attention. Spend it wisely.**

**High attention cost:**
- Large motion (200px+ movement)
- Color changes
- Multiple properties animating
- Long duration (800ms+)

**Low attention cost:**
- Small motion (20px movement)
- Opacity only
- Brief duration (200ms)

**Budget rule:** Reserve high-cost motion for primary actions. Use low-cost for secondary.

## Testing Attention Flow

**Watch the animation with these questions:**

1. Where does my eye go at each moment?
2. Is that where I *should* be looking?
3. Do I understand the sequence of importance?
4. Does anything compete for attention?
5. Is the total duration comfortable?

If any answer is no, revise the sequence.

## Systematic iteration

# Systematic Iteration

## Core Concept

**Broad Strokes → Easing → Secondary → Polish**

Don't perfect details before structure is right. Iterate in phases, each building on the previous.

## The Four Phases

```
Phase 1: Broad Strokes    (40% of time)
    ↓
Phase 2: Easing           (20% of time)
    ↓
Phase 3: Secondary Motion (25% of time)
    ↓
Phase 4: Polish           (15% of time)
```

---

## Phase 1: Broad Strokes (40%)

**Goal: Get the sequence and rhythm right**

### Do
- Basic opacity and position only
- Round timing (200, 300, 500ms)
- Simple stagger (50, 100, 200ms)
- Linear easing (ignore curves for now)
- Test attention flow

### Don't
- Custom easing curves
- Squash & stretch
- Color transitions
- Particle effects
- Precise timing

### Success Criteria
- ✅ Sequence makes sense
- ✅ Attention flows correctly
- ✅ Nothing overlaps wrong
- ✅ Overall rhythm feels approximately right

### Example
```
// Phase 1: Basic structure only
Title card entrance:
  0ms:   opacity: 0, translateY: 40px
  300ms: opacity: 1, translateY: 0
  easing: linear  // ← ignore easing for now

At 30fps: 9 frames
Simple stagger for multiple elements: +100ms each
```

**If broad strokes feel wrong, stop.** Don't proceed to Phase 2. Fix the sequence first.

---

## Phase 2: Easing (20%)

**Goal: Make motion feel natural**

### Do
- Replace linear with appropriate easing
- Fine-tune timing (±50ms adjustments)
- Test at 0.5× and 2× speed
- Round to nearest 10ms when settled

### Easing Selection
- **Entrance:** ease-out (decelerating to rest)
- **Exit:** ease-in (accelerating away)
- **Within-screen:** ease-in-out (smooth start and stop)
- **Never:** linear (except spinners/mechanical)

### Success Criteria
- ✅ Motion feels natural, not robotic
- ✅ No jarring speed changes
- ✅ Comfortable to watch repeatedly
- ✅ Material choice is evident

### Example
```
// Phase 2: Add proper easing
Title card entrance:
  0ms:   opacity: 0, translateY: 40px
  330ms: opacity: 1, translateY: 0  // ← adjusted from 300ms
  easing: ease-out  // ← cubic-bezier(0, 0, 0.2, 1)

At 30fps: 10 frames (frame-aligned)
Stagger refined: +80ms each (was +100ms)
```

---

## Phase 3: Secondary Motion (25%)

**Goal: Add life and personality**

### Do
- Squash & stretch (material-appropriate)
- Anticipation & follow-through
- Scale overshoots (102-105%)
- Background dimming during focus
- Subtle rotation during motion

### Material-Based Deformation

**Playful (rubber) - for fun brands:**
```
Logo bounce entrance:
  0ms:   scale: 0.8
  245ms: scale: 1.08  (70% through animation)
  350ms: scale: 1

At 30fps: ~10 frames
Material: Rubber (energetic overshoot)
```

**Professional (glass) - for corporate:**
```
Title card entrance:
  0ms:   translateY: 40px, scale: 1
  210ms: translateY: 0, scale: 1.02  (70% through)
  300ms: translateY: 0, scale: 1

At 30fps: 9 frames
Material: Glass (subtle overshoot)
```

### Success Criteria
- ✅ Motion has personality
- ✅ Elements feel like they have weight
- ✅ Natural physics evident
- ✅ Not overdone (still serves message)

---

## Phase 4: Polish (15%)

**Goal: Handle edge cases and optimize**

### Do
- Micro-adjustments (±10ms)
- Edge case testing (long text, missing images)
- Performance optimization (will-change)
- Accessibility (prefers-reduced-motion)
- Cross-browser testing

### Edge Cases to Test
- Very long text (does it read in time?)
- Very short text (does timing still work?)
- Different aspect ratios (16:9, 9:16, 1:1)
- Different resolutions (SD, HD, 4K)
- Export format compatibility (H.264, ProRes, WebM)
- Audio sync (if applicable)
- Color space (sRGB, Rec.709, Rec.2020)

### Performance
- Pre-render heavy effects (particles, blur, 3D)
- Use appropriate motion blur (180° shutter for natural look)
- Optimize layer count in compositions
- Ensure smooth playback at target framerate (24/30/60fps)
- Check export file size vs quality tradeoff
- Test on target viewing platform (mobile, TV, cinema)

### Accessibility

For video content intended for diverse audiences:

```
Standard version:
- Full motion with bounces, particles, effects
- Fast-paced stagger patterns

Accessible version (when required):
- Simpler animations (cuts or dissolves)
- Longer durations (easier to follow)
- Reduced flash/strobe effects
- Lower contrast for motion elements
- Maintain core message clarity
```

**Accessibility guidelines:**
- No flashing faster than 3Hz (seizure risk)
- Sufficient contrast for legibility
- Longer read times for critical text
- Alternative static versions available
- Captions/subtitles always available

### Success Criteria
- ✅ Works across all target browsers
- ✅ Performs at 60fps
- ✅ Handles edge cases gracefully
- ✅ Accessible (reduced motion support)
- ✅ Mobile and desktop optimized

---

## Knowing When to Stop

### Animation is Done When
1. ✅ Serves the message clearly
2. ✅ Guides attention intentionally
3. ✅ Feels natural and polished
4. ✅ Performs smoothly (60fps)
5. ✅ Survives 10+ repeated views
6. ✅ Works on target devices
7. ✅ Handles edge cases
8. ✅ Passes accessibility checks

### Animation is Not Done If
- ❌ Tweaking timing by 5ms increments (over-polishing)
- ❌ Adding motion for motion's sake
- ❌ Can't explain why specific values chosen
- ❌ Haven't tested on real devices
- ❌ Feedback says it's distracting

### The 10-View Test

Watch the animation 10 times in a row:
- **Views 1-3:** Notice everything
- **Views 4-6:** Start to see flaws
- **Views 7-10:** Boring vs still pleasant?

**If annoying by view 7, simplify or remove.**

---

## Common Iteration Mistakes

### Mistake 1: Polishing Too Early

```javascript
// Bad: Perfect easing before sequence is right
element.animate([...], {
  duration: 347,  // overly precise
  easing: 'cubic-bezier(0.43, 0.01, 0.22, 0.99)'  // custom curve
});
```

**Sequence is still wrong!** Broad strokes first, always.

### Mistake 2: Not Testing Edge Cases

Only testing:
- Fast computer ✗
- Fast internet ✗
- Perfect data ✗
- Ideal device ✗

**Test worst cases:**
- Slow device
- Slow network
- Missing images
- Very long text
- Rapid interaction

### Mistake 3: Ignoring Feedback Patterns

Multiple people say "too fast" → it's too fast.

Don't defend your choice. Listen to patterns.

### Mistake 4: Over-Engineering

```
// Too complex for a simple title entrance
Title card with:
- 3D rotation on 3 axes
- Color shift through 5 hues
- Particle system
- Light bloom effect
- Depth of field animation
All in 600ms

Viewer gets: overwhelming blur, misses the title text
```

**Simpler is better.** Only add properties that serve the message. Title should be readable above all.

---

## Add vs Remove Decision

### Remove If
- ❌ Distracts from message
- ❌ Slows user progress
- ❌ Boring on 3rd+ view
- ❌ Performance issues
- ❌ Breaks on some devices
- ❌ Violates "one focus at a time"

### Add If
- ✅ Guides attention better
- ✅ Communicates state clearly
- ✅ Adds personality without distraction
- ✅ Makes interaction feel responsive
- ✅ Shows relationships between elements

**Default: Subtract when in doubt.**

---

## Testing at Different Speeds

Good animation works at multiple speeds:

```javascript
// Test at various playback rates
const speeds = [0.5, 1, 1.5, 2];

speeds.forEach(speed => {
  element.animate([...keyframes], {
    duration: baseDuration / speed,
    easing: easing
  });
});
```

**Quality test:**
- **0.5×:** Still feels intentional?
- **1×:** Perfect?
- **2×:** Doesn't break logic?

If it breaks at any speed, timing relationships are wrong.

---

## Getting Feedback at Right Stage

### After Phase 1 (Broad Strokes):
**Ask:** "Does the sequence make sense?"

Don't ask about easing or polish yet. Just sequence.

### After Phase 2 (Easing):
**Ask:** "Does the motion feel natural?"

Now curves matter. But don't ask about details yet.

### After Phase 3 (Secondary):
**Ask:** "Does it have personality?"

Deformation and weight should be evident.

### After Phase 4 (Polish):
**Ask:** "Are there any issues or edge cases?"

Now details matter.

**Don't ask for polish feedback when sequence is wrong!**

---

## Workflow Checklist

```
□ Phase 1: Broad Strokes (40%)
  □ Basic motion working
  □ Sequence is right
  □ Linear easing OK
  □ Round timing values
  
□ Phase 2: Easing (20%)
  □ Natural movement
  □ Correct curves applied
  □ Fine-tuned timing (±50ms)
  □ Material choice evident
  
□ Phase 3: Secondary Motion (25%)
  □ Squash & stretch added
  □ Anticipation on actions
  □ Follow-through on heavy elements
  □ Supporting animations
  
□ Phase 4: Polish (15%)
  □ Edge cases tested
  □ Performance optimized
  □ Accessibility implemented
  □ Cross-browser verified
  □ 10-view test passed
  
□ Done
  □ Serves message clearly
  □ Guides attention
  □ Performs at 60fps
  □ Works on all targets
```

---

## Time Investment Guide

For a typical UI animation project:

**Total time: 8 hours**
- Broad strokes: 3.2 hours (40%)
- Easing: 1.6 hours (20%)
- Secondary: 2 hours (25%)
- Polish: 1.2 hours (15%)

**If you find yourself:**
- Spending 50% on polish → too early, go back to broad strokes
- Spending 10% on broad strokes → rushing, will require rework
- Skipping phases → recipe for weak foundation

Respect the phase distribution. It's optimized for quality with minimum rework.
