Sizzle
Star on GitHub Install

Your app, now showing

A skill for AI agents. It reads your code, cuts a 25-second reel from your real interface, and renders it to 4K with music.

npx skills add mhfrough/sizzle

Free and open source. Works with Claude Code, Codex, Cursor and any agent that reads skills. Other ways to install

How it runs, in four steps

  1. 01 Install npx skills add mhfrough/sizzle
  2. 02 Ask “Make a promo reel for this app.”
  3. 03 Approve Watch the preview and call picture lock.
  4. 04 Render 750 frames to a 4K MP4, cut to music.

Out now · v1.0

25 s · 9:16 · 4K · MIT

Cut yours

Now cut yours.

Type your app’s name and one line. The reel recuts as you type. Copy the prompt when it looks right.

AEditing bay

Features One idea each
Accent Your colour, not ours
Cut
Type
Sizzle reel 1080 × 1920 · 30 fps

00:00:00/ 00:25:00

Hook

Drag to scrub. ← → one frame, with Shift one second, Space to play.

A sketch, cut in your browser. The real skill reads your code, rebuilds your actual interface and renders 750 frames to 4K, cut to music.

BShot list

Sizzle reel · 25 s · 1080 × 1920

TCSceneOn screen

CThe prompt

Not installed yet?

npx skills add mhfrough/sizzle

Seven steps. Two of them wait for you.

Nothing renders until you say yes.

The agent reads, writes, cuts and checks on its own. Then it stops twice: once for you to lock the picture, once for you to pick the music.

25 s9:1630 fps2160 × 3840

Monitor

01 Research

~/your-app

  • your-app/
  • README.mdpromise, features, what it doesn’t do✓
  • index.htmltagline as written, live URL✓
  • css/app.csscolour tokens, light and dark✓
  • fonts/*.woff2the font files✓
  • icons.svgthe icon set✓
  • favicon.svgthe mark✓
  • og-image.pngthe share image✓
  • src/render.jsa renderer the reel can drive✓

Notes before a word is written

Promise
The one line, word for word.
Features
Three to five real ones.
Not
What it does not do.
Claims
Only what the code backs.
Brand
Tokens, fonts, icons, mark, URL.

Screenshots are checked for private data. All demo data is invented.

Read, don’t guess. The reel will use your own stylesheet and renderer, so it stays sharp at 4K and can’t drift from the real look.

02 Script

promo/your-app-reel-script.md

Your app reel: shot list

25 seconds, 9:16. 1080 × 1920 CSS, rendered at 2160 × 3840, 30 fps.

Shot list
0.0–3.0Hook. Name the pain in one line. Strike out the old workarounds.
3.0–5.3Reveal. Mark, name and the product’s own tagline, full screen.
5.3–10Demo 1. One headline, and the real UI doing it.
10–15Demo 2. One headline, and the real UI doing it.
15–19Proof. The promise, kept.
19–25Call to action. “Try it.” The address types itself. Hold two seconds.

Claims check

Claims check: every claim on screen and where the product backs it
ClaimSource
“No sign-up”README, Features
“Open source”LICENSE (MIT)

Headline check

Still doing it14/16

the slow way?13/16

  • One idea per headline
  • Two lines at most
  • 16 characters a line
  • Every claim traceable

The proven arc for 25 seconds: hook, reveal, two to four demo beats, proof, call to action. Every claim on screen gets a source in the table.

03 Cut

promo/your-app-reel.html

var CUES = {  a: .15, strike: 1.75, aOut: 2.75,     // hook  wipe: 2.95, wipeOut: 5.05,            // reveal  brandIn: 5.3,  demo: 5.45, tapA: 7.2, demo2: 10.5,   // demo beats  proof: 15.5,                          // proof  cta: 19.5,                            // call to action  end: 25                               // reel length}; <span data-in="demo" data-out="demo2">Feature one,</span>
Canvas
1080 × 1920 CSS, fitted to any preview window
Safe zones
No text in the top 190 px or the bottom 380 px
Real parts
Your stylesheet’s classes, or your renderer driven with scripted data

Built from the template: every time lives in CUES, and elements hang off it with data-in and data-out. A retime is one edit, and the preview runs in any browser.

04 Dailies

…/stills/sheet.jpg

$ python reel.py stills promo/reel.json 1.5,4,7,10,13,16,19,23
  • Stray dots or lines before their cue1 at t1.50, fixed, retaken
  • Elements visible too early
  • Overlapping headlines
  • Clipped or overflowing text
  • Unreadable contrast
  • Empty states left empty
  • Console errors
contact sheet …/your-app-reel/stills/sheet.jpg

Before anyone is asked, the agent renders key frames, reads the contact sheet and fixes what it finds. The pitfalls list holds every bug class hit on real reels.

05 Picture lock

Gate · waits for you

Agent

The cut is ready. Hook, reveal, two demo beats, proof, call to action: 25 seconds.

Previewlocalhost:8123/promo/your-app-reel.html

Key frames…/stills/sheet.jpg

Approve it, or tell me what to change?

You

Approved. Lock it.

Until this yes: no 4K frames, no music downloaded.

Each gate exists because rework after rendering is expensive. Ask for changes and it goes back to the cut. Ordered several reels? One is approved before the next is started.

06 Mix

Gate · asks before downloading

Shortlist · free for commercial use · no Content ID · not AI-generated

  1. ABright future bass1:43build, then a dropPicked
  2. BPlayful pizzicato2:05steady, light
  3. CWarm synth-pop2:31slow build
$ python -I music_sync.py promo/your-app-bgm.mp3 --cue 2.95 --length 25
track 103.0s, beat ~0.47s (~128 bpm, or double that)
loudness per second (dBFS): …

hits, and the offset that lands each on the cue at 2.95s:
  hit   57.02s  offset   54.07  pre  -14.6 dB  body   -8.9 dB  dips at -  fits yes
  hit   25.61s  offset   22.66  pre  -11.2 dB  body   -9.4 dB  dips at [12]  fits yes

Music comes only after picture lock, so the cut is final. The offset that puts the drop on the reveal goes into reel.json, under a quieter build beneath the hook.

07 Print

videos/your-app-reel.mp4

Frames2160 × 3840 JPEG, fake clock

VideoH.264 High, crf 16

AudioAAC 192k, −14 LUFS, under −1 dBTP

$ python reel.py all promo/reel.json
$ node …/render.js frames http://localhost:8123/promo/your-app-reel.html …/frames 30 2
ready, duration 25
frame 720 / 750 144s
  encoded 750/750 frames (68s)
---- mux summary ----
output      : …/videos/your-app-reel.mp4 (38.2 MB)
video       : 2160x3840, 750 frames @ 30 fps, H.264 High crf 16 preset slow
duration    : 25.000 s
audio       : AAC 48 kHz stereo 192k | VO=no BGM=yes
loudness    : -14.0 LUFS integrated (BS.1770-4 K-weighted, gated); pre-normalisation -23.2 LUFS, gain +9.2 dB
peak        : -4.03 dBTP (4x oversampled), -4.31 dBFS sample peak
elapsed     : 71.4 s
poster …/videos/your-app-reel.webp from 00240.jpg

A fake clock steps the page through every frame, so each animation lands where the timeline says. No ffmpeg: PyAV encodes it. A new track only needs reel.py mux, not new frames.

This trailer was made by Sizzle.

Same skill, same seven steps, pointed at its own repo.

Warming up the projector...

Reel 01

Sizzle trailer

The Sizzle trailer, made by Sizzle about itself. 25 seconds, music by BombinSound.

Cues

    Contact sheet
    Mix report

    Click the projector to run the reel from the top. Point at the audience to hear what they think.

    The room needs JavaScript. Watch the trailer as a video file.

    Starring one folder of plain files.

    Read every one before you run it.

    starring SKILL.md with AGENTS.md

    templates reel-engine.html reel-script.md and reel.json

    tools reel.py render.js mux.py and requirements.txt

    sound by synth.py and music_sync.py

    no two alike, checked by fingerprint.py

    references concepts.md direction.md craft.md and sound.md

    with pitfalls.md case-studies.md and techniques/

    Markdown, HTML, JSON, Python and one JavaScript file, plus three open-licence fonts for the reels. No build step. Open the folder on GitHub

    Runtime 25 seconds.

    750frames, each one rendered

    Frame rate
    30 fps
    Picture
    2160 × 3840, 9:16
    Video
    H.264 High, crf 16
    Sound
    AAC 48 kHz stereo, 192k
    Loudness
    −14 LUFS, peak under −1 dBTP
    Needs
    Node 18, Python 3.10. No ffmpeg
    Licence
    MIT

    A fake clock steps through the reel one frame at a time, so every animation and every beat lands where the timeline says.

    In your terminal now.

    One line. Then say: make a promo reel for this app.

    npx skills add mhfrough/sizzle

    Adds it to Claude Code, Codex, Gemini CLI, OpenCode and the other agents the skills tool supports. For Cursor, use the Cursor tab: it puts the skill where Cursor looks.

    By hand

    Copy skills/sizzle/ from the repository into ~/.claude/skills/sizzle/ (Claude Code) or ~/.cursor/skills/sizzle/ (Cursor):

    git clone --depth 1 https://github.com/mhfrough/sizzle cp -R sizzle/skills/sizzle ~/.claude/skills/sizzle

    Any other agent: put the folder in your repo and add one line to your AGENTS.md: “For promo reels, follow skills/sizzle/SKILL.md.”

    The tools need these once per machine:

    npm i -D playwright && npx playwright install chromium pip install -r skills/sizzle/tools/requirements.txt

    Requirements Node 18 or newer with Playwright, Python 3.10 or newer. No ffmpeg, no editing app.

    Then say make a promo reel for this app

    Questions.

    Does it use screenshots of my app?

    No. It rebuilds your interface from your own stylesheet, or drives your own renderer, with made-up demo data. Text stays sharp at 4K, everything can move, and no real names or files end up in the video.

    Where does the music come from?

    Either a free track that allows commercial use, is not registered for Content ID and is not AI-generated, or a music bed Sizzle makes on your machine. For a track, the agent shortlists a few and asks before downloading one. Either way the drop lands on your reveal, and the sound effects can be made locally too.

    Can I change it after approving?

    Yes. Every time sits in one list of cues, so a retime is one edit and a re-render. A new track or offset only needs the mix step again, not new frames.

    What does it cost?

    Nothing. Sizzle is MIT licensed, and everything renders on your own machine.

    More free tools by Mohammad Hamza.