Files
AISkills/flat-artist/video-hooks/SKILL.md
T
Soroush Asadi 4ffbcac9ee refactor: bundle the whole template suite under flat-artist/ + fix references
flat-artist is now the single container: all 16 template skills + the R&D
references/ moved inside flat-artist/. Cross-references updated — the orchestrator
points to bundled `<name>/SKILL.md`, sub-skills point to `../<name>/SKILL.md`,
and the R&D report path is relative. README catalog updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:31:53 +03:30

9.7 KiB
Raw Blame History

name, description
name description
video-hooks 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.
// 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.
// 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 (
  <div style={{ direction: "rtl", fontFamily: FONT, display: "flex",
                gap: L.vmin(8), justifyContent: "center", flexWrap: "wrap",
                maxWidth: L.width * 0.86 }}>
    {words.map((w, i) => {
      const s = spring({ frame: f - i * stagger, fps, config: { damping: 12 } });
      return (
        <span key={i} style={{
          fontSize: L.pick(L.vmin(96), L.vmin(84), L.vmin(72)), fontWeight: 900,
          color: textColor, WebkitTextStroke: `${L.vmin(6)}px ${BRAND.ink}`,
          paintOrder: "stroke", transform: `translateY(${(1 - s) * L.vmin(40)}px)`,
          opacity: s,
        }}>{w}</span>
      );
    })}
  </div>
);

Curiosity & retention pacing across the open

  • Open a loop, close it later — the hook promises, the hero pays off. Never resolve the question in the first 2s or there's no reason to stay.
  • One idea per beat — staging: dim/blur everything but the hook; let it own the eye before the next element competes.
  • Hold for the read — a hook line needs ~0.6-0.8s minimum on screen before motion competes. Robotic = linear; floaty = held too long. Cut frames before adding.
  • Tiny life in the hold — a sin(f/fps) breathe/shimmer so the held hook isn't a frozen frame.
  • Grain + texture from f0 — even the cover frame should have animated grain (offset background-position per frame); flat-saturated = reads as AI/template.

Platform hook norms → template implication

Platform Hook window Norm Template move
TikTok 3s curiosity-gap / bold-claim; word-by-word captions calm neutral grain + warm-earth variant; word-by-word hook as editable layer
IG Reels 2-3s cleaner, less-cluttered than TikTok refined kinetic type, glass lower-third, mesh-gradient bg, one clean interrupt
YT Shorts f0 no runway — open on the peak result-first / hero-at-f0; cinematic graded look
YT long-form intro 5-15s cold-open hook, brand sting <3s state payoff first, brand second
IG Story full-bleed heavy UI chrome keep hook clear of top ~250 / bottom ~250
All three 1-2s first frame = hook = cover; authenticity > gloss hook prop in every aspect, re-flowed not letterboxed

Tie the hook into template structure

  • Make the hook copy a Zod prop (e.g. hookText: z.string()) + a seeded Text element whose key matches — same binding model as ../remotion-template-composition/SKILL.md. Ship strong Persian default copy so it reads finished pre-edit.
  • Hook color = accentColor/textColor from colorSchema; pass user hex through a grade so a garish value doesn't break the open (../remotion-svg-colors/SKILL.md).
  • The hook is a <Sequence from={0} durationInFrames={sec(2.5)}>; the hero sequence overlaps its tail so the handoff is a flow, not a cut.
  • 3D hooks: keep the interrupt object filling the frame per aspect (tune fov/position.z), drive entrance from useCurrentFrame() with high mass for weight; let StudioEffects (bloom/DOF/vignette) finish it.

Hook checklist (gate the open)

  • Frame 0 reads as a finished, intriguing cover — no black/empty/half-loaded frame.
  • A single clear pattern interrupt in f0-12 (motion / flash / scale / color / silence-then-hit) with SFX.
  • ONE hook archetype; Persian-first copy with Persian numerals; en mirror present.
  • Hook text is an editable prop, high-contrast + outlined, in the tightest safe zone, no clipping with long Persian strings.
  • An open loop is posed and NOT resolved in the first 2s; payoff lands at the hero.
  • Eased/overshoot motion (no linear), held for the read, with a tiny live shimmer; animated grain from f0.
  • Verified the open in all three aspects (pick-tuned), recolors cleanly, re-renders identical (deterministic).

Related: ../remotion-template-composition/SKILL.md, ../remotion-aspect-ratios/SKILL.md, ../remotion-design-styles/SKILL.md, ../remotion-sound-effects/SKILL.md, ../remotion-music-picker/SKILL.md, ../remotion-svg-colors/SKILL.md, ../persian-fonts/SKILL.md, ../remotion-template-catalog/SKILL.md, ../flatrender-template-seo/SKILL.md.