Making a film

How General Translation films are made in Prototemplate's motion/ folder with HyperFrames.

Covers
The brief, the research package, three directors and a judge for the script and the look, the storyboard, the kit, narration and music through ElevenLabs, the mix targets, draft and delivery renders, the frame-by-frame critic, and how a finished film reaches /motion; and the short media around a launch: promos, product demo videos, README GIFs, launch captures and briefs for the video vendor.
When to use
When planning, scripting, building, critiquing or rendering a GT film, trailer, sting, showreel, promo or demo GIF, or when reviewing one against Kevin's standard.
Also in
Motion
Section 1install-skills.mjs

Install

node scripts/install-skills.mjs gt-films --project <dir>

Run it from a Prototemplate checkout. It links skills/gt-films into the project’s .claude/skills and .agents/skills; --copy vendors the folder instead, and --dry-run prints each step first.

Section 2SKILL.md

Instructions

General Translation (GT) makes its brand films, blog trailers and translation-history films as HyperFrames compositions: HTML pages with one paused GSAP timeline that the HyperFrames CLI seeks and renders frame by frame to MP4. They are built in $PROTOTEMPLATE/motion/, one project per film, from one shared kit and one brief, motion/MOTION.md. A new film goes through the same steps: three directors' treatments, a judge who writes the script and the concept, a storyboard on the 0.5 s grid, the build, narration and music, a draft, critic passes and a delivery render. This skill states each step, the rules it answers to, and the checks a film passes before it reaches /motion.

Paths are relative to a Prototemplate checkout ($PROTOTEMPLATE) unless they name another one, and paths that start with films/, kit/ or out/ sit inside motion/. references/ is this skill's folder of detail files. Its helpers need only Node and ffmpeg and run as node <this skill's folder>/scripts/<name>.mjs (skills/gt-films/scripts/ in a Prototemplate checkout). motion/ is untracked and exists only in the checkout of the session that makes the films; the public repository holds the published copies under public/media/, the generated /motion data and this skill. Where motion/ is missing, the kit, the brief and the film sources are missing too: review against this skill, and ask Kevin for the Videos session before building a film.

1. Ownership

  • One session owns motion/: Kevin calls it the Videos session. Kevin, 2026-10-03: "the videos one should be making them, and this one is just for prototemplate work". Any other session reads motion/, sends film requests to the Videos session by name, and runs no film lane itself (Kevin, 2026-10-02: "wait another agent is working on the motion and videos").
  • motion/ stays untracked and motion/out/ is in .gitignore. Never run git add -A or git add . in the shared checkout. On 2026-10-01 a broad add in another session pushed 229 files (72.6 MiB) of motion/ to main in b56e64c, and commit 8c8d6fb had to untrack them again. Stage paths by name.
  • Renders stay in motion/out/. The committed outputs are the web copies in public/media/ and the files pnpm build:motion writes, and the Prototemplate session commits them.
  • The kit (motion/kit/) is read only for a film lane. A kit change goes through the Videos session, because every film loads it.
  • Explorations stay local until Kevin reviews them on localhost or in a sent file. Publishing a film to main is Kevin's call.

2. The brief

  • Read motion/MOTION.md whole before writing a frame. Its dated sections at the top override the older sections below them: "Dithered artifact pictures (2026-10-05)", "Round 7 direction", "Round 4 direction". A film's own SCRIPT.md, CONCEPT.md and NOTES.md record later rounds (7b, 7d, 8) and override MOTION.md for that film.
  • The canon MOTION.md applies is BRAND.md (the name, the idea, the voice, the mark, color, type, language as material, the avoid list), DESIGN.md (the four colors, the line law, the doubled line, the isometric family, the 1-bit Bayer language, moving type, motion discipline) and the deck (deck/, rendered as deck/preview/sNN-dark.jpg; open slides 1, 3, 6, 9, 14, 17 to 23, 26, 30, 34, 37, 54 and 64 before designing).
  • The roster is the ## The films section of MOTION.md, one entry per film headed ### <slug>: <title> (<length>). scripts/build-motion.mjs parses that heading, so keep its form.
  • Two kinds of film follow different rules:
  • Blog and brand films (blog-fuma-nama, blog-designing-docs, the brand film, the mark sting, the release board, the showreel) follow every rule in MOTION.md and its round directions.
  • Translation series films (jihe-yuanben, journey-to-the-west, modern-hebrew) are free of the round 4 and round 7 palette and layout rules. Kevin, 2026-10-02: "you can just take whatever creative liberties you need to make the most awesome motion designed video that also tells this story". Their directors choose the material, the faces, the structure, the captions and the sound. Each stays bound by its BRIEF.md facts and hedges, Kevin's writing rules, public-domain or credited sources, deterministic rendering and the studio standard.
  • The standard is a studio reel. Every move explains structure (things assemble, reveal, connect or translate), timing is designed on a beat grid, type and lines are crafted at the pixel, and one idea holds each beat.

3. The pipeline

stepoutputrule
Research package (series films)films/<slug>/BRIEF.mdfive numbered sections: an animation script table, the long-form post, a vocabulary table, sources with rights, a fact-check list in three groups
Three directorsmotion/concepts/<slug>/<lens>/each writes a one-sentence story, a script, key frames and a motion test of the signature move
Judgefilms/<slug>/SCRIPT.md, CONCEPT.mdscores the three on six criteria of 10, picks one, grafts only moves that use the winner's grammar
Kevin reads the scriptsa sent page or filethe build waits for his answer
Storyboardfilms/<slug>/STORYBOARD.mdbeats on the 0.5 s grid, words verbatim, picture, motion and easing, transition out
Takes and bedfilms/<slug>/audio/ (sound/ in journey-to-the-west and modern-hebrew)one take per line through kit/audio/el.mjs, the bed edited to the film's length
Buildindex.html, lib/key actions timed to the takes' word timings
Checknpx -y hyperframes@0.8.106 check .ends with 0 errors; every kept warning is intentional and named in NOTES.md
Draftmotion/out/_draft-<slug>.mp430 fps, draft quality
CriticsCRITIQUE-<n>.md or NOTES.mda defect table with times and fixes; picture, sound and story, and history and rights for a series film
Revise and verifya new drafteach fix checked on the frames that showed the defect
Deliverymotion/out/<slug>.mp460 fps, delivery quality, the earlier final copied to motion/out/v<N>/ first
Finishposter, contact sheet, credits, NOTES.mdsee section 10
  • Directors. Each round uses three lenses chosen for the film: the blog films' round 7 used people, idea and viewer; jihe-yuanben used page, words and geometry; journey-to-the-west used names, editions and woodblock; modern-hebrew used dictionary, roots and documents. In a from-scratch round the directors never open the earlier cuts (Kevin, 2026-10-02: "the new scripts and visuals and stuff have to be done from scratch ... it needs to be coherent and tell a clear simple story taking excerpts from the blogs").
  • Scripts. A blog film tells one simple story, at least two thirds of its spoken words verbatim excerpts from the post (the Fuma Nama film is 0.80). Every excerpt matches the post's MDX by exact string, curly quotes included, and the post's hedges stay. A heading never shares a content word with the line spoken under it.
  • The judge's table. Story, facts, writing, picture, sound and build, each out of 10, so out of 60 (jihe-yuanben: The page 52, The geometry 45, The words 44). The judge reads every key frame at full size and at 1280 x 720, the contact sheets and the motion tests read frame by frame with ffmpeg, then writes the decision, the lines with their source sentences, the heading sources, pronunciation and an audit into SCRIPT.md, and the look, the type and every beat into CONCEPT.md.
  • When Kevin rejects a script, diagnose the story first. His note on the three series films (2026-10-05): "a little too slow paced. the video isnt very interesting and the script is just kind of weird ... we love the diagrams and visuals though". The scripts were fact inventories with no hook. The rewrite ran a story editor's diagnosis, three writers on different angles (reveal, problem, word), and an editor who fact-checked and wrote SCRIPT-v2.md with a list of the visual changes, at 60 to 75 s and about 140 to 190 words.
  • When a move fails twice, change the idea. Kevin called jihe-yuanben's triangle reassembly "a bit wacky" (2026-10-04). Rounds 3 to 6 retuned the paths of twenty thin pieces, and every critic still saw a scatter, because twenty pieces cannot travel calmly in about 4 s. Round 7 changed the idea: one pair moves and the rest of the strip prints in place. Round 8 refined that idea and passed.

references/pipeline.md has the package format, the director and judge briefs, the storyboard format and the workflow lessons.

4. The project

cd motion/films
npx -y hyperframes@0.8.106 init <slug> --example blank --non-interactive
cd <slug> && ln -sfn ../../kit kit
  • Pin the CLI at 0.8.106 in every command (check, snapshot, render, preview). The wiki's hyperframes skill suggests upgrading a pinned project; the films keep 0.8.106 until the Videos session moves every film together.
  • Load every script, stylesheet, font and picture through kit/... or the film's own folder. Never load from a CDN: renders must not touch the network.
  • Type is var(--font) from kit/tokens.css. A literal font-family: 'Inter' anywhere in a composition makes the compiler fetch Inter from Google Fonts and override the kit's InterVariable ("[Compiler] Fetched 11 font face(s) for "Inter" from Google Fonts"); the static cut is about 5 percent wider and line masks clip. A film's own faces (JetBrains Mono for a code panel, a CJK serif, a Hebrew face with niqqud) live in its fonts/ folder under private family names. Fallback families for glyph text are set from JavaScript, because the compiler reads family names from <style> text and fetches any it cannot find.
  • One paused GSAP timeline per composition, built synchronously and registered on window.__timelines['<composition-id>'] (initialise window.__timelines first). The root carries data-composition-id, data-start="0", data-width, data-height and data-duration.
  • Every frame is a function of time. No Date.now, performance.now, unseeded Math.random (use GTDither.rng(seed)), requestAnimationFrame, timers or repeat: -1. Canvas drawing happens in the timeline's or a tween's onUpdate from tween-driven proxies. A picture a canvas reads is also a hidden <img> in the DOM, so the renderer waits for it.
  • Animate transforms, opacity and colors only. A transformed element is block level and sized. Never pair a CSS transform with a GSAP tween on the same element; set the start inside fromTo.
  • A short film may be one index.html (composition_file_too_large and timeline_track_too_dense are then kept warnings). A long film splits scenes into sub-compositions (journey-to-the-west builds eight with tools/compose.py).
  • Read the working examples before building: films/_smoke/ (a dither field mixing two pictures, Inter type, the bar monogram), films/_gem/ (gem smoke, a glass shape, a dithered field) and films/_endcard-fire/ and _endcard-blue/.
  • A film folder holds index.html, lib/, audio/ (or sound/), assets/, fonts/, archive/ (earlier compositions and storyboards), SCRIPT.md, CONCEPT.md, STORYBOARD.md, NOTES.md and, for a published film, CREDITS.txt. init also writes hyperframes.json, meta.json, package.json and its own AGENTS.md and CLAUDE.md. Each film builds its sound with its own scripts, and its NOTES.md names them: Fuma Nama's lib/ holds cues.mjs, make-voice.mjs, make-bed.mjs and make-mix.mjs; the docs film keeps its drawing in lib/film.js and its sound in audio/make-bed.py and audio/mix.py; the series films use Python tools under tools/ and sound/.
  • references/traps.md lists the composition and render traps the films met, each with its fix.

5. The kit

pathwhat it is
kit/tokens.cssthe Inter @font-face on kit/fonts/InterVariable.woff2, --font, the colors and the deck ladder at 1920 (.t-display 106, .t-h1 88, .t-h2 53, .t-lead 31, .t-body 26, .t-cap 18)
kit/dither.jswindow.GTDither: grid(canvas, cell), toneFromImage(img, grid, { fit, focusX, focusY, gamma, invert, blur, lift, region }), draw(grid, field, { ink, paper }), mix(a, b, p) on one smoothstep, fields.ramp, fields.disc, fields.horizon, rng(seed)
kit/gemsmoke.jswindow.GTGem (ES module, fires gtgem-ready): mount(host, { palette, params, image, width, height, pixelRatio, offset, rate }) resolves to { at(t), set(params), dither(grid, { tones, gain, gamma, amount }), canvas, mount }
kit/endcard/the shared series end card, addEndCard(tl, { palette, title: [line1, line2], url, start })
kit/sheet.jswindow.GTSheet, the deck's series frame (rails 67 px in, registration crosses). The blog films dropped it on 2026-10-04
kit/marks/, kit/brand/gt-mark.svgthe speed marks (bar monogram, lockup, dithered, ASCII, plate inverted, double cut, livery stack, two-way, globe G) and the doubled-line GT mark
kit/logos/, kit/logos/adopters/Fumadocs, MDX, Next.js, React, TanStack and Mintlify marks; the thesvg marks and GitHub avatars of Fumadocs' adopters
kit/gem-shapes/processed shape PNGs for gem smoke glass (gt-bar-monogram, gt-mark, fumadocs-moon); add a row to make.mjs and run it for a new one
kit/sources/, kit/two-tone/, kit/tone/, kit/blog/full-resolution picture sources, the deck's dithered mood and opener files, the sign-in plate's tone grids, every image the blog posts ship
kit/lottie/the Ramp demo's translated .lottie files in en, de, es, ja and zh with a manifest
kit/gsap.min.js and plugins, kit/lottie.min.js, kit/fflate.min.js, kit/paper-shaders/GSAP 3.15 with DrawSVG, MorphSVG, SplitText, Flip, MotionPath and CustomEase; lottie-web 5.13; Paper Shaders 0.0.78 (Apache-2.0)
kit/audio/el.mjs, kit/audio/voice.jsonElevenLabs narration, beds, music and transcription; the narrator's settings
kit/_retired/the dictionary pictures, retired 2026-10-05. Never use them
  • Gem smoke. Draw a frame with gem.at(seconds) inside the timeline's onUpdate. Change uniforms per frame with mount.setUniformValues (cached and synchronous); gem.set() loads its image asynchronously, so never call it per frame. Keep at most two full-frame mounts live at once. Use pixelRatio: 0.5 for a mount that is only read or dithered. An outer glow lays a faint smoke wash over the whole frame (plain above about 0.2, and visible in a dithered field's black cells at any value), so keep it at 0 under a field. Clip glass 1 px outside its limb when hard dither cells sit beside it.
  • Dither. One cell size per film (2 or 3 px at 1920 x 1080), anchored at (0, 0), never resized inside a transition. A dithered field changes state only by mixing tone on one grid with one smoothstep, so cells switch in Bayer order. Alpha fades, wipes and moving masks are refused on dithered fields. Pictures of writing that carry readable plain text are retired (Kevin, 2026-10-05: "never distract with text on the artifacts. this disqualifies the dictionary and oxford"). gt-dither owns the Blue Marble tone standard for dithered artifact pictures.
  • End card. The card is 4.0 s from a hard cut on the beat: the doubled-line GT mark in the film's gem smoke, the post's title in two lines at 120 px (it shrinks to no less than 100 px for a long line), and the link at 40 px, every margin 160 px. Set the root's data-duration to start + 4, end the film's clips at start, add the card after the film's own tweens and register the timeline after it. The card is silent; the bed resolves under it. frame defaults to false. hyperframes check reports two text_occluded infos at start + 0.33, which are expected. The card's last frame is the blog films' poster. kit/endcard/README.md has every measurement.

6. Look

These rules bind the blog and brand films. The series films choose their own look inside section 2's limits.

  • Material palettes. A film's palette is its gem smoke material and the Bayer dither in that material's tones (Kevin, 2026-10-01: "the color we can use is the color and dither and shaders we used (gem smoke from glyphfield)"):
  • blue: ground #2f5ce0, smoke #ffffff and #86a8ff (Designing docs for humans);
  • fire: ground #000000, smoke #fe5b16, #f7ff61 and #ffffff, with ember #7a2a08 in prints (Fuma Nama);
  • ink #070707 stays the ground between material scenes.
  • Refused in every blog film: gradients other than the material, CSS glows and shadows, blur filters, glass UI chrome, rounded corners, any color from outside the film's material.
  • Dither in the background. Kevin, 2026-10-02: "im sad to see the dither disappear from background". Since round 7d both blog films print their free gem smoke through the Bayer screen as a field behind every scene. The field is kept clear of the type and the objects by zones drawn from their own ink, and it changes only by tone mix (Fuma Nama holds it 44 px off a heading's ink and 18 px off an object's).
  • Headings. At most two lines, set at 100 to 150 px (the line length decides), Inter 500 through var(--font), sentence case, proper nouns capitalised, no trailing period. Seat each line's first glyph ink on x 160 from the loaded face's side bearing. Fuma Nama's cap tops sit at y 172; the docs film sets its heading box at top 146. A sentence that cannot fit two lines at 100 px is replaced by the post's own shorter wording.
  • No captions and no subheaders in the blog films (Kevin, 2026-10-01: "no need for captions/subheaders"): no bylines, dates, quote attributions, labels, tags, counters or URLs. One heading or one quote per beat, plus the picture. The one URL a film shows is the post's link on the end card (round 7).
  • Logos appear where they say more than words and where the post supports them. A logo keeps its drawing and proportions; a one-color mark is recolored only to the film's ink, paper or material tones. Fuma Nama's adopters beat shows true marks with GitHub star counts; it uses Turborepo's mark for "Vercel Turborepo" so one repository's stars are never credited to a whole organization.
  • No series frame on the blog films. Kevin, 2026-10-04: "remove the frame that seems to be overlaid on top of everything, its overlapping with a lot of stuff and is not the cleanest."
  • Safe areas. No text closer than 120 px to an edge at 1920 x 1080, and 160 px for titles and the heading axis.
  • Credits. Every photograph or scan on screen carries its credit, from MOTION.md's credits table or the film's CREDITS.txt. The blog films show no photographs or scans, so they print no credit line; their third-party logos follow the Logos rule below.
  • Motion. Arrivals on expo.out or power3.out, moves on power2.inOut, processes on none, no overshoot. Arrivals and scene changes land on the 0.5 s grid. A sentence holds at least (words / 3) + 1 s after it has fully arrived. Transitions come from MOTION.md's four: a tone mix between dither fields, a hard cut on a beat, a hairline that draws a seam, or moving type. gt-motion carries the full motion rules.

7. Sound

Every film carries a narrator and a music bed, both through kit/audio/el.mjs (references/sound.md has the commands, the mix recipe and the traps).

  • The key lives in the ElevenLabs config file that el.mjs reads (~/.config/elevenlabs-sfx/config.yml). Never print it, copy it, pass it on a command line or put it in a prompt. Which account the key belongs to has changed more than once; test the Music API with a short request before composing a new bed.
  • The narrator is whatever kit/audio/voice.json names: Clara since 2026-10-02 (eleven_multilingual_v2, stability 0.65, style 0.2, speed 1.0). Kevin chose her after "make the voice more australian and make the voice less shaky", "a much more friendly australian voice", "like 2 but more female" and "lets use clara". Never generate below speed 1.0: the slowed takes measured the most pitch wobble. Calm comes from the gaps between lines.
  • Kevin asked on 2026-10-05 whether the series films need another narrator ("why are we using clara? what are some better voices?"). Auditions are in kit/audio/auditions/series/in-context/. A series narrator gets its own voice file or --voice per take, so the blog films keep Clara.
  • Takes. One request per line with --prev and --next, the request text exactly the script's. Respell a name in the request text only (line 7 of Fuma Nama sent "Ver-sell" for Vercel) and keep the script's spelling everywhere else. One take per line unless a take is wrong. Check every take, the bed and the final with el.mjs hear. A non-English take (Mandarin, Hebrew) needs a native listener's sign-off before release.
  • Music. The blog films use round 5's peaceful sound-generation bed, edited to each film's length (Kevin: "for the blogs i liked the peaceful music from before"). Other films compose with the Music API at the film's exact length. Prompts are instrumental with no vocals, drops, risers or genre clichés: fire is warm, dark and slow; blue is airy, glassy and precise; the series films avoid pastiche (jihe-yuanben's bed has no guqin, pentatonic figures or gong). The Music API ignores timing in a prompt, so plan an edit.
  • Mix targets (MOTION.md): narrator about -16 LUFS integrated for the film, bed about -26 LUFS under speech and about -20 LUFS alone, true peak under -1 dBTP, fades of 0.3 to 0.8 s, no clicks, AAC in the MP4, audio as long as the video. The five delivered films read -16.0 to -16.5 LUFS with true peaks of -2.0 to -3.0 dBTP.
  • Intelligibility outranks the bed figure. The docs film's bed sits almost all in 160 to 800 Hz, and at -26 LUFS under speech it covered Clara's vowels, so it plays at about -28.5 LUFS under speech with a dip shaped by her voice. A film that leaves a target says why in NOTES.md, with the measurement.
  • The renderer's AAC trap. HyperFrames 0.8.106 turns the whole AAC track down to -1.5 dBTP whenever its true peak passes -1 dBTP (enforceAacTruePeak), and its limiter has no lookahead. Control peaks in the ffmpeg masters before the mix: Fuma Nama limits each narration master to -3.6 dBTP at 192 kHz, and the docs film to a -3.5 dBFS ceiling at 4x oversampling. A film measuring about -1.5 dBTP, or quieter than the sum of its stems, has been corrected.
  • The picture reads with the sound off. No beat depends on audio alone.

8. Facts

  • A film states only what its post ($PROTOTEMPLATE/content/blog/<post>.mdx, or the MDX in gt-cloud apps/landing/content/blog/en-US/) or its BRIEF.md states. Titles, names and dates appear exactly as published.
  • Hedges stay. Fuma's "I think" and "probably" are spoken. The contested items in a BRIEF's fact-check list stay hedged on screen and in the narration (jihe-yuanben dates the book as translated in 1606 and 1607 and printed in 1607). Captions follow the BRIEF's image notes ("early 17th-century printing", "Qing edition").
  • No numbers about the company (customers, revenue, funding, team) and no launch dates. Customers appear only as BRAND.md names them.
  • Star counts are read from the GitHub API on the day and rounded as GitHub rounds them (one decimal under 100,000, whole thousands above). NOTES.md records the date.
  • A work in copyright is named on a typographic card and never shown (Waley's Monkey, 1942). A CC BY-SA source is credited on the end card and in the credits file; whether the film as a whole is published under share-alike is Kevin's decision.
  • Copy follows MOTION.md and gt-voice: plain declarative English, no marketing adjectives, no exclamation marks, no rhetorical questions, no em dashes, no "X, not Y" pairs, no metaphors, no eyebrows. Product tokens keep their exact form (gt, gt-next, npx gt translate) and never start a sentence.

9. The critic

A critic watches the render frame by frame against MOTION.md's "How a film is judged", the BRIEF, SCRIPT.md, CONCEPT.md and the writing rules, and reports every defect with its time and a fix. references/critic.md has the full procedure and the report format.

  • Frames. Every 0.5 s at full size, and every 0.25 s for 1 s either side of each cut and each major move (skills/gt-films/scripts/frames.mjs, which selects by frame index). Read signature moves frame by frame. Read a handful at 1280 x 720 for legibility.
  • Whole-film scan. Every frame decoded small in grey: no blank or black frame that the storyboard does not plan, no single-frame flicker, and the largest changes on the planned cuts (skills/gt-films/scripts/scan.mjs --cuts ...).
  • Sound. el.mjs hear on the render against SCRIPT.md, skills/gt-films/scripts/measure-render.mjs for loudness, peak and stream lengths, silence at the head and tail.
  • Facts and scripts. Every line, heading, tag, gloss and credit against its source. Each non-Latin sentence is one text node with lang and dir, because per-character spans break Arabic joining and Devanagari matras.
  • Safe area and reading floors. Measured on frames.
  • The report. A table of # | time (s) | defect | fix, then "Left as they are" with a reason for each, then what was fixed and how each fix was checked on a new draft.
  • Separate critics by lane: picture, sound and story, and history and rights for a series film. A failed critic sends the film to revision, then verification, then a second verification if fixes were needed.
  • Kevin's note outranks a critic's pass. A beat he called wrong stays open until he says otherwise.

10. Renders

# from motion/films/<slug>
npx -y hyperframes@0.8.106 check .
npx -y hyperframes@0.8.106 render . -o ../../out/_draft-<slug>.mp4 --quality draft --fps 30 --workers 3 --quiet
npx -y hyperframes@0.8.106 render . -o ../../out/<slug>.mp4 --quality delivery --fps 60 --workers 3
npx -y hyperframes@0.8.106 snapshot . --at <seconds> --no-end --describe false
  • Drafts render at 30 fps and draft quality. The final renders at 60 fps and delivery quality at 1920 x 1080. In 0.8.106 delivery is an alias of high (x264, preset slow, CRF 15); the wiki's hyperframes-cli skill writes --quality high for the same preset.
  • --workers 3 on the shared machine, where several films and other lanes render at once. Record the machine's load in NOTES.md when a render or a timing check runs slow.
  • Before overwriting a final, copy it to the next motion/out/v<N>/ folder (today v1 to v5, v7, v7d and v8; there is no v6). Earlier finals are never deleted.
  • Render the delivery cut without --quiet, save its log (| tee), and check that the log never mentions Google Fonts (skills/gt-films/scripts/measure-render.mjs <film.mp4> --log <file>).
  • Prove determinism before a final: two lossless renders (--crf 0) with different worker counts compared by framemd5 must match on every frame. When large type rasterises differently between render processes on the hardware GPU, render with --no-browser-gpu (modern-hebrew does).
  • Deliverables: out/<slug>.mp4; out/<slug>.png, the poster (the blog films use the settled end card, snapshotted on the hardware GPU path, since --no-browser-gpu moves the smoke by up to 5 levels); out/_sheets/<slug>.png, a contact sheet of one frame per second in six columns of 480 px tiles with the time under each (films/journey-to-the-west/tools/sheet.py); films/<slug>/NOTES.md with the deliverables, measurements, ElevenLabs ledger and traps; and for a published film out/<slug>.credits.txt and films/<slug>/CREDITS.txt.
  • A share copy for a large master: ffmpeg -i <slug>.mp4 -c:v libx264 -preset slow -crf 22 -pix_fmt yuv420p -c:a copy -movflags +faststart <slug>-share.mp4 (jihe-yuanben went from 153 MB to 39 MB).

11. Reaching the site

  • /motion lists every roster film with its length and status, plays the published films, and shows each series film's research package at /motion/<slug>. /brand shows the blog films under "Made with the system".
  • The Prototemplate session publishes a film only after Kevin approves the cut: it copies the final (or the share copy) to public/media/<name>-film.mp4 with the moov atom at the front (ffmpeg -i <in> -c copy -movflags +faststart <out> when the streams stay unchanged), writes public/media/<name>-poster.jpg at 1920 x 1080, lists both in public/media/README.md, adds the film's film and poster paths to public/motion/published.json (a new film) and pins the cut with node scripts/build-motion.mjs --pin <slug>, then runs pnpm build:motion.
  • The version rule: the film on the site, its credits, its contact sheet and its script describe one cut. public/motion/published.json pins each published cut by the SHA-256 of its video and audio streams (a faststart remux hashes the same as its render), its label and its length. pnpm build:motion copies <slug>.credits.txt, sheets/<slug>.webp (and the .png up to 20 MB) and scripts/<slug>.md to public/motion only from the folder in motion/out whose render has the pinned streams (out/ itself, or an archive folder with the kit's sheets/ or scripts/, such as out/series-100s), and checks the script's label and length against the pin. A newer render in out/ is listed on the site as "in review" by its label and length only. So keep each cut's records beside its render: when a final is replaced, move the old render with its credits, sheet and script into an archive folder with the same layout.
  • pnpm build:motion also reads motion/MOTION.md and each films/<slug>/BRIEF.md and writes src/lib/motion.ts and public/motion/<slug>.md. A film's status comes from the files: rendered when its final or web copy exists, in production when its folder exists, planned otherwise. The script throws before writing anything when a BRIEF's shape or a script's shape changes or a web copy is not its pinned cut, and refuses quoted hex colors in motion.ts and absolute local paths anywhere. Its outputs are committed, so the site builds without motion/.
  • After any re-render of a published film, the Videos session tells the Prototemplate session, which refreshes the public copy and poster and reruns pnpm build:motion. The Videos session regenerates the credits files first when a picture changed.

12. Short media

Promos, product demo videos, README and launch GIFs, launch captures and vendor briefs are made outside motion/ and follow references/short-media.md.

  • A promo opens on the finished result and cuts quickly between different scenes on the beat of the music, with no narration, in under about 30 s. When Kevin dislikes a new cut, the previous cut comes back exactly (2026-08-19).
  • A product demo starts inside the product, sets the value proposition large and centered, and holds its last frame about 1.5 s longer.
  • A README or launch GIF runs the real CLI with fresh results and ships with its render script. A launch capture records the real shipped pages from CDP screencast frames (2026-08-14).
  • A brief for the video vendor is pasteable, names the priority deliverable, cites prototemplate.com/deck for the brand rules and uses dummy brands until a customer's permission is confirmed (2026-09-30).
  • Kevin directs media. Finished pieces go onto /brand or /motion in the same round.

Review checklist

  • [ ] MOTION.md's dated directions and the film's later rounds were applied; the series rules or the blog rules were used as the film's kind requires.
  • [ ] The script is one clear story; a blog film's spoken words are at least two thirds verbatim from the post and match it by exact string; hedges are kept.
  • [ ] Headings are at most two lines at 100 to 150 px, seated on x 160, and share no content word with the spoken line under them.
  • [ ] No caption, byline, date, label, counter or URL in a blog film except the end card's link; no series frame.
  • [ ] Type is var(--font) and the render log has no Google Fonts line; nothing loads from a CDN.
  • [ ] Every arrival and cut is on the 0.5 s grid; every sentence holds (words / 3) + 1 s; transitions are from MOTION.md's four.
  • [ ] Dithered fields change only by tone mix on one grid; at most two full-frame gem mounts are live.
  • [ ] No text inside 120 px of an edge (160 px for titles); every picture is credited.
  • [ ] Narration is the voice.json narrator (or the film's own voice file) at speed 1.0, every take passed el.mjs hear, and non-English takes are flagged for a native listener.
  • [ ] measure-render.mjs passes: -17 to -15 LUFS, true peak under -1 dBTP, AAC, audio length equals video length, 1920 x 1080 at 60 fps.
  • [ ] scan.mjs shows no unplanned black frame, no single-frame flicker and the largest changes on the planned cuts.
  • [ ] hyperframes check ends with 0 errors and NOTES.md names every kept warning.
  • [ ] The earlier final is in out/v<N>/; poster, contact sheet, NOTES.md and credits are written.
  • [ ] Nothing under motion/ is staged, and the Prototemplate session was told about a re-render of a published film.
  • [ ] Short media: a promo cuts on the beat with no narration; a demo starts inside the product; a GIF runs the real CLI and ships its script; captures show the real shipped pages; a vendor brief uses dummy brands (references/short-media.md).

Wiki skills this one depends on: hyperframes (the entry point), hyperframes-core (the composition contract; read references/variables-and-media.md for <audio> clips), hyperframes-animation (motion rules and the Lottie adapter), hyperframes-keyframes (seek-safe moves), hyperframes-cli (check, snapshot, render), hyperframes-audio (the mix, <hf-audio-group> and carve.mjs), elevenlabs-sfx (the config path el.mjs shares). GT skills beside it: gt-motion (the motion rules for pages and films), gt-voice (the writing rules and replies to the vendor), gt-dither (the Bayer material and the artifact picture standard), gt-brand (the marks and the palettes), gt-isometric (the docs film's isometric bridge), gt-diagrams (the doubled-line connector), gt-graphics (still artwork such as the partnership globes), prototemplate (the /motion page).

Sources

  • Prototemplate: motion/MOTION.md (the brief, round directions dated 2026-10-01 to 2026-10-05); motion/kit/endcard/README.md; motion/kit/audio/el.mjs; motion/kit/audio/voice.json; motion/kit/tokens.css; motion/kit/dither.js; motion/kit/gemsmoke.js.
  • Prototemplate films: motion/films/jihe-yuanben/BRIEF.md, SCRIPT.md, CONCEPT.md, NOTES.md, CREDITS.txt; motion/films/blog-fuma-nama/SCRIPT.md, CONCEPT.md, STORYBOARD.md, NOTES.md; motion/films/blog-designing-docs/NOTES.md, STORYBOARD.md; motion/films/journey-to-the-west/CRITIQUE-1.md, NOTES.md; motion/films/modern-hebrew/NOTES.md.
  • HyperFrames 0.8.106 itself: the render command's quality aliases (delivery maps to high, preset slow and CRF 15) and enforceAacTruePeak, read in the package's dist/ on 2026-10-05.
  • Prototemplate site: scripts/build-motion.mjs, src/lib/motion.ts, public/media/README.md, .gitignore.
  • Kevin's wiki: skills/engineering/hyperframes/SKILL.md, skills/engineering/hyperframes-core/SKILL.md, skills/engineering/hyperframes-cli/SKILL.md, skills/.runtime/all/hyperframes-audio/SKILL.md, skills/misc/elevenlabs-sfx/SKILL.md.
  • Claude memory notes: gt-motion-films (seven rounds of Kevin's direction, 2026-10-01 to 2026-10-05), elevenlabs-account (narrator casting and the key's handling), session-lanes-prototemplate (Kevin, 2026-10-03).
  • Short media: Kevin's messages on the open-source promo (2026-08-18 and 2026-08-19), launch captures (2026-08-10, 2026-08-12, 2026-08-14) and the vendor brief (2026-09-30); public/media/README.md.
  • Kevin's directives: 2026-10-01 (the films, round 4, the sound pass), 2026-10-02 (rounds 6, 7, 7b, 7c and 7d; the series films), 2026-10-03 (session lanes), 2026-10-04 (the frame, the reassembly), 2026-10-05 (the series scripts, the series narrator, the dictionary pictures).
Section 39 files

Files

The folder skills/gt-films as agents fetch it. Each file opens raw.