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
- 01 Install
npx skills add mhfrough/sizzle - 02 Ask “Make a promo reel for this app.”
- 03 Approve Watch the preview and call picture lock.
- 04 Render 750 frames to a 4K MP4, cut to music.
Now cut yours.
Type your app’s name and one line. The reel recuts as you type. Copy the prompt when it looks right.
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
| TC | Scene | On screen |
|---|
CThe prompt
Not installed yet?
npx skills add mhfrough/sizzleSeven 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
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.
| 0.0–3.0 | Hook. Name the pain in one line. Strike out the old workarounds. |
|---|---|
| 3.0–5.3 | Reveal. Mark, name and the product’s own tagline, full screen. |
| 5.3–10 | Demo 1. One headline, and the real UI doing it. |
| 10–15 | Demo 2. One headline, and the real UI doing it. |
| 15–19 | Proof. The promise, kept. |
| 19–25 | Call to action. “Try it.” The address types itself. Hold two seconds. |
Claims check
| Claim | Source |
|---|---|
| “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
- ABright future bass1:43build, then a dropPicked
- BPlayful pizzicato2:05steady, light
- 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/sizzleAdds 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.
macOS / Linux
curl -fsSL https://mhfrough.github.io/sizzle/install.sh | shWindows PowerShell
irm https://mhfrough.github.io/sizzle/install.ps1 | iexPuts the skill in ~/.claude/skills/sizzle. Run it again to update.
macOS / Linux
curl -fsSL https://mhfrough.github.io/sizzle/install.sh | sh -s -- cursorWindows PowerShell
$env:SIZZLE_AGENT='cursor'; irm https://mhfrough.github.io/sizzle/install.ps1 | iexPuts the skill in ~/.cursor/skills/sizzle. Restart Cursor, then ask Agent for a reel, or type /sizzle. Cursor also loads a Claude Code install, so you only need one of the two.
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/sizzleAny 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.txtRequirements 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.