First pass (skill montage-pipeline) delivers the frozen final cut: pauses and duplicates are cut out,
length is proven. The second pass draws on top: hook, step panels, subtitles, logos, call to action.
Render — HyperFrames (Apache-2.0): HTML page plus
a stopped GSAP timeline → MP4 via headless Chrome.
The second pass doesn't move a single cut. Duration before and after is compared down to the frame; any discrepancy is a failure, not rounding. Loudness and bitrate — after the second pass, in the output (see
montage-pipeline).
Error log —
docs/errors.md. Read it before starting work. Found a new problem — immediately add an entry (where · symptom · cause · fix · rule · status), without putting it off until the end: the error base accumulates so the next video is assembled faster.
Frame format and safe zones for Instagram Reels
Frame 1080×1920, 30 fps. On top of the reel, Instagram places its own interface:
| Zone (y, px) | What's there | Rule |
|---|---|---|
| 0–115 (top 6%) | Reels header, camera icon | nothing important; background only |
| 115–1248 | safe | all text, logos, face |
| 1248 | safe line | below it, text may be overlapped |
| 1248–1520 | free, only if the post caption is short | face allowed, text not |
| 1520–1920 | Instagram interface always: username, caption, sound | no text, no mouth, no chin |
| right ≈ x 950–1080, y 1080–1720 | button column: like, comment, repost | no text, no important details |
Header — 6%, owner's decision 22.09.2026: Meta's conservative recommendation (14%, 270 px) gave the header too much space, and the step panel didn't fit. The right column is given approximately — verify against a screenshot of your reel from the Instagram app.
Before every render take a verification frame with the zones applied — a separate copy of the composition with semi-transparent layers on top (code below). With your eye, from the image, not from memory.
Layout 1: screen on top, speaker below (approved 22.09.2026)
L1 selected: step panel on top, speaker below full width, edge to edge, without shrinking and without blurred borders. The owner rejected blurred sides and bottom. You can trim the bottom of the speaker — only the body; head and shoulders — never.
Head measurements — anew on every take
Take a frame of the final cut with a grid every 100 px of the source and record in source pixels: the crown (top of hair), chin, shoulders.
ffmpeg -v error -y -ss 4.5 -i чистовик.mp4 -frames:v 1 -vf "scale=540:960,drawgrid=w=540:h=50:t=1:c=red@0.6" кадр-сетка.png
Reference take: crown 610, chin 1380, shoulders 1300–1400. Second take (23.09,
analysis): crown 430, chin 980 →
y0 = 350, seam 860 — the formula below worked without corrections. Each take has its own head: the measurement
is not carried over from reel to reel.
Layout calculation
- First visible line of the source
y0 = макушка − воздух; headroom 80 px (above the head the wall and the painting are visible — the owner chose exactly this frame). - Seam (the border of the panel and the speaker) is placed so that the chin lands above 1520 with margin,
and the mouth — near the safe line:
шов ≈ 1490 − (подбородок − y0). On the reference:y0 = 530, seam 640 → eyes ≈1110, mouth ≈1320, chin ≈1490. 110 px of the body is cut off at the bottom. - A seam at 770 gives the same speaker frame, but the chin goes to ~1620 — under the nickname and caption. Do not place the seam lower than the formula gives.
- The seam — an 8 px orange line in Claude's color (
#D97757).
#face { position:absolute; left:0; width:1080px; top:640px; height:1280px; overflow:hidden; }
#face video { position:absolute; left:0; top:-530px; width:1080px; height:1920px; object-fit:cover; }
Wrapper #face — untimed: data-start only on <video> (see traps).
Step panel
Bar 115 … seam − 50 (on the reference, 115–590). Everything important is inside it; between the last panel block and the subtitle line at the seam there is a gap, checked on the frame with zones.
- line:
01 / 10on the left + a progress scale of 10 divisions on the right (as many light up as steps completed); - step title: Golos Text 900, accent word in italics Playfair Display in the Claude color ("Morning summary»);
- visual block of the step: compact, one or two lines high (notification cards with logos, chips, mini-table, checklist, diagram) — the panel is low, the phone does not fit entirely into it;
- panel background: dark
#0E0D0Cwith a soft highlight in the Claude color.
The step panel appears on the spoken step number ("First…", "Second…") — the timing is taken from recognition of the clean take, not by eye. If the author did not name the number (only "firstly"), the step boundaries are chosen by meaning and agreed with a person — that is why numbers are asked to be spoken aloud during filming.
Panels — only in split frames. Under the "В" and "Т" inserts the panel is removed: otherwise check
sees the text under the insert and reports an overlap.
Texts on screen — from speech. Headings, numbers, service names and the code word — what the author said; nothing of one's own.
Subtitles — at the seam
In a split screen, subtitles are placed at the seam (on the reference, y ≈ 600–680): inside the safe
zone, above the top of the head, not overlapping anything. The canon captions.py (line at 1400) does not work here —
it would land on the mouth.
- flashes of 2–3 words, dark plate
rgba(10,9,8,.82), Golos 800, accent words in the Claude color; - text — from re-recognition of the clean take; editing an individual word is allowed ("code" → "Claude", "bridge" → "brain"), replacing a phrase is not;
- in the call-to-action window, subtitles fade out: one message per frame.
Frame dynamics: split, visual full screen, speaker full screen
A static full-length shot of the speaker for the whole video is boring, even if the panel on top changes. The benchmark is the owner's Reel, edited by a human (instagram.com/reel/DdL-LKOMFIH, 30 s, 9 cuts):
- the base is a 50/50 split, visual on top, speaker below, short subtitles on the seam;
- the visual on top changes every 0.5–2 s — each new idea gets its own screen;
- full-screen visual for 1.5–3 s, no speaker, voice continues — 3 times per 30 s;
- full-screen speaker for ~1 s, a different shot — an emotional accent;
- layout change roughly every 3 s, hard cuts only, no transitions;
- the code word large on the seam over the visual.
Three shot types
| Type | What's in the shot | When to use | Length |
|---|---|---|---|
| S — split | step panel on top, speaker below (layout 1) | the base of the video | the rest of the time |
| V — full-screen visual | screen without the speaker: phone with Claude, a fan of posts, large logos with connections, a system diagram | on the result that needs to be seen in full: “assembles one page,” “posts for a week,” “I connect it to email,” “the full pipeline” | 1,5–4 с |
| T — full-screen speaker | the source shot in full + a smooth zoom 100 → 107% | personal phrases and emotion: “I usually…,” “it really helps…,” “I don't recommend…,” a call to action | 1–4 с |
Rhythm in a 59-second video: 4 “V” inserts, 4 “T” inserts, layout change every 2–5 s. A single camera has no second angle of its own — the “different shot” is done by zooming into the same frame.
How it works
- Т — a separate clip of the same frozen final cut over the split, with an exact offset:
<video id="fsN" src="plate.mp4" data-start="A" data-duration="D" data-media-start="A" muted>. There is one audio track for the whole video (<audio>), so the sync doesn't drift. The zoom is GSAPscaleon the clip, the anchor point below the mouth (transform-origin: 540px 1300px), so that the chin does not go under the subtitles. - В — a full-screen 1080×1920 scene with an opaque background over the split (
z-indexabove). Content — in the band 115–1248, the scene title on one line (white-space: nowrap). - Over Т — only what does not cover the face: a pill-shaped plate above the head (y≈230), for the CTA — a darkening of the upper third and a “Claude Code ×10” block.
- Subtitles move depending on the frame type: in “С” — at the seam, in “Т” — under the chin (y≈1410, in the band
1248–1520), in “В” — under the visual (y≈1300). The subtitle layer is above all inserts (
z-index: 30). - A subtitle group does not cross a cut: at the moment of a frame change the group breaks, and its display does not extend past the cut — otherwise the line would move mid-phrase.
- Checking the sync of “Т” inserts after rendering: SSIM of the face region of the finished video against
the clean version at the same moment and with a ±0.5 s shift (the first frames of the insert, while the push-in is ≈100%). On this
video: same moment 0.67–0.93, shift 0.37–0.62 — the sync is exact. If “same moment” is not higher than
the shifts — the clip was taken without
data-media-start. - The insert plan is a table “from — to — type — what” in the composition generator; each boundary is placed on a word from the recognition, not by eye.
Hook
- Owner’s rule, always: the title and logo are fully visible on the FIRST frame (frame 0). No word-by-word assembly, no logo fly-in: while the hook is “typing”, the viewer loses fractions of a second, does not understand what the video is about, and leaves. The hook must catch the eye immediately — it is also the cover of the first frame.
- Title = the main promise of the video; the logo of the brand the video is about — next to its name (owner: “so that they immediately associate it with Claude”).
- Movement — only over what is already visible: a short flash of the word (scale 1 → 1.08 → 1 over 0.28 s) at the moment it is spoken, a slow rotation of the mark. The hook’s opacity from frame 0 — 100%.
- Check: a snapshot at
--at 0— the entire title and logo are legible. - The hook lands before the first step is heard.
- The hook style is chosen by comparison: put together 2–3 options and shoot one sheet
hyperframes compare вариантA вариантB вариантC --at <секунда> --labels ....
Call to action with a code word
- The code word is — from the author's speech (“Write the word “weekend” in the comments”), it is not invented.
- A large word in a dark pill + an asterisk/logo, with the line “write in the comments” above it.
- It is placed in the safe zone (panel or above 1248) — not in the bottom third, where it will be covered by the post caption.
- It comes in on the spoken “write the word…” and stays until the end; subtitles are turned off in this window.
Where to get screens and UI components
1. The HyperFrames catalog — the foundation
hyperframes catalog --json (380+ elements), installation hyperframes add <имя> --no-clipboard.
Blocks are placed in compositions/, configured via variables.
| Screen | Catalog block |
|---|---|
| Claude dialog (mobile, 1080×1920) | claude-exchange |
| Claude Code terminal | code-terminal-run, terminal-simulator, typed-prompt |
| notifications, summary | notification-cascade, notification-stack, native-notification-pop |
| correspondence, role debate | chat-thread, message-thread-reveal, thread-message-stack, typing-indicator |
| notes, knowledge base | notes-reveal, notes-typing |
| money, charts | apple-money-count, count-up, bar-chart-race, chart-story |
| checklist, statuses | marker-checklist-card, success-check, state-chip-rail |
| system diagram | flowchart-vertical, hw-pipeline, constellation-hub |
| card grid, logos | grid-card-assemble, logo-wall, logo-sting |
| posts | x-post, reddit-post, instagram-follow |
| device frames | device-frame-stage, browser-device-stage, multi-device-splay |
1920×1080 blocks (flowchart, x-post, code-snippet-*) should be recomposed for vertical.
2. Apps that are not in the catalog
They are assembled in advance into static HTML + CSS (Vite + React → renderToStaticMarkup,
Tailwind via @tailwindcss/cli into one file), built-in animations are turned off, all animation is GSAP.
React does not go into the composition.
| Screen | Source | License |
|---|---|---|
| Gmail-style inbox | shadcn/ui, Mail example (v3.shadcn.com/examples/mail, tag shadcn@2.1.8), block sidebar-09 |
MIT |
| calendar, day view | lramos33/big-calendar | MIT |
| finance, KPI, tables | Tremor и Tremor Blocks | Apache-2.0 |
| tasks, kanban | shadcn Tasks, Dice UI | MIT |
| system graph | xyflow / React Flow | MIT |
| chat interfaces | Vercel AI Elements, assistant-ui | Apache-2.0 / MIT |
We do not take the code: React Bits (MIT + Commons Clause — redistribution not allowed), Aceternity (the license of the free part is unconfirmed), official Telegram clients (GPL-3.0), small clones of Telegram and Calendar without a license. Magic UI / Aceternity live animations run in real time rather than frame by frame — we take only the appearance from them.
3. Logos
| Source | What | File license |
|---|---|---|
| glincker/thesvg | 5886 brands, brand colors and mono | MIT |
| gilbarbara/logos | colored: google-gmail, google-calendar, claude-icon, claude-code |
CC0 |
| simple-icons | monochrome; 223 icons have their own license — check | CC0 |
Only real SVGs from these three sources, the logo is not drawn or generated. No
open logo (as was the case with GetCourse) — the service name as a text label. Look at each logo
on the frame, not by the file name: instagram-icon in gilbarbara — monochrome black, on a
dark panel it came out as a black blob; the brand gradient was taken from thesvg (E33).
The logo is inserted inline SVG (no network during rendering). The file license is only copyright on the SVG: a trademark is placed to designate the service in question, without any implication of partnership.
3a. Image generation (gpt-image-2 via a Codex subscription)
When you need to show something that is not on the computer screen (food, buildings, products) or make a volumetric background
for the hook, step icons, a phone with an empty screen for a real interface. For AI content, HyperFrames is usually enough
without generation. Sources of prompts, methodology, templates and pitfalls —
docs/image-prompts.md. The model draws the image; text and logos — we do.
4. Competitor screencasts — selectively
If you need a live screen rather than a drawing:
- search by the step topic (Gmail, Calendar, mail, notes…), and not by the mishap in general: the frame must match what the author is saying;
- a screen with the ChatGPT / Codex / Gemini interface at the step “system in Claude” — reject;
- only authors whose permission has been obtained; outside this list — do not take;
- for competitors, their subtitles and icons are almost always printed over the screen: cut out the strip of screen between them, and blur nicknames and personal data;
- downloading and clipping: Loore
download_instagram_media→ frames every 0.5 s → cutout with cropping and blurring per the specification.
Hook references: competitor database in Loore, selection by outperformance relative to the author's median, then a separate filter by graphics quality: with one outperformance, visually poor videos make it to the top (Instagram's white placeholder), this teaches the claim, not the design.
Assembly
- The composition is generated by code from a file of words and step timecodes, not written by hand: there are dozens of groups of subtitles, each tied to a measured word.
- Fonts — as local files with
@font-face: Golos Text (variable, declarefont-weight: 100 900), Playfair Display Italic for emphasis, PT Serif for the “Claude response”. Without@font-facethe headless browser will silently substitute its own. - GSAP — as a local copy (
vendor/gsap.min.js), not from a CDN: rendering without a network. - Render streams — explicitly:
-w 3(or by the number of cores),autoselects one. A 1:08 video on a MacBook Air M2 with-w 3— 1 min 41 s. - The generator finds the boundaries of inserts and panels in the words of the final cut by exact match of the word and only in the window between neighbouring anchors. Searching by the beginning of a word finds the wrong word (E36).
Verification layer of zones (a copy of the composition, not included in the render)
<style>.gd{position:absolute;z-index:50}.gd b{position:absolute;left:14px;top:8px;color:#fff;font:700 26px sans-serif}</style>
<div class="gd" style="left:0;right:0;top:0;height:115px;background:rgba(255,0,0,.28)"><b>шапка Reels 6%</b></div>
<div class="gd" style="left:0;right:0;top:1248px;height:272px;background:rgba(255,160,0,.26)"><b>1248–1520</b></div>
<div class="gd" style="left:0;right:0;top:1520px;bottom:0;background:rgba(255,0,0,.34)"><b>интерфейс всегда</b></div>
<div class="gd" style="right:0;width:130px;top:1080px;height:640px;background:rgba(0,120,255,.3)"><b>кнопки ≈</b></div>
Gate before render
hyperframes lint— 0 errors (a lint error disables the layout audits —0 sample(s)does not mean “clean”).hyperframes check— overlaps and contrast.hyperframes compare/snapshot— a verification frame with zones at each step: panel, seam, subtitles, face.- After render: duration = duration of the final cut down to the frame. A 60 fps source rendered at 30 fps gives a video one frame longer than the audio (E35) — verify audio to the millisecond, video ±1 frame.
Verification frame with zones — at each step of edits, not only before the final render. On the first video about the repository, he caught a two-line hook, a full-width title and a black logo.
Traps found on a live build
- Cyrillic as garbled characters in catalog blocks. The block puts variables as JSON into the tag
<html>,<meta charset>travels beyond the first 1024 bytes — the browser reads the page as Latin-1. Write the variable values as ASCII escapes (\uXXXX), remove the comment before<!doctype>. - Block fonts are Latin (Inter, EB Garamond в
claude-exchange). Replace@font-facewith Golos Text and PT Serif, otherwise Cyrillic will fall back to a foreign typeface. data-starton<video>and on its wrapper at the same time — the extractor and the visibility window diverge: wrong frames, then the clip disappears. Time — only on the video, the wrapper is untimed.<video>withoutidfreezes in the render. Each media — its ownid.<audio>withoutiddoes not get into the mixer — the render is silent.- Emoji in headless Chrome are drawn with foreign glyphs. Do not use, or place as an image.
- Subtitles by the 1400 canon with split screen land on the mouth — on the seam.
- Step header at y≈70 — under the Reels header. Panel content — from 115.
- Two-line cards are taller than they seem from the font size: the third card slid under the subtitles at the seam. Measure on the frame, not in your head.
- The first version of the layout put subtitles at 1760–1840 — into the zone that the interface always covers. A test frame with zones catches this in a second.
- Blurred fields around the scaled-down speaker were rejected by the owner: the speaker edge to edge, cropped from below, not scaled down.
- A hook that assembles by words, eats the first 2 seconds: at frame 0 it is empty, the viewer leaves without understanding the promise. The title and logo — entirely from frame 0 (error log, E22).
- Subtitles under full-screen inserts: the subtitle layer was below the "V" and "T" layers — on the insets they were not visible. Subtitles are the topmost layer.
- Recognizer timecodes can be non-monotonic ("mail.@37,06 He@36,98"): sorting words by time shuffled the phrase into "his He mail". Word order is as the recognizer returned it.
- Badge in the step header line overlaps the header: the header width depends on the word. The step result — as a separate card over the widget, sources fade into the background.
- Subtitle at the seam in two lines falls on the top. A group — no longer than 24 characters, one line.
- zsh in one-liners:
set -- $pairdoes not split words,$i:l— a variable modifier. Loops and filter assembly — in bash or Python. - Double prefix
xlink:hrefin inline SVG after processing by code — references to gradients do not resolve, the fill disappears. One prefix (orhref), the logo — check on the frame (E34). - Monochrome logo under the brand name (
instagram-iconin gilbarbara) — a black spot on a dark panel. Check each logo on the frame (E33). - Panel under the inset —
checkcomplains about overlap. Panels only in splits. - Inset anchor by the start of the word found another word. Exact match, window between adjacent anchors (E36).
- A number out of thin air in a mockup — only as an illustration of the system in question; in subtitles and the code word — only what the author said.