Skip to content

cinetic v1.1.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 00:26
· 960 commits to main since this release

Calm pace by default and fragment chains

cinetic's films now move at a calm pace, and their copy arrives as short fragments, one at a time, instead of whole sentences. A frame-by-frame study of eight professional films (how they split sentences, how long a fragment stays readable, how text and picture take turns) became the copy grammar; the house default keeps that grammar and holds each fragment about 1.5× longer than the references did.

  • Fragment chains. A fragment is 1–4 words. A longer thought is a chain of fragments, in build mode (appending to one held line) or replace mode (each leaving whole before the next rises in its place), with a picture beat of 30–90 f between them, in which the picture really moves: "here is the newest" → the product arrives → "earshot." references/copy-and-type.md §2b gives the break rules, eleven recipes (the animation as the verb, the cliffhanger cut, a stem with a rotating slot, an image as a word, a giant keyword with a small continuation, edit instead of replace, a persistent container, emphasis by colour, text-free stretches, quiet after the text), a timing template in frames and six before-and-after examples.
  • Calm by default, crisp on request. PACE.calm and PACE.crisp drive word timing, reading holds and the checks. Calm words enter over 36 f, 11 f apart, rise 0.2 em without blur, hold max(60, 42 + 8 f per word) and leave together over 24 f; crisp keeps the 1.0 timing. Text-led films run at 100 BPM, one fragment per 1–1.5 bars of 2.4 s, with 24–36 events per 30 s instead of 45–60, shots of 100–160 f and a visible change about every second.
  • Holds alive, not frozen. A reading hold with a 0.3–1 px/f drift or a 1–3% creep counts as life, so a film no longer has to plant a tick inside a read to pass the gates. Calm never turns static: the energy arc, the moving frame 0, the moving last third and the building end card all stay.
  • No springs on type. Words settle on eases or the critically damped SPR.calm; overshoot and per-letter tricks on copy join the hard bans.

Sound from earshot kits

A film's effects now come from an earshot sound kit instead of cinetic's own synth voices: one sound per role, designed in code, rendered into takes with measured sync points, audited by measurement, and placed on the cue frames by score.py, which keeps the timing, typing plan, levels and master. The synth voices stay as the fallback, with a loud warning. Calm text-led films play effects only, and the scoring rules for text come from a frame-measured study of professional text-led films: one soft sound per fragment, near-silence while it is read, a big hit only for the one reveal, and a lockup that is a stop-down and one clean element.

Added

  • src/fx/Chain.tsx in the Remotion starter renders a chain in build or replace mode from COPY, set in a new quiet line type role, and gives the frame each fragment visibly lands for its sound (chainResolves(id, { size }): its last word's drawn rise, through arrive(), 97% home at the size the act sets it at). chain(id, mode, [[text, inFrame], …], { out }) in timeline.ts writes one COPY entry per fragment (<id>.1, <id>.2, …) with its chain and mode, timed from the pace; line() does the same for a single fragment and chainOf() reads a chain back.
  • src/lib/pace.ts with PACE.calm and PACE.crisp, picked by PACE_NAME in timeline.ts; pace, easeOf, durFor, E.expo and SPR.calm in src/lib/anim.ts; the same profiles in the HyperFrames starter's motion.js.
  • One reading-window rule for every check: readingWindows() in src/lib/pace.ts (and motion.js), exported by the timeline as READ. A fragment is read from resolved for at most readMax; an earlier fragment of a chain only for its own hold, so the picture beat between two fragments is never taken for a read; a build line once more after its last fragment. grid-check.ts, layout-audit.sh, measure-speed.py and forensics.py read it (the scripts through out/cues.json), and the layout audit's word cap is 5 when every word on screen belongs to a replace chain.
  • export-cues.ts writes a copy block (every COPY entry as {id, text, in, resolved, out, words, kind, chain?, mode?, read}) and the film's pace to out/cues.json, and the sound gets one soft text event per fragment landing instead of one per word.
  • grid-check.ts checks the text flow against the film's own pace: fragment, copy-concurrent (a leaving fragment counts until its exit is over), read-first (two separate lines arriving together), reveal-rate, chain-gap, chain-beat (a cue inside every picture beat), read-quiet (no cue starting inside a read), word-rate and text-share; in a calm film a short or empty picture beat, lines arriving together and copy on more than 75% of the body are errors. Its event-gap check counts a reading hold as covered. It also reads a HyperFrames timeline.js (with the pace from motion.js): node scripts/grid-check.ts timeline.js.
  • lint-film.mjs rules copy-literal (also a sentence written one word per element), text-spring (followed by name, never through object keys, strings or .property names, and any element given an overshooting cfg/config/spring preset around text), text-pace, typing-rate (followed through the names a count is built from) and per-letter (strings only, not arrays).
  • references/hyperframes-engine.md §11.6: a GSAP recipe for fragment chains and the timeline check.
  • scripts/test/grid-check.test.mjs: the timeline checks on fixtures, both starters (at 120 and 100 BPM for HyperFrames, the Remotion statement as a replace chain, each text sound against its words' visible stop) and a new-film.sh --pace crisp scaffold. CI installs the Remotion starter, type-checks it and runs this test and score.test.py (against main's score.py, bit for bit on the synth engine).
  • taste-and-slop.md names B8 Sentence dump, B9 Subtitle film, K7 Hurry and F11 Springs on type.
  • craft-rules.md adds the eight-film study of fragment flow (words per beat, hold, entry and exit, beats that continue a sentence, frames with text) beside the calm default.
  • A fifth eval, a calm 20 s update film for X, and text-flow expectations (words per frame, fragments, reading time, no overshoot on copy) on every brief.
  • scripts/audio/kit.py: the film's sound kit. init --preset calm|crisp|minimal (from earshot, tuned to the film's key), tune, render (cached, into audio/kit/ with kit.lock.json), vendor, check (the kit against the film's cues plus earshot's audit of every take), audition, listen and where. It finds earshot through --python, score.json sfx.python, EARSHOT_PYTHON (none turns it off), an importable earshot, or earshot on PATH.
  • scripts/audio/listen.py: earshot's timeline with the cues, its annotated sound sheet and its critique in one process, into out/qa/listen.json and out/qa/sound_sheet.png; --kit listens to each kit role against cheap-synth alternatives. Without earshot or its models it records why and exits 0.
  • score.json "sfx": {"engine": "auto" | "earshot" | "synth", "kit", "python"}. auto renders the kit when it changed and earshot is found, else plays the rendered takes as they are, else the synth voices with a warning; earshot fails instead of falling back.
  • score.json "music": "none" | "synth" | <track>. none (effects only) needs no chords and masters at −18 LUFS by default, so a few accents keep their dynamics; synth is the built-in band, the default for a score that has chords.
  • Cue kind text: the soft sound of a copy fragment resolving, in export-cues.ts and the starter's sync.ts. A chain's fragments step through the chord's tones.
  • A stack guard in score.py: a warning, and a stacks row in the report, when a second sound lands within ±3 f of the payoff or the lockup cue (a score.json motif counts) or a whoosh, riser, swell or suck runs into it, and when two heavy sounds or three kinds start within ±3 f anywhere.
  • The report's sfx block (where every effect came from: the kit's roles with their takes, the cues of roles the kit leaves off, the kinds still on synth voices) and target_lufs; the console prints the source and the limiter's gain reduction. Warnings for a kit role whose pitch climbs or whose hit stacks attacks (earshot's audit), and for every synth riser.
  • npm run kit and npm run listen in both starters; each starter ships the calm kit as audio/kit.json.
  • scripts/test/score.test.py: the synth engine renders bit for bit as before, a kit's takes land on their frames, the fallbacks, the stack guard, effects-only scores, tuning; with EARSHOT_PYTHON set, a real kit render.
  • references/sound.md §1 (where the sounds come from: the kit, its roles, the fallback), §5.7 (scoring a text-led film) and §9.1 (listening with earshot); new rows in the report table, the failure modes and the sound-slop table (a rising synth whip, a stacked sting, a sound on every word, a synth pad bed under quiet type).
  • docs/setup.md: "Sound from earshot" (installing earshot in its own environment, EARSHOT_PYTHON) and sound troubleshooting rows.
  • CI parses every shell script with bash 3.2.57, the version macOS ships, and runs the HyperFrames preflight (npm run cues) under it. A macOS job does the same with the real /bin/bash, scaffolds a HyperFrames project and refreshes its scripts with BSD sed and tar, and runs the encode-tags test on Homebrew's ffmpeg (7.1 or newer). CI also fails on a heredoc inside $( ), backticks or <( ), found by a multi-line scan (scripts/test/bash32.test.py), and checks the syntax of the Node scripts (node --check).
  • scripts/test/encode-tags.test.py, run in CI: every BT.709-tagged encode in the scripts sets setparams, and the hf-finish.sh and accumulate.py encodes write bt709/bt709/bt709/tv on the local ffmpeg.
  • scripts/test/scripts-docs.test.mjs, run in CI: every script but the two internal helpers handles --help, hf-preflight.cjs exits 2 with its pointer, the docs name the same helpers, and docs/setup.md refreshes a project's scripts with new-film.sh's own copy.

Changed

  • SKILL.md bans full sentences dumped at once, gives each format its total words, new words per fragment and words visible at once, gates Step 1 on fragments, chains and text share, and adds a "Text flow" subsection. The timing and motion systems state both profiles. Text and the hero action take turns, and fast moves are rare and never run while text is read.
  • Word budgets: launch films 15–25 words, capped at 30 (was 35); product videos 12 (was 15); loops 6 (was 8); stings the name plus 3 (was 4); walkthrough captions 5 (was 6).
  • The starter film runs at 100 BPM, and its statement is a two-fragment build chain with a picture beat between the fragments and no pulse planted in the read: the dot is born only after the first fragment has been read, lands on the downbeat, and the second fragment answers on the off-beat. Its flight sets off on a beat and peaks half-way, on the grid, so new-film.sh --pace crisp (120 BPM) gives a starter that passes grid-check.ts too.
  • chain() in replace mode (both starters) ends a fragment's 24 f exit 6 f before the next one rises, so two fragments never share the spot.
  • The HyperFrames starter's fall takes its length from the grid (DUR.fall = CUE.hit - CUE.fall), so its contact meets the hit at any --bpm.
  • The treatment template's beat sheet carries each fragment with its mode and picture beat. The Plumb example runs at the calm pace and splits its 7-word UI note into a replace chain.
  • The director lens counts words per frame on the sheets and checks the text flow, the art lens checks the fragment rules, and the verifier checks text-flow issues on sheet frames rather than by eye at speed. Critics may propose splitting a sentence into a chain.
  • Motion characters per brand personality run about 1.4× longer, calm moves follow 0.5 s + 1.6 ms per px, and a line read as it types runs at about 20 characters per second.
  • The starters' lockup is a stop-down and one clean hit: no whoosh into it, no drop, snap or motif bells on it, no ticks through the hold. Their statements get one text sound per fragment instead of a pop and a tick per word, and their score.json plays effects only.
  • render.sh --build-audio renders the kit before the score; new-film.sh lists npm run kit and npm run listen in its next steps and says whether earshot was found.
  • av-audit.py: the loudness target follows score.py's default for the score (−18 for an effects-only film), the loudness range for an effects-only film is 3–14 LU, and events score.py's report lists as skipped (a role the kit leaves off) are not expected to sound.
  • The sound critic reads listen.json and the report's sfx and stacks; its fixes name a kit change, a new sound from earshot (a forge run, or a fit of a public-domain or owned recording), a cue or a score parameter. A stacked lockup, a rising synth whip and shipping on the synth fallback are P1.
  • SKILL.md Step 5 renders and checks the kit, scores text one fragment at a time, keeps the music off for calm text-led films, and listens with earshot; the spec line names the kit.

Fixed

  • macOS (#2). hf-finish.sh parses with the stock /bin/bash 3.2: its preflight was a heredoc inside $( ), which bash 3.2 scans as shell code, and now lives in scripts/hf-preflight.cjs (same checks, same cues). The other heredocs inside $( ), in deliver.sh and render.sh, became shell functions. The frame-count check compares numbers, so BSD wc's padded count passes; colour codes are stripped from the HyperFrames logs with BSD sed too; render-chunks.sh counts cores with sysctl where there is no nproc; new-film.sh matches motion.js with a POSIX character class instead of \s.
  • BT.709 tags with ffmpeg 7.1 and newer, which take a file's colour tags from the frames over -color_primaries and -color_trc. The HyperFrames encode came out without primaries and transfer (bt709/None/None/tv) and failed probe.py; hf-finish.sh, accumulate.py and the WebM in deliver.sh now end their filters with setparams. In accumulate.py, the encoder of every --blur master, the unlabelled raw planes also made ffmpeg insert a matrix conversion that shifted the colours; they now pass through bit-exact. With ffmpeg 6 the files are identical to before.
  • A project keeps its own copy of the scripts, so a project made with 1.0.0 gets these fixes when you copy the skill's scripts into it the way new-film.sh does, without the skill's own tests (docs/setup.md, "Updating a project's scripts").
  • hf-preflight.cjs, which hf-finish.sh runs, answers --help or a missing environment with a pointer to hf-finish.sh --help and exit 2 instead of a stack trace, and SKILL.md names it with brand-svg.ts as the scripts without a --help of their own.
  • .gitattributes checks every text file out with LF line endings. A Windows clone with core.autocrlf (the Git for Windows default) gave CRLF scripts, which WSL's bash refuses (set: pipefail: invalid option name).
  • docs/setup.md: removed a stale macOS row about date %N; render.sh uses only date +%s, which BSD date has, and times the sharp pass with python3.
  • new-film.sh --link-modules makes a directory junction on Windows (Git Bash, MSYS2, Cygwin), where ln -s copied the whole node_modules.
  • add-font.mjs starts npm on Windows (it failed with spawnSync npm ENOENT), extracts the package with a relative path, and replaces the import it supersedes in fonts.ts (the starter's stand-in or a family vendored before) on a CRLF checkout too.

Install with the skills CLI: npx skills add Leonxlnx/cinetic

Or download cinetic.skill below: a zip of the skill folder with cinetic/SKILL.md at its root.