--- name: video-hooks description: How to design the scroll-stopping first 1-3 seconds of a FlatRender Remotion template — hook archetypes, pattern interrupts, on-screen text hooks, curiosity gaps, and platform-specific (Instagram/TikTok/YouTube) hook norms — and bake them into the template's opening beats. Use whenever building or reviewing a template's first frames, the cover/first frame, the caption hook layer, or retention pacing of the open. --- # The hook (first 1-3 seconds — where templates are won or lost) On a 9:16 feed the viewer decides **stay or swipe in 2-3 seconds** (TikTok's "3-second rule"; IG rewards 3-sec view rate). YouTube Shorts has **no runway** — open on the most compelling moment. So a FlatRender template doesn't get a polite logo intro: the **first frame is the cover/thumbnail and the hook**, and the first ~45-90 frames (@30fps) must arrest the eye. Everything here is a *pure function of `useCurrentFrame()`* — no `Math.random`/`Date.now`/`useFrame`; use `rand(seed)` from `lib/anim.ts`. Read `../remotion-aspect-ratios/SKILL.md` before positioning a single hook element. ## The frame budget for the open (30fps; use `sec(s)=Math.round(s*fps)`) | Beat | Frames | Job | |---|--:|---| | **f0 — cover** | 1 | Must already read as a finished, intriguing thumbnail. No black/empty frame 0. | | **Pattern interrupt** | 0-12 | One bold motion/sound jolt that breaks the scroll rhythm. | | **Hook text lands** | 6-30 | The promise/question/claim, big, high-contrast, lower-middle third. | | **Curiosity hold** | 30-75 | Pose an open loop the rest of the video closes. Don't resolve yet. | | **Hero handoff** | 60-90 | Flow into logo/headline (`../remotion-template-composition/SKILL.md`). | Front-load the payoff — **no preamble, no slow brand sting first**. Brand comes *after* the hook earns the watch. ## Hook archetypes (Persian-first copy; pick ONE per template) | Archetype | Persian opener pattern | Best for | Motion signature | |---|---|---|---| | **Curiosity gap** | «اینو تا آخر ببین…» / «هیچکس اینو بهت نگفته» | tips, reveals, teasers | text snaps in, then a held pause (open loop) | | **Bold claim / contrarian** | «این روش رو فراموش کن» / «۹۰٪ اشتباه انجامش میدن» | how-to, product | hard cut + overshoot back-bezier | | **Question** | «دنبال … میگردی؟» | services, lead-gen | rise + tilt, then steady | | **Negativity / warning** | «این اشتباه رو نکن» | finance, health, safety | red accent flash + shake | | **Number / list** | «۳ دلیل که…» / «۵ نکته…» | listicles, carousels | counter ticks up, items pre-stack off-screen | | **Result-first** | show the after/price-drop/win immediately | promo, sale, before-after | hero appears f0, *then* explains | | **Direct address** | «تو که … هستی، اینو لازم داری» | niche/targeted | type fills 70-90% of frame | Use Persian numerals (`۰-۹`) — never Latin digits — in hook copy and counters; `fa` is source of truth, `en` mirrors 1:1. ## Pattern interrupts (the scroll-breaking jolt in f0-12) The feed has a rhythm; a hook *breaks* it. Stack 1-2 of these, never all: - **Motion jolt** — whip-in with overshoot: `Easing.bezier(0.34,1.56,0.64,1)`, or a low-damping `spring({mass:0.6,damping:9,stiffness:200})`. Add motion blur on the fast frames (its absence is an amateur tell). - **Hard cut + flash** — a 1-2 frame white/accent wash: `opacity = frame < 2 ? 1 : 0` over a `hexToRgba(accentColor, …)` fill. Pair with a thump SFX (`../remotion-sound-effects/SKILL.md`). - **Scale punch** — start at `scale` 1.6→1.0 (clamp) so the subject "slams" toward camera. - **Color shock** — open on a dopamine accent (electric blue/coral/acid) on a neutral base; pull it from `accentColor` so the studio recolors it. - **Silence-then-hit** — a held silent f0-8, then riser+downbeat on the hook (`../remotion-music-picker/SKILL.md` BPM map). The pause *is* the interrupt. ```tsx // Pattern-interrupt whip-in for the hook line (deterministic, clamped) const f = useCurrentFrame(); const { fps } = useVideoConfig(); const intro = spring({ frame: f, fps, config: { mass: 0.6, damping: 9, stiffness: 200 } }); const y = interpolate(intro, [0, 1], [L.vmin(60), 0]); // rises into place const flash = interpolate(f, [0, 2, 5], [1, 0.5, 0], { extrapolateRight: "clamp" }); ``` ## On-screen text hooks (the highest-ROI layer) The hook text is a **first-class editable field**, not decoration — it is the captions/cover layer the whole brief calls the biggest cross-platform win. - **Placement:** lower-middle third, inside the *tightest* safe zone (Story/TikTok) so it's safe everywhere. For 1080×1920 keep hook Y ≈ `height*0.18-0.55`; clear top ~108 and bottom ~320 (UI chrome). - **Legibility:** high-contrast white or acid-yellow fill + **black outline** (`WebkitTextStroke` or layered `textShadow`), never thin grey on busy bg. Add a scrim if over media. - **Oversized & clipped:** the hook word can fill 60-90% of frame (`fitText` from `@remotion/layout-utils`); clip with `overflow:hidden`. Strongest on 9:16. - **Kinetic / word-by-word:** beats full sentences on TikTok. Split to spans, `delay = i*stagger`, drive each with `spring({frame: f - delay, fps})`. Stagger looser on tall, tighter on wide via `pick`. - **Variable weight pop:** Vazirmatn ships a variable build — animate `fontVariationSettings: "'wght' " + interpolate(f,[0,12],[300,900])` for a Persian hero hook. ```tsx // Word-by-word Persian hook, RTL, outlined, beat-staggered const words = hookText.split(" "); const stagger = L.pick(2, 3, 4); // wide reads faster → tighter return (