~/satyajit

Scenario's kinetic-film skill: a launch film as one web page, and what 'reverse engineered' can mean

mdjsonmcp

2026-11-01 · 17 min · agents · code-generation · video · audio

Why read this

Solidtop 85%

Reads all 34 files of the skill, parses its three specs, and rebuilds its still-frame gate to measure what counts as motion; the engine itself was not run.

  • Original analysis
  • Concrete numbers to act on
  • Explained from first principles

Agents & harnessesRuns on a laptop CPUNo licencePractitioner teardown

How this was scored
Is it new?
1 of 3: An incremental tweak
Can I trust it?
2 of 3: Measures key facts from files, code or configs
Can I run it?
1 of 3: API-only, gated or restrictive licence
Will I understand it?
2 of 3: Mechanism from first principles with figures
Can I act on it?
2 of 3: A concrete recipe, numbers or comparison
Will it last?
1 of 3: Relevant for months
Does it affect many?
1 of 3: A specialist community
Only here?
2 of 3: A teardown or measurement few others did

Score 54 of 100, ranked 502 of 666 rated articles. Each question is answered 0–3 by hand, and a 3 is rare. How articles are scored

A post from Scenario's account says, in the words that reached me: "R.I.P. Higgsfield Katana. We reverse engineered it into a Claude skill you can feed your agent." You describe a product, game or brand in a few lines, and Claude writes, animates and cuts a kinetic-typography launch film. I did not open the post itself; the quote is as relayed. The skill it points to is on scenario.com/explorations/kinetic-film as a ZIP.

I downloaded it and read all of it. This article is about what is in the box, what it asks an agent to do, what it costs, and what the phrase "reverse engineered" does and does not tell you. I also rebuilt the skill's quality gate to see what kind of motion it can detect. What I could not do is render a film: the permission layer in my sandbox refused the command that set up a film folder from the downloaded scripts, and I stopped there rather than route around it. Everything below is labelled measured (I computed it from the files or from clips I made), reported (the page or the skill says so) or reasoned.

For the sibling question of what a one-shot launch video actually contains, see One-shot launch videos. This skill is another data point of the same kind.

What you download

The ZIP is 568,073 bytes, 34 files in 40 archive entries, 1,318,415 bytes unpacked (measured). Its SHA-256 is 2217ab5d...3ebf, which is also the path segment of the download URL, so the address is content-addressed and you can check what you got against what you were pointed at (measured).

PartWhat is in itSize
SKILL.md + references/the instructions: method, blocks, craft, sound, render, MCP loop15 KB + six files
engine/blocks.js 1,425 lines, core.js 396, compositor.js 150, main.js 54, index.html, three OFL fonts2,025 lines of JS (measured)
scripts/17 files: render, mix, audit, beat grid, finish gatePython, Node and shell
examples/starter, kaiju-postal, just-code specsthree spec.js

There is no licence file for the skill itself. The three fonts (Archivo, Instrument Sans, JetBrains Mono) each ship their SIL OFL text. The page text I fetched showed none either (reported). So the honest label is "no stated licence": you can read it and run it for yourself, and whether you may redistribute it is a question the files do not answer. I marked the frontmatter unlicensed for that reason, not as a judgement.

There are no API keys anywhere in the code. A grep for network calls finds two things: a local http.server for previewing, and a curl in get_fonts.sh that fetches fonts from the google/fonts repo only when the shipped ones are missing (measured). The paid part goes through Scenario MCP, which the agent calls with your account, and references/scenario-mcp.md tells the agent to price every generation with dry_run and wait for a go before the first paid run (reported).

The idea: a film is a function of time

The skill's central design is one sentence in SKILL.md: the engine turns a spec into one web page "where every frame is a pure function of time, so a renderer can seek anywhere and average sub-frames into real motion blur." Nothing is generated by a video model. The words on screen are the words in the spec, and a palette change rebrands the whole film.

That is a claim about code, so it can be checked in code. A case-insensitive grep over the four engine files for random and Date. returns nothing (measured). The one use of performance.now is in the preview loop in main.js, which is skipped when the URL has render in it. The film grain added at render time uses a seeded NumPy generator, eight tiles reused modulo frame number, so a re-render matches (read in post.py). I have not diffed two renders, so "deterministic" is read, not demonstrated.

The spec is plain data. This is the whole starter file's skeleton:

export default {
  title: 'Product',
  bpm: 170, beats: 42, fps: 60,
  palette: { bg: '#0D0F14', paper: '#F2F0EA', accent: '#3D7BFF', hot: '#1F5BFF', ink: '#12141A' },
  scenes: [
    [Words, 0, 8,  { charge: true, shots: [ { at: 1, style: 'punch3d', text: 'MEET', fx: 'punch' }, /* ... */ ] }],
    [Strike, 8, 17, { items: [ /* NO X. NO Y. */ ] }],
    [Hero, 17, 25, { kicker: 'INTRODUCING', title: 'PRODUCT', sub: '...' }],
    [Logo, 31, 42, { wordmark: 'PRODUCT', tagline: '...' }],
  ],
}

Every time in the file is in beats. A scene is [Block, startBeat, endBeat, options]. Change bpm and the whole film retimes. That is the part I would copy even without the engine.

I parsed the three example specs as text, never by running them (measured):

SpecBPMBeatsLengthFrames @ 60Scene entries
starter1704214.82 s8895
kaiju-postal1726823.72 s1,4238
just-code173.7515051.80 s3,10815

just-code matches the page's headline numbers: 51.8 s and 3,108 frames (reported, reproduced). Two small differences. The page says 174 BPM, the spec says 173.75, which is rounding. The page says 14 scenes; the spec has 15 entries, because the Logo starts at beat 139 while the last Words scene runs to 140 and so overlaps it by one beat. Neither matters. They are the kind of difference you get from counting by eye on one side and by regex on the other.

The engine has 14 block classes: Words, Strike, Graph, Hero, Prompt, Stretch, Fly, Notes, Counters, Progress, Chain, Rewind, Picture, Logo (measured, from export class lines). The page speaks of 13 block types for its own film, which is the same list without Picture, the one that shows generated key art.

What the agent is asked to do

The workflow in SKILL.md has five steps, and the instructions are specific enough that the agent's job is mostly arithmetic and judgement, not invention.

  1. Brief to beat sheet. Choose a tempo and length from a table (teaser 15 to 25 s at 160 to 175 BPM; launch 40 to 55 s; calmer 30 to 45 s at 120 to 128 BPM), write the words as a spoken script, then cut it to one or two words per beat. method.md maps phrase types to blocks: a denial becomes Strike, a number that kills an old way of working becomes Graph, the brief itself becomes Prompt. It also insists that on-screen numbers come from the film ({lines}, {frames}, {seconds}) or the user. "An invented statistic in a launch film is the first thing a reader checks."
  2. Sound and pictures through Scenario MCP. A music bed at the sheet's tempo, a kit of about 15 one-shot sounds (twelve core names, each with a prompt and a duration in sound.md), and key art if the film shows any.
  3. Write spec.js.
  4. Preview and stills. Scrub in a browser; take stills at beats.
  5. Render at 1080p, pass the gate, render 4K once.

The interesting design decision is that the paid, non-deterministic parts are exactly the parts code cannot draw: music, sound effects, pictures. The motion is free and repeatable. If you change one word you re-render in minutes and pay nothing; if you change the music you pay and re-measure the beat grid with beat_grid.py.

The tempo is where the two meet, and it is the one place the skill is nervous. A generated track's tempo is "a tempo you hope for" unless the model has a bpm input, so the skill tells the agent to prefer one that does, to measure the take, and to fit the grid on the stretch the film uses. It states the stakes as "a 0.15 % tempo error puts the last hits of a 50 s film 70 ms late."

That figure holds up (reasoned): 0.15 % of 51.8 s is 78 ms, which is 4.7 frames at 60 fps. And a beat at 172 BPM is 20.93 frames, so every cut is rounded to a frame boundary, at most 8.3 ms off. The rounding is invisible; the drift is not.

The render path, read line by line

render.py serves the film folder on a local port, asks the page for its length and cuts, splits the frames across workers, and joins the parts. Each worker (post.py) starts a headless Chrome (render.mjs) that seeks the page to each sub-frame time, takes a JPEG screenshot, and streams it to Python. Python averages the samples (motion blur), adds a bright-pass bloom at quarter resolution and deterministic grain, and pipes frames to x264.

Details I found by reading that you would otherwise learn the expensive way:

On this sandbox (15 GB, 4 vCPUs) that formula gives one worker at either resolution (reasoned from the code). The skill's own timing is "a 24 s film renders at 1080p in about 4 minutes with 4 workers, and in 4K in about 10 minutes with 3; a 52 s film in 4K takes about 18 minutes" on a 36 GB laptop (reported). Folding those into a per-frame cost: 4 minutes by 4 workers over Kaiju's 1,423 frames is about 0.67 worker-seconds a frame; 18 minutes by 3 workers over 3,108 frames is about 1.04 (reasoned, from rounded figures). A single worker on a 4K master of the long film would then be near an hour on a laptop-class core, and I would expect more on a shared vCPU. I could not check that.

The gate: what "parked" means to a script

The skill's best idea is not the blocks. It is that "reads as professional motion design" is turned into three numbers, taken from reels it says it measured frame by frame (reported): no more than 40 % still frames, no hold over 0.6 s before the end card, and a median motion of at least 2.5 px per frame. finish.py fails the delivery if any are missed, and also checks frame count, size under 1 GB, H.264 level, and loudness between -16 and -12 LUFS.

I could not run finish.py or dyn.py (they need OpenCV, and the command that set up a folder and installed it was refused). But the still-frame test is eight lines of NumPy: decode to 320x180 grey, mark a pixel changed when it moves by more than 24 levels, call a frame still when under 1.5 % of its pixels changed. I rewrote exactly that, with my own code and ffmpeg, and fed it clips I made.

Ten seconds at 60 fps, 172 BPM, x264 CRF 18 (measured, my reimplementation of the gate, not the skill's scripts):

ClipStill framesLongest stillGate (40 %, 0.6 s)
flat colour swap every 1.5 s, static block99.0 %1.48 sfail
colour flip on every beat, static block95.3 %0.33 sfail
every-beat flip + box 1/3 of the frame moving 6 px/frame95.0 %0.33 sfail
same, 12 px/frame94.5 %0.33 sfail
same, 24 px/frame93.7 %0.33 sfail
same, 48 px/frame0.0 %nonepass
same, 96 and 192 px/frame0.0 %nonepass

The second row is the finding. A film that cuts on every beat has a hold of 0.33 s, which sails under the 0.6 s limit, and is 95 % still by the other test. The two checks are not redundant: the hold check catches slideshows, the still-share check catches a film that cuts often and moves nowhere. "Cut on the beat" is not the same as "never park", and the gate knows it.

The cliff between 24 and 48 px/frame is the other finding. For a solid box the changed area per frame is the leading and trailing edges, 2 * min(v, width) * height. The gate wants that to pass 1.5 % of the frame, so the speed that counts is 0.015 * 1920 * 1080 / (2 * height): 43 px per frame for a box a third of the frame high, which is 1.35 frame-widths per second at 60 fps (reasoned, and consistent with the six clips).

Slide the height down to the size of a line of type and the number goes up fast: a band 120 px tall has to move 130 px a frame to register. That is a statement about the gate, not about the films. craft.md recommends "a slow scale (2 to 5 % per second), a slide (70 px/s for type rows)" to keep things alive after a word lands. By this metric those do nothing: 70 px/s is just over 1 px a frame. They may still read as alive to a viewer, and the median-flow check uses optical flow, which I did not reimplement and which sees small motion the area test does not. But the 40 % still-share gate is carried by the big moves (whips, cuts, flashes, colour flips), and the texture under them is invisible to it.

One more reading of finish.py: the dynamism checks run only up to duration - 4.5 s, so the end card is exempt. In kaiju-postal the Logo is 11 beats, 3.84 s, so the exempt window covers it plus the last 0.66 s of the scene before it (reasoned from the spec and the code). A hold there would pass.

What the page's own film claims

The page reports its film at 30 % still frames (allowed up to 40), no hold over 0.6 s, 8.5 cuts per 10 s, under 1 GB (reported). The Kaiju example reports 24 % still, no hold over 0.6 s, 12.5 cuts per 10 s (reported in SKILL.md and method.md). I could not reproduce any of these, since that needs the rendered films. What I can say is that they are produced by a gate whose definition I have just read and re-run in part, so they are not a vibe. They are also the film's authors' numbers about their own film.

The just-code spec credits "OPUS 5.5" on screen (read in the spec). That is a statement in the film, not something the files verify. The page says the film was rendered on a laptop; nothing in the render path needs more than Chrome, ffmpeg and Python (reasoned).

"Reverse engineered": what it tells you

The skill never uses the phrase. A search of every file in the ZIP for Higgsfield, Katana and reverse finds one hit, and it is "reverse cymbal swell riser" in sound.md (measured). The page does not use the phrase either (reported). The claim lives in the post, and I have not used the other product, so I cannot compare against it.

What can be said from the files is narrower. SKILL.md describes the target style as "the style of viral motion design reels (huge words on every beat, whip pans, colour flips, 3D letters the camera flies through, a hard drop)". The method says its energy targets were "measured on the references this style comes from" with dyn.py, with a table for the fastest reference (16 % still, 0.37 s longest still, 9.1 hard cuts per 10 s) and calmer ones. And the scripts include rectify2.py, which tracks the phone's camera on the static surroundings and warps a filmed screen flat, so that a reference filmed off a monitor can be measured. method.md adds: "Match its register with your own words and brand; do not copy its script, its logo or its frames."

Put together, that is consistent with one reading of "reverse engineered": someone measured the look of finished output (cut rate, hold length, motion, devices like whip pans and extruded type) and wrote an engine that produces films with the same measurements. It is a reconstruction of a style from its outputs. It tells you nothing about how any commercial product works inside, and the skill does not claim it does. It also tells you the opposite of what "clone" might suggest: there is no UI, no API and no template library here, only a spec format, 14 blocks and a gate.

That reading is mine. The files support it; they do not prove it is what the post's authors meant. If you are evaluating this skill against another tool, compare films, not that sentence.

What it costs, and what it needs

Where it breaks

A few things I would test before relying on it, none of which I could.

  1. The first spec an agent writes. The gate catches parking after a 1080p render, minutes in. A quicker loop would run the same still-share test on snap.mjs stills before rendering. The skill does not do that; it is a modest addition.
  2. Text fitting. Blocks measure and scale words to the frame, and the mistakes list says custom code must measure once, while visible. The 14 blocks cover a lot, but a brief that wants a layout none of them has means writing a custom block, documented at the end of blocks.md.
  3. Memory at 4K. At 11 GB a worker, a 16 GB machine renders on one worker, and the skill's own warning is that six workers "filled 36 GB of memory and stalled the machine".

What I would take from it

Three things transfer even if you never run the engine.

What I did not establish: that the engine renders what the page shows, how long it takes on your machine, what the music and sounds cost in Scenario credits, or how it compares with the product named in the post. The first is the one I would run next: a made-up brand through new_film.sh, snap.mjs at a few beats, and a 1080p render, with dyn.py on the result.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "Scenario's kinetic-film skill: a launch film as one web page, and what 'reverse engineered' can mean", ai.thesatyajit.com, November 2026.

bibtex
@misc{ghana2026scenariokineticfilmskill,
  author = {Satyajit Ghana},
  title  = {Scenario's kinetic-film skill: a launch film as one web page, and what 'reverse engineered' can mean},
  url    = {https://ai.thesatyajit.com/articles/scenario-kinetic-film-skill},
  year   = {2026}
}
share