Repository navigation
cinetic v1.1.0
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.calmandPACE.crispdrive 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.tsxin the Remotion starter renders a chain in build or replace mode fromCOPY, set in a new quietlinetype role, and gives the frame each fragment visibly lands for its sound (chainResolves(id, { size }): its last word's drawn rise, througharrive(), 97% home at the size the act sets it at).chain(id, mode, [[text, inFrame], …], { out })intimeline.tswrites oneCOPYentry per fragment (<id>.1,<id>.2, …) with itschainandmode, timed from the pace;line()does the same for a single fragment andchainOf()reads a chain back.src/lib/pace.tswithPACE.calmandPACE.crisp, picked byPACE_NAMEintimeline.ts;pace,easeOf,durFor,E.expoandSPR.calminsrc/lib/anim.ts; the same profiles in the HyperFrames starter'smotion.js.- One reading-window rule for every check:
readingWindows()insrc/lib/pace.ts(andmotion.js), exported by the timeline asREAD. A fragment is read fromresolvedfor at mostreadMax; 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.pyandforensics.pyread it (the scripts throughout/cues.json), and the layout audit's word cap is 5 when every word on screen belongs to a replace chain. export-cues.tswrites acopyblock (everyCOPYentry as{id, text, in, resolved, out, words, kind, chain?, mode?, read}) and the film'spacetoout/cues.json, and the sound gets one softtextevent per fragment landing instead of one per word.grid-check.tschecks 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-rateandtext-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 HyperFramestimeline.js(with the pace frommotion.js):node scripts/grid-check.ts timeline.js.lint-film.mjsrulescopy-literal(also a sentence written one word per element),text-spring(followed by name, never through object keys, strings or.propertynames, and any element given an overshootingcfg/config/springpreset around text),text-pace,typing-rate(followed through the names a count is built from) andper-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 anew-film.sh --pace crispscaffold. CI installs the Remotion starter, type-checks it and runs this test andscore.test.py(against main'sscore.py, bit for bit on the synth engine).taste-and-slop.mdnames B8 Sentence dump, B9 Subtitle film, K7 Hurry and F11 Springs on type.craft-rules.mdadds 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, intoaudio/kit/withkit.lock.json),vendor,check(the kit against the film's cues plus earshot's audit of every take),audition,listenandwhere. It finds earshot through--python,score.jsonsfx.python,EARSHOT_PYTHON(noneturns it off), an importable earshot, orearshotonPATH.scripts/audio/listen.py: earshot's timeline with the cues, its annotated sound sheet and its critique in one process, intoout/qa/listen.jsonandout/qa/sound_sheet.png;--kitlistens 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"}.autorenders the kit when it changed and earshot is found, else plays the rendered takes as they are, else the synth voices with a warning;earshotfails 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;synthis the built-in band, the default for a score that has chords.- Cue kind
text: the soft sound of a copy fragment resolving, inexport-cues.tsand the starter'ssync.ts. A chain's fragments step through the chord's tones. - A stack guard in
score.py: a warning, and astacksrow in the report, when a second sound lands within ±3 f of the payoff or thelockupcue (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
sfxblock (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) andtarget_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 kitandnpm run listenin both starters; each starter ships the calm kit asaudio/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; withEARSHOT_PYTHONset, 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 BSDsedandtar, 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 setssetparams, and thehf-finish.shandaccumulate.pyencodes writebt709/bt709/bt709/tvon the local ffmpeg.scripts/test/scripts-docs.test.mjs, run in CI: every script but the two internal helpers handles--help,hf-preflight.cjsexits 2 with its pointer, the docs name the same helpers, anddocs/setup.mdrefreshes a project's scripts withnew-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 passesgrid-check.tstoo. 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
textsound per fragment instead of a pop and a tick per word, and theirscore.jsonplays effects only. render.sh --build-audiorenders the kit before the score;new-film.shlistsnpm run kitandnpm run listenin 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.jsonand the report'ssfxandstacks; 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.shparses with the stock/bin/bash3.2: its preflight was a heredoc inside$( ), which bash 3.2 scans as shell code, and now lives inscripts/hf-preflight.cjs(same checks, same cues). The other heredocs inside$( ), indeliver.shandrender.sh, became shell functions. The frame-count check compares numbers, so BSDwc's padded count passes; colour codes are stripped from the HyperFrames logs with BSDsedtoo;render-chunks.shcounts cores withsysctlwhere there is nonproc;new-film.shmatchesmotion.jswith 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_primariesand-color_trc. The HyperFrames encode came out without primaries and transfer (bt709/None/None/tv) and failedprobe.py;hf-finish.sh,accumulate.pyand the WebM indeliver.shnow end their filters withsetparams. Inaccumulate.py, the encoder of every--blurmaster, 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.shdoes, without the skill's own tests (docs/setup.md, "Updating a project's scripts"). hf-preflight.cjs, whichhf-finish.shruns, answers--helpor a missing environment with a pointer tohf-finish.sh --helpand exit 2 instead of a stack trace, andSKILL.mdnames it withbrand-svg.tsas the scripts without a--helpof their own..gitattributeschecks every text file out with LF line endings. A Windows clone withcore.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 aboutdate %N;render.shuses onlydate +%s, which BSDdatehas, and times the sharp pass withpython3.new-film.sh --link-modulesmakes a directory junction on Windows (Git Bash, MSYS2, Cygwin), whereln -scopied the wholenode_modules.add-font.mjsstarts npm on Windows (it failed withspawnSync npm ENOENT), extracts the package with a relative path, and replaces the import it supersedes infonts.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.