nateherkai/hyperframes-student-kit

video-storytelling

Design and build the visual layer of a long-form talking-head video as one persistent world the camera moves through, instead of a sequence of separate graphics.

Vedi sorgente
Documento Skill originale

Contenuto dal repository con titoli, esempi, codice, tabelle, link e immagini preservati.

Video storytelling

How to build the visual layer of a long-form video so a viewer is still oriented at minute nine. Applies to any talking-head video with a script: a news take, a single-topic deep dive, a comparison, a tutorial, a listicle.

The failure this replaces is scene-thinking. A new graphic every few seconds makes the viewer restart comprehension every few seconds, and no individual graphic is at fault, which is why the feedback comes back as the vague "confusing" or "too much going on" rather than as a specific defect.

For a neutral practice beat sheet, read docs/STORYTELLING-WORKBOOK.md at the repository root. The reusable design system is in this skill's reference/design-system.md; the worked open-loop geometry is in reference/patterns/wall-of-slots/. Private source videos are not required.

This is not hyperframes-video-beats. That skill places overlay cards on top of a talking head. This one designs the thing the video is about.

1 · The four principles

One persistent world

The video lives in a single space, and it never resets. Everything the viewer has already seen still exists where they left it, and new material is added to it rather than replacing it. An element, once drawn, is never redrawn, moved, or relabelled.

Everything else in this skill depends on that. The camera has nowhere to go if the space keeps changing, a spotlight means nothing if there is nothing else on screen, and an open loop cannot stay open in a world that resets.

The world also has to be worth staying in, so decide the spatial grammar before building anything and hold it for the whole runtime. In the reference build: left to right is toward the outcome, up is toward a human and is the only warm thing on screen, down is into a system of record. A viewer who learns that in the first two minutes reads the rest of the video for free.

Break the grammar exactly once, deliberately, for meaning. The reference build's only right-to-left move in ten minutes is a resolved ticket reversing back into the queue on the words "reopen rate". It lands without a word because direction had meant something for the previous three minutes.

The camera is the edit

Cutting to a new graphic costs the viewer their bearings every time. Moving through a space that stays put costs them nothing. A push-in then reads as "look closer at the part of the thing you already understand", and the pull-back is where the work pays off, because everything built so far is still there.

Work in three altitudes and move between adjacent ones:

AltitudeShowsUsed for
Worldthe whole spaceorienting, payoffs, "here is where we are"
Regionthe subgraph under discussionmost of the runtime
Detailone elementa number, a state change, a decision

Jumping detail to world is disorienting unless it is a deliberate payoff beat, in which case it is the best move available.

Rules that cost a rebuild each to learn:

  • Name framings as exact world windows, not as scale factors. Window

boundaries must fall between labels. Specifying a framing as "scale 0.6" slices words in half at the frame edge.

  • Reuse the same window for the same purpose. If the orient shot is a

particular window, always use that exact window. Near-identical framings read as drift.

  • Vary duration with distance travelled, not one global preset.
  • Camera and full-frame wipes sequence, never overlap. Concurrent, they

guillotine content. Camera first, then wipe.

  • A cover layer goes opaque before the camera moves underneath it. Reversed,

part of the move is exposed and reads as a jump cut.

  • No label within 60px of the frame edge. An element may bleed and read as

"the world continues"; a word cut mid-glyph reads as a bug. Solve it by placement, not by masking, since a feather cannot catch a blade travelling ~1800 px/s.

  • Two sequential moves on the same subject read as a glitch. A reframe that

finishes and THEN a wipe that starts is a zoom, a dead stop, and a second motion — the client calls it "a glitch and a pause". Merge them into ONE gesture: shared symmetric ease (power2.inOut), landing on the same frame, so the panel reads as pushing the footage aside. Related: front-loaded eases (power3/4.out) on a visible footage move front-load ~90% of the travel and crawl to a stop; reserve them for element entrances, where the crawl reads as settling. (Cost a client-flagged revision on the worked example intro.)

Attention is a spotlight

Exactly one element at full brightness at any moment. Everything else already drawn stays quiet but legible. Not optional. This is the rule that lets a world accrete to a dozen elements without ever feeling busy, and it is what makes the pull-back readable rather than overwhelming.

Hierarchy comes from the RATIO between active and quiet, never from making everything dark. The first prototype of the reference build had a brightest pixel of 101/255 and was unreadable at 960x540; the fix was brighter ink, not more dim.

StateTarget luma (Y, 16-235)Opacity in the reference
Active stroke + label200-2351.0
Context, drawn and still relevant120-1500.56
Spent, said its piece85-1050.33
Any label, any statenever below 90

Implementation is one function, called at every narration beat with the ids being talked about (build_s01.py, lit()):

python
def lit(t, active, warm=(), dur=0.5, floor=FLOOR):
    active = list(active) + [g + "-l" for g in active if g + "-l" in order]
    for gid in order:
        if gid not in opac:      # never drawn: nothing to light
            continue
        tgt = 1.0 if gid in active else (WARM if gid in warm else floor)
        if gid not in active:
            tgt = min(tgt, MUTED.get(gid, 1.0))
        ...

Three details in there are load-bearing:

  • A label always inherits its element's state (the -l suffix fold-in). A lit

element with an unreadable label under it is the exact defect being fixed.

  • A prop that is finished retires to opacity 0, via a MUTED cap, not to 0.3.

Muting to 0.3 leaves unexplained confetti on screen for the rest of the section. Generator rule: emit the mute() before any later-starting lit(), or the mute is overwritten when its tween completes.

  • Opacity events must be emitted in timeline order or the tracker sees stale

state and computes the wrong target. Keep a seq() guard that raises on out-of-order emission.

  • One opacity authority per element per window. Two simultaneously-active

tweens driving the same element's opacity to different values render smoothly in a standalone section but strobe at ~10Hz in the assembled master, where the winning tween alternates per frame. The reference build shipped this defect at every dock. When a sweep like lit() overlaps an explicit brighten or mute, the later-starting tween is the sole driver and the sweep skips those ids (lit(..., skip=(...))). Gate it: scan assembled renders for one-frame out-and-back luminance pops in every transition window (qa/v4/flicker/qa-flicker.mjs in the reference build).

Open loops are spatial

Never say "we'll come back to this." Leave something visibly incomplete in the world and return to it. A promise the viewer can see is a promise they can feel outstanding.

The mechanic is identical whatever the shape:

  1. Plant it in the hook, in the space, on the line that makes the promise.
  2. Re-show it at every boundary. Any section handoff is a boundary. The

re-show costs about two seconds and is the retention mechanic; skipping it is how a payoff ends up paying off nothing.

  1. Advance it visibly. The viewer must see the incompleteness shrink.
  2. Close on the image you opened on.

Pick the shape from the script:

Script shapeThe worldWhat is incomplete
N parallel itemsN sockets on a spineempty slots (see reference/patterns/wall-of-slots/)
One process, deepthe full pipeline, laid outa broken or missing link
Levels or tiersa stack that builds upwardthe top of the stack is a ghost
Comparisontwo columns on one shared axisone column empty, or an unfilled scorecard
News or announcementa timeline, or a landscape mapthe right side, the future, is blank
Tutorial or build-alongthe finished architecture, ghostedevery node not yet built

Handing an item to its slot is a live camera move, ~1.4s, never a cut to a pre-made picture of the filled state. The viewer watches the thing they just learned become the tile. If the video has a numeral or counter, roll it inside that same move, as an odometer, with the same start, duration and ease for both digits or they merge into one band mid-roll.

The counter needs a name, not just a number. A corner numeral tells the viewer where they are in the sequence but not what the current item is; nine minutes in, "03" carries nothing. Pair the numeral with a 1-2 word label naming the current item (SUPPORT, VOICE AI), in the same plate chemistry, moving as one rigid fixture with the numeral, swapping inside the same odometer roll. Client review of the reference build asked for exactly this: the visuals were good and the viewer still lost track of which use case was on screen.

2 · Discipline

If it doesn't get a word, it doesn't get drawn

Every shape is a labelled element, a connector between labelled elements, the travelling subject, or the ambient ground. Nothing else.

This came from a mute-test review that scored comprehension 7/10 and named the cause: the diagram was clean, the decorations were not. A dozen unlabeled shapes drawn in the same weight and palette as the meaningful ones, so a first-time viewer cannot tell signal from texture and spends real attention decoding things that were never meant to be decoded. That is "too much going on" relocated from words into shapes.

Budget: ~20 on-screen words per section. No sentences. Labels are 1-2 words, under their element. The reference section 1 is 21 words in 84 seconds.

Corollary: an anti-pattern visual never overlays the recommendation. Take the world to near-black first, or put the counter-example where the good diagram isn't.

Give the viewer one thing to follow

Carry the story on a single travelling subject whose state visibly changes: a chip with data rows and a score bar, a document, a request. Rows fill, a bar moves, a border changes colour. A viewer must be able to follow an entire section by watching that one object, and it must never teleport; it moves along drawn connectors at a constant speed.

One global token set

Full detail in reference/design-system.md.

TokenValues
Stroke1.5 / 2.5 / 4 / 7, and nothing else
Type22 / 30 / 42 / 60 / 84
Radius10 / 22 / 40, always superellipse(2)

Five colours, five fixed meanings, zero decoration. Warm means a human is involved and nothing else is ever warm. Never pure white for type; it halates on dark.

The failure this prevents, from the craft review of the first build: "every decision was made locally and correctly; no decision was made globally." 18 stroke weights and 9 type sizes, most of them 0.2 apart, individually invisible and collectively fatal. The eye gets noise instead of hierarchy. Gate it with a token linter that fails the build.

Motion

Entrances on cubic-bezier(0.16,1,0.3,1), exits faster than entrances, duration varies with mass so a 290px node and a 40px label do not decelerate identically, and back.out exactly once per section, on the beat that deserves it.

No cross-fade, ever. Full-bleed layers enter and exit by clip on an already opaque background. At any instant a pixel is either graphic or footage.

3 · Build contract

  • One generator per section. scripts/build_<id>.py writes only

compositions/<id>.html. Edit the generator, never the generated HTML. Section roots index-*.html are hand-maintained, so framing changes go there.

  • Shared modules stay shared. Anything that defines shared geometry is

imported by every section. If sections are built by parallel agents, forbid them from touching shared modules; divergent geometry silently breaks every handoff.

  • Namespace the SVG defs before assembly. Each section defines its own

gEdge, shadow, edgeFade. In one assembled DOM url(#id) resolves to the first match in document order, so once an earlier composition unmounts, every later graphic paints nothing. Cost in the reference build: 35 seconds of empty screen with every gate green. Only looking at the assembled render caught it.

  • Time everything to word starts from a word-level transcript, entering

0.15-0.25s ahead of the anchor word. Never divide a section evenly into beats.

  • Audit gaps. Any stretch over ~1.5s with nothing happening is a dead screen;

have the generator print them.

  • Leave air in the cut. Aggressive pause-capping produces near-zero gaps at

sentence boundaries that the client hears as "stutters" and jump-cuts. Calibrate against the speaker's own norm: flag sentence-boundary gaps that are ~10x tighter than their median (the reference speaker's was 470ms; the flagged junctions sat at 20-40ms), and repair by inserting 4-8 frames of air, words never touched. Air is a donor slice of the track's real room tone with 10ms fades, never anullsrc: digital silence against a -50dBFS floor is an audible dropout. Video holds the frame; between sentences a talking head is near-still and 4-8 frames of hold is invisible. Insertion-only changes shift the transcript deterministically, so no re-transcription is needed. An insert's edges must respect the speech envelope: butting donor tone against a word tail in 1ms amputates a decay that naturally takes 60-120ms, and the client hears the cliff as a stutter. Splice with a 40ms equal-power crossfade into the tail's own decay, never a hard butt.

  • Baked times mean scripted shifts. Generators bake hundreds of proto-time

literals, and section offsets, seam tables, and root tweens all hold absolute times. Any timeline change after graphics exist (even a 2s air pass) must go through a reviewed AST/parse-level transform that shifts every literal after each edit point, then a diff review, never hand edits. Derive every shift from the canonical edit list itself; the reference pass caught a 2-frame error in its own audit's precomputed offsets by re-deriving.

  • --workers 1 on render. Parallel workers make the video layer paint black.

4 · Gates

Run all of them, then look anyway.

bash
node scripts/qa-tokens.mjs          # 0 off-system strokes / type sizes / radii
node scripts/qa-no-crossfade.mjs    # 0 full-bleed layers animating opacity
node scripts/qa-legibility.mjs <render>
node scripts/qa-seamjump.mjs <render> plan/cover-seams.json --control <cut>
node scripts/qa-deadframe.mjs <render> --control <cut>
node scripts/qa-presence.mjs <render> --every 1      # speaker on-screen share
node scripts/integrate.mjs --check                   # 0 gaps, 0 stale mounts
npx hyperframes lint

Gates do not replace looking, and gates that have never failed are not gates.

  • Motion smoothness is verified on contiguous frames, never on beat frames.

A single frame per beat proves composition; every motion defect — a jump into a move, a crawl to a dead stop before a second move, a freeze, a one-frame pop — lives between beat frames and survives every other gate. Before any delivery, tile a contiguous strip across EVERY transition window (camera move, wipe, reframe, dock, section seam) and read the strips:

bash
  ffmpeg -i render.mp4 -vf "select='between(n,A,B)',scale=480:270,tile=layout=6x7" \
    -frames:v 1 tile-<window>.png

Defect signatures to read for: adjacent tiles with a large jump then several near-identical tiles (front-loaded ease / dead stop); motion that halts and a different motion that then begins (two moves that should be one gesture); any single tile unlike both neighbours (pop). The worked example intro shipped a zoom-freeze glitch that passed tokens, cross-fade, legibility, dead-gap and per-beat frame reads; the client caught it in one viewing. This class recurs — treat this strip-read as load-bearing, not optional.

  • Legibility is not automatable. Absolute pixel-share thresholds measure text

volume, not legibility; a wordy build scores higher than a sparse one. Read a 960x540 downscale of every major beat by eye.

  • Validate every gate against a control known to trip it. A seam gate that

reports zero on a broken build is worse than no gate.

  • Verify the assembled artifact, never section prototypes. Most of the bugs

that survived this build's gates existed only in the assembly.

  • Audit audio at the acoustic level, not the transcript level. A transcript

derived from the pre-edit source is structurally blind to artifacts the edit created, and a text sweep over it will report "clean" while the client hears stutters. What actually finds them, in yield order from the reference build: a whole-track dead-span scan (runs of digital-zero samples inside pauses whose real floor is ~-70dBFS read as dead holes; fill with room tone), 1ms-resolution envelope forensics at every junction edge (a sample-derivative click check passes an envelope cliff that the ear flags instantly), and a fresh ASR pass on the delivered audio diffed against the expected text. Phoneme-repeat autocorrelation found nothing: what clients call "stutters" are usually truncation cliffs and dead holes, not doubled words.

Presence: aim for ~40% speaker-on-screen across a graphics-led video. Long absences are fine at payoff beats that need full width; audit them rather than capping them.

5 · Order of work

  1. Lock the cut and get a word-level transcript. Everything is timed to it.
  2. Choose the world, the spatial grammar, and the travelling subject. Write them

down before building.

  1. Choose the open loop from the script shape and build its shared module first.

Its geometry is referenced by the hook, every boundary, and the payoff.

  1. Build one section end to end and render it. Prove the system there before

touching the others.

  1. Review that section by eye, muted, for ornament and legibility. Fix the

system, not the section.

  1. Build the rest against the proven section. Then namespace, assemble, render,

gate, and read the assembled render.

Steps 4 and 5 are where the quality comes from. The reference build's entire visual system is the output of reviewing its first section three times.

dallo stesso repository

Altri Skills

Tutti gli Skills
nateherkai
Community

edit-video

Edit a raw talking-head video through transcription, silence trimming, mistake review, visual storytelling, motion graphics, and verified HyperFrames rendering. Use for a complete edit; route a single requested operation directly to its specialist skill.

installazioni
2
GitHub Stars
1,4K
Aggiornato
13 ott
nateherkai
Community

hyperframes-video-beats

Plan, write, time, style, and validate motion-graphic beats for long-form talking-head HyperFrames videos. Use when Codex is asked to add or improve visual beats, glassmorphism lower thirds, crumpled-paper full-screen takeovers, transcript-synced callouts, pacing coverage, YouTube retention graphics, or render-ready overlays in an existing HyperFrames video project.

installazioni
2
GitHub Stars
1,4K
Aggiornato
13 ott
nateherkai
Community

make-a-video

Beginner-friendly end-to-end video creator for HyperFrames. Use when the user says "make a video", "create a video", "new video", "build a video", "video from scratch", "I want to make a video", "help me create a video", or when someone who's never used HyperFrames before arrives with a concept, script, or rough idea and wants a finished MP4. Interviews the user in one pass, then builds the full video with mandatory preview and visual-verification gates.

installazioni
2
GitHub Stars
1,4K
Aggiornato
13 ott
nateherkai
Community

short-form-video

Maintain the existing May Shorts teaching compositions using their legacy face-mode, scene-overlay, and karaoke-caption patterns. Use only when working on those examples or explicitly requesting this legacy scaffold. For new reels, Shorts, or short advertisements, use short-form-edit.

installazioni
2
GitHub Stars
1,4K
Aggiornato
13 ott