Communitygithub.com

themosthappypiano/reel-style-studio

Use when analyzing or recreating reel subtitle grammar.

What is reel-style-studio?

reel-style-studio is a Claude Code agent skill that use when analyzing or recreating reel subtitle grammar.

Works with✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/themosthappypiano/reel-style-studio/tree/HEAD/skills/reel-subtitle-analysis

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Reel Subtitle Analysis

Use when a reference reel must teach us how captions are edited, or when producing the original subtitles of a new talking-head reel. This is separate from transcription: what is spoken and what appears on screen are different timelines.

When to Use

  • A supplied reference reel needs caption timing, typography, emphasis, or placement studied.
  • A new reel needs a transcript-to-caption plan or subtitle rendering check.
  • Not for visual asset provenance alone; catalogue that in the reel reference entry.

Procedure

  1. Inspect the actual video with ffprobe through terminal for duration, native dimensions, fps, audio track. Extract a word-timed transcript, check dubious terms against audio; do not infer caption timing from the transcript alone.
  2. Decode EVERY frame and diff it programmatically — never hand-pick cut points off a contact sheet. Use scripts/analyze_reel.py <video> <outdir> [--caption-band y0 y1]: it decodes all frames, detects hard cuts, white-flash transitions, caption events with exact first/last frame, and crops the caption band into OCR sheets. Hand-picking switch points from a 2 fps sheet on Reel 003 produced 16 beats when there were 18 and timestamps off by up to 0.5s. The full decode costs ~4s for 45s of video and is exact. Validate any change to the detector against a reel you have already verified by hand — tuning it blind produced, in sequence: a false-negative cut (absolute neighbour cap), 6 false-positive "flashes" (white CARD beats misread as flashes), then 0 flashes when a guard overcorrected and rejected flashes confirmed by eye.
  3. Three transition types exist; cut detection alone finds only one. (a) Hard cut: single frame, diff 70-192, neighbours <14. (b) White flash: luma ramps dark->white over 7-9 frames, holds white exactly 2 frames, hard-cuts out. Surfaces as a cut with a high BEFORE-neighbour — never dismiss that as threshold noise. (c) Hard cut into a scale-in: clean cut, then the incoming plate animates in over ~6 frames; surfaces as a high AFTER-neighbour. Verify each flagged cut on a 10-12 frame native-fps strip before calling a reel "hard-cut only". Flashes are RARE (5 across 265s of Saraev footage) — an accent, not a default.
  4. Derive the caption band PER REEL before comparing reels — cross-reel caption statistics are worthless otherwise. Measuring 7 reels through one shared band (native y704-904) produced a confident, wrong finding: words-per-caption spanning 1.80-5.14 with an apparent "listicle = dense captions" rule. Re-measured with each reel's own band, five of seven collapse into 2.02-2.39 w/cap and the rule evaporates. A band 40px off changes an event count by 30-100%. Probe ~12 frames spread across the reel, take the median text-row cluster, and use that. Measured bands vary enormously between reels of the same creator (native y540-640 for 001 vs y800-1000 for 003). Treat event counts as ±10% unless verified event-by-event by hand.
  5. Re-running the analyzer overwrites earlier results — re-pass any per-reel flags every time. A batch re-run of 003 without its verified --caption-band 400 500 silently replaced the hand-checked 77 events with 51. Keep each reel's verified band in the reel folder and always pass it.
  6. Caption density does NOT track script type. Across 7 measured reels speech sits at 199-252 wpm and five of seven cluster at 2.0-2.4 words/caption with 0.51-0.79s holds. Target ~2-2.5 words held ~0.5-0.8s. One reel (004) genuinely runs long at 5.14 w/cap and 1.31s holds — outliers exist, but they are not predicted by listicle-vs-narrative.
  7. Caption position MOVES between screen modes within a single reel. Reel 001 puts captions at native y≈561-621 in split beats (graphic/face seam) and y≈906-980 in full-frame face beats (over chest/mic). A single band captured his FACE and produced unreadable OCR sheets. For OCR, detect per event: extract the full frame at each event midpoint, find the densest strong-horizontal-gradient row cluster within y380-1150, crop that. Verify the detector against two frames whose caption position you have eyeballed. (A single median band is still right for cross-reel TIMING stats — just not for OCR.)
  8. Captions repeat across a cut rather than re-animating. 001 holds list of everyone, across 3 consecutive events and to target, across 2; 008 holds GitHub across 2. The caption persists while the picture changes underneath — do NOT log a repeated caption as a detection bug or 'stale subtitle'.
  9. Any number on screen must be read across consecutive native frames. Counters routinely live in the CAPTION layer, not on a card: Reel 003 animates OF 12→18→21→24 and 26,876→57,987→80,000 inside the subtitle, each landing on the spoken figure. A single sampled frame reports a confident FALSE value ("OF 18", "85.0k"). Sample 4+ frames across any digits before recording them.
  10. Generate contact sheets at ~2 fps for screen-mode classification. For subtitle changes, sample 8 fps; when a word's entrance, phrase replacement or typography switch matters, inspect consecutive native frames at the file's fps. Store labelled frames and source in a per-reel reference entry.
  11. For each contiguous caption span record: spoken words + start/end; visible text + first/last frame; screen mode (split/presenter-only/full-screen evidence); text family (serif/sans), case, weight, italic, color/emphasis; lines, placement relative to face/cursor/UI, shadow/outline; entrance/exit/hold. Mark items unknown at compressed resolution. Log uncaptained spoken words explicitly rather than 'repairing' captions into verbatim transcript. Report the event count, mean/min/max hold, and how many swaps land exactly on a cut — Reel 003: 77 events, mean 0.56s, min 0.13s, max 2.53s, 16/77 on-cut. When OCR labels and detected events disagree in count, say the pairing is approximate rather than forcing an alignment.
  12. Detect grammar, not just font: one-word substitution, 1–3-word phrase replacements, cumulative two-line builds, selective colored keyword, serif-to-sans changes across modes, keyword hold across a continuing spoken CTA. Identify whether captions are inside a website capture or are independent editorial overlays.
  13. Build a compact, original caption spec keyed to the exact words in your transcript; use real source timing, no guessed names/data, and safe placement around face, microphone, active UI and social platform controls. Preserve readability on phone-sized viewing and avoid filling the top proof area with caption text.
  14. Render, then review first/last spoken words, 3–4 random midpoints, visual-mode transitions, and CTA. Confirm no clipped captions, no stale previous words, no missing intended emphasis, and no speech-caption drift. A transcription pass is not proof of on-screen caption sync.

Reference grammar from studied Nick Saraev reels (patterns, not a clone)

full table, and source_index.json for live Instagram URLs.** Reels enumerated via Apify (apify~instagram-reel-scraper) using the APIFY_API_KEY vault item; the other APIFY_API_TOKEN item is a DIFFERENT account that is over its free quota (403 platform-feature-disabled). yt-dlp fetches a single reel fine but hard-429s on profile enumeration.

Reel 009 is a DIFFERENT creator — do not pool it blindly

"constants" derived from the Saraev set: 99.2 captions/min (vs 49-78) and a 0.03s minimum hold (vs a supposed 0.17s floor). Density and floor are creator choices. Always check whose reel you are measuring before generalising.

It also demonstrates the cleanest two-register switch keyed to the FACE, not to cuts: canvas beats (no face) get 1-2 lowercase sans words at native y580-740 held 0.44s; face beats get 3-4 ALL-CAPS mono words on a dark pill at y480-570 held 0.91s. Only 3 of 60 swaps land on a cut. Detect the face per frame (bottom-third luma + variance) rather than per beat — the face enters and leaves mid-beat with no cut.

And it shows the money-word rule executed with typeface instead of colour: display serif italic used exactly 4x, only for $20, $300, for Claude, or ChatGPT. Prices and product names, nothing else.

Measured constants across the library

  • Caption rate is the stable constant, cut rate is not. captions/min sits at 49-78 for six of seven reels; cuts/min ranges 10.0-22.7 (a 2.3x spread). Match caption cadence; choose cut cadence per register.

  • Caption mean hold 0.53-1.21s. Minimum observed hold is 0.17s across every reel — a hard floor, not a coincidence.

  • Two architectures, not one style. (A) Persistent split (001): presenter on screen nearly throughout, proof surface swaps ABOVE him WITHOUT a cut — 5 cuts in 30s, beats up to 8.93s. (B) Full-screen card sequence (003, 008): every visual change is a cut, 17 cuts/45.8s, presenter in a minority of beats. A cut-counting pass badly understates (A): content changes every 1-2s while the frame does not. Diff the proof band separately for that register.

  • Reel 001 (DdQF09DPR04, Google Maps Scraper Kit, 30.1s, 157k views): the reel the old unsourced bullet described — now confirmed and measured. Captions MOVE by mode (y≈561-621 split / y≈906-980 face), not a fixed band. The top band is a live animated Claude Code session, not a screenshot: claude · google-maps-scraper-kit window with an autopilot dot, streaming real tool calls (Read(raw.csv) → 308 rows, Bash(python scripts/scrape.py --socials), Write(leads.csv) → 267 clean leads), flanked by two orange 8-bit crab mascots with hey / on it speech bubbles. A live counter climbs 13 → 134 → 267 alongside a $1,044 value figure — the number IS the demonstration. Held CTA keyword "Google" stays on screen while the sentence continues.

Three caption registers, switched by what the beat is doing (OCR'd 001 + 008)

  1. Full-screen card / cream beats — large BLACK ITALIC DISPLAY SERIF, ALL CAPS, centred, no pill (JUST USE, THESE THREE, CONNECTS CLAUDE DIRECTLY TO GOOGLE SEARCH CONSOLE). Editorial headline register.
  2. Face beats — WHITE BOLD UPRIGHT SANS on a solid black rounded pill, over chest/mic (to turn, Claude Code, meaning, now study). The pill guarantees contrast over skin and shirt.
  3. Split / light-canvas beats — BLACK BOLD UPRIGHT SANS, no pill, on the cream background (This free AI, pay for anything, tell it the city).

Colour appears exactly once per reel, on the money word. 008: THREE in salmon inside an otherwise black serif line, plus a giant dark-red $3,346 price-pain hook. Nothing else is coloured.

  • Reel 002 ("Apple design skill"): UNVERIFIED — not among the 12 reels on the current profile page; no file located. Do not cite it as evidence.
  • Reel 002 (Apple design skill): different type treatments for different editorial jobs, not a strict mode map. Some cream split-screen process beats use small black bold sans, whereas the split-screen mountain website uses lowercase black italic serif. The full-screen GitHub text, X post, and white audit beats use black or white uppercase italic high-contrast serif phrase captions. Full-frame speaker uses white clean sans over microphone/chest; selected current words can be aqua/light blue, or italic serif. The quoted CTA keyword 'Apple' becomes aqua and holds while later words are spoken. The source's blue highlights on a GitHub document/X post are proof-object emphasis, not spoken subtitles.
  • Do not enforce literal word-by-word karaoke everywhere: choose on-screen phrase duration by legibility and semantic emphasis. For a complicated evidence panel, caption the key phrase in clear negative space, not over real product controls.
  • Reel 003 (Agent Skills / addyosmani repo promo, cream register): the cleanest example of two caption systems switched by screen mode. Graphic/full-screen beats use black (white on dark) ALL-CAPS ITALIC display serif, 1-2 words, phrase-replaced, parked in lower-third negative space. Face beats use white bold sentence-case geometric sans, 1-3 words, over the chest/mic line, never over the face. The swap is instantaneous on the cut frame - there is NO global y-position, each mode owns its own. Captions are a selective EMPHASIS track, not verbatim: whole spoken runs compress to a fragment ('completely on its own' -> on it's own) and function words are dropped. The CTA token (skill) holds while the rest of the sentence continues.

Deriving mode-switched caption specs

When a reference shows different caption typography in different screen modes, build the ledger keyed to MODE first, then to time. A single global caption style copied across modes is the failure this grammar prevents. Verify the switch frame at native fps: the previous mode's last caption must be gone on the same frame the new mode appears, otherwise you are looking at a renderer hold bug, not source grammar.

Applying the grammar to an original reel (validated once, Sep 2026)

First build from this library: 11.97s proof section, 4 cuts (20.0/min, inside the measured 10.0-22.7), 12 captions at ~2.2 words / 0.5-0.9s holds, all three registers, colour used exactly once on the hook's key word. The measured constants transferred directly — no re-derivation needed.

  • Pick the section by sentence boundaries from the word-level transcript, then cut on natural pauses >0.18s. Those pauses are where the reference reels cut too.
  • Stop the section BEFORE any unverified factual claim. Numbers about the user's business (leads processed, replies, clients) do not go on screen until the user confirms them, even when the audio says them and even in a throwaway test.
  • Listen before captioning a product name. Whisper large-v3 rendered n8n as "N8M" at beam 5 and 10; the caption must show the real spelling. Re-transcribe the narrow window at two beam sizes and check word probabilities before trusting any name or figure (a contradictory phrase like "two became three" is a misspeak to resolve with the user, not to render).

Pitfalls

  • Do not copy a third-party creator's exact proprietary font, captions or animations; encode transferable editorial functions and make an original house version.
  • Small compressed frames cannot confirm font family or hex color. Label visual approximations as approximations and verify against full-resolution stills.
  • The word-level ASR transcript can hallucinate or 'correct' names; patch visible subtitles only after listening and checking product spelling.
  • Re-running the SAME ASR model is not verification. Two faster-whisper large-v3 passes on the same audio returned byte-identical output (max start-time delta 0.00s across 187 tokens) — that is determinism, and it proves nothing about a disputed word. Settle disputed words against the video: reels routinely caption their own product names on screen (Reel 003's ASR said 'clawed skill'; frame t=0.40 renders CLAUDE in orange serif). On-screen text beats any number of ASR passes. For a real second opinion run a DIFFERENT model on just the disputed few seconds — cheap and fast: slice the window with ffmpeg and decode it with medium.en/small.en/base.en. On Reel 003 medium.en heard 'Claude' cleanly while large-v3 said 'clawed', proving the error was a large-v3 artefact rather than ambiguous audio. A bigger model is not always righter on proper nouns.
  • Count real words, not ASR tokens, for WPM. Whisper emits glue fragments (,000, -launch, -ready) as separate tokens; Reel 003's 187 tokens are 184 words. Derive pause stats from the word array too (largest inter-word gap), never estimate them.
  • A held CTA word is intentional, not a missing transcript bug. Conversely a held stale subtitle due to renderer mismatch is a bug: inspect and distinguish from source rhythm.
  • Screen-mode changes may relocate captions instantly. Look at two frames on either side of each cut before assigning a constant global y-position.

Completion output

Deliver a caption event ledger (speech, screen text, style, timing, mode) plus a short original implementation recipe, with frame evidence and uncertainties. If implementation was requested, deliver an actual rendered reel and inspect it at native resolution. Related visual provenance/motion patterns belong in the per-reel reference catalog, not this skill.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Related Skills