description: The foundation motion-craft reference for FlatRender Remotion templates — easing curves and when to reach for each, timing & spacing, the 12 animation principles applied to Remotion, anticipation/overshoot/follow-through/settle, staggering & choreography, secondary motion, spring() vs interpolate(), and the blocking→timing→polish workflow. Use whenever animating ANY element in a template, reviewing motion quality, or deciding how something should enter, move, or leave. Read this BEFORE writing animation code.
---
# Motion design principles (the FlatRender craft floor)
**Linear motion is the sound of an amateur. Almost nothing in a FlatRender template should move at a constant rate.** This skill is the floor every template stands on.
## The one rule everything hangs on
A Remotion frame is **pure**: `frame → pixels`, sampled at an arbitrary `t` (the After Effects mental model — a keyframe graph read at time `t`). The renderer samples frames **out of order and in parallel**.
- Derive every value from `useCurrentFrame()`. If a value can't be, it doesn't belong in the render.
- **Never** `useFrame` (R3F), `Math.random()`, `Date.now()`, `setState`, or `useEffect`-driven motion. For "randomness" use `rand(seed)` from `anim.ts`.
## `spring()` vs `interpolate()` — pick deliberately
| | `interpolate()` | `spring()` |
|---|---|---|
| Who authors the curve | **you** (explicit easing) | **physics** (mass/damping/stiffness) |
| Reach for it when | a value must hit an exact mark on an exact frame — storyboard reveals, crossfades, value remaps, color/blur sweeps | organic entrances, pops, bounces, anything that should "feel" alive |
| The trap | forgetting `extrapolate*: "clamp"` → elements drift off-screen / opacity goes negative | trying to land a value on an exact frame |
**Always combine them** — spring drives the *feel* (0→1), interpolate *remaps* it to real px/units in the layout's own scale:
// or: spring with low damping (config { mass: 0.6, damping: 10, stiffness: 170 })
```
**Follow-through** — drive children from the *same* trigger, delay each, looser spring so they settle after the parent. The biggest "feels professional" upgrade for grouped elements:
> `pick` is the standard per-aspect selector on `useLayout()`. If it isn't on `Layout` yet, add it in `aspect.ts`: `pick: <T,>(wide: T, square: T, tall: T): T => kind === "wide" ? wide : kind === "tall" ? tall : square,`
## 3D motion (`@remotion/three`)
Drive every transform off `useCurrentFrame()` (deterministic under ANGLE) — **never `useFrame`**. Rotation/orbit = `linear` (mechanical); entrances/landings = `spring` with **high mass** for weight. Keep crisp Persian text as a 2D `<AbsoluteFill>` overlay above `<ThreeCanvas>`. Let `StudioEffects` (bloom + DOF + vignette) carry the cinematic polish in one component; tune `camera.fov`/`position.z` per aspect so the subject fills the frame.
## The pro workflow — 5 passes, IN ORDER
Polishing before timing is locked wastes the most time.
1.**Reference** — decide the feel before code; pick style (`../remotion-design-styles/SKILL.md`), type (`../persian-fonts/SKILL.md`), composition (`../remotion-template-composition/SKILL.md`), per-aspect rules (`../remotion-aspect-ratios/SKILL.md`). Write the beat list ("logo in → tagline → 3 features cascade → CTA → out").
4.**Polish** — swap linear for easing/springs; add anticipation + overshoot/settle, follow-through, secondary motion, arcs, squash/stretch; `StudioEffects` for 3D; wire SFX (`../remotion-sound-effects/SKILL.md`) + music sync (`../remotion-music-picker/SKILL.md`) to the locked frames.