CHANGELOG.mdChangelog
All notable changes to showtime. Each entry says what changed and why, so this file also answers
"where did X go?". Versions follow semantic versioning; the version lives in
skills/showtime/lib/st/__init__.py and python3 scripts/check_release.py keeps the plugin manifests, the registry files and
setup/package.json in sync with it.
0.2.0: every coding agent, a lighter first run, a real music catalog#
showtime is now a local video studio for your coding agent, not only for Claude Code: describe a video, your
agent directs, your machine renders. The same repository installs in Claude Code, Codex, Cursor, Devin and OpenCode
(each made a real test video) and is packaged for Copilot, Gemini CLI, Antigravity, Cline, Kilo Code, Kiro, Zed, Goose,
Amp, Factory Droid and Qwen Code. The first run fell from about 3 GB (0.1's own setup --estimate: 1.3 GB of downloads plus 1.7 GB of
packages) to about 770 MB on Linux x64 (setup --plan; about 560 to 610 MB on macOS and Windows); nothing was
removed, the rest is fetched the first time a video needs it. Music now comes from a catalog of 249 produced tracks with the credit written for
you, and Apple Silicon, Linux arm64 and Windows on Arm run end to end in CI.
Highlights (details in the entries below):
- Any agent.
showtime install --agent <name>, Agent Plugins manifests next to.claude-plugin/, a stable~/.showtime/bin/showtimecommand, background runs for hosts that kill long commands, MCP task ids, and a doctor that knows each agent's sandbox. Install steps per agent:docs/agents.md. - Smaller first run.
showtime setup --plan(per platform),--full(everything now, for offline use),--fetch,--prune; one fp16 Kokoro voice, Whisper, Manim, the audio library beyond a 41 MB starter part, icons and the browser fetched on first use, each announced with its size. Homes made by 0.1 keep working. - Music and sound. A catalog of 249 measured tracks (Scott Buckley, Kevin MacLeod, CC0 and public-domain
recordings), fetched from the creators on first use, credited automatically in
credits.txtand the post copy; 35 more sound packs; Openverse live search. - Platforms.
scripts/e2e.pyand a manual workflow run the whole path on Apple Silicon, Linux arm64, Windows 11 on Arm and Windows Server 2025; Windows on Arm runs an x64 Python under emulation. - Security. The preview and server commands answer only requests that carry a per-session key.
- Transcription. Parakeet-TDT 0.6B v3 is the default (fetched on first use, 465 MB): it keeps "um" and "uh", finds 81% of the fillers on a public human-labelled podcast set at 83% precision against its labels (0.1.0's Whisper small.en: 11% at 78%), and hears speech under music better (7.3% word errors at equal loudness, was 19.7%). Local evaluations on small sets; details in the entry below.
- Faster tests.
run_all.py --changedruns only the tests a change can affect. - Launch videos. The default launch/promo film has four to six scenes, a big still hook, a still camera and
at most one motivated fly-through (from the hook into the product, one continuous dolly), match cuts and a
dissolve into the end card; motion comes from the content (typing, results landing). A produced catalog track is
cut so its swell lands on the end card (
showtime audio cuts --for launch), terminals fit every size (data-st="fit"), and text cut off by its container, or cropped during a transition, is now a check ERROR;render --sizechecks the layout at that size first. - Captions, boards and edits. Caption shadows follow the outline colour (clean on light themes); standalone studio boards show a clear "copy for your agent, then paste it" step; footage edits are mastered to -14 LUFS unless you ask to keep the source level.
Before publishing 0.2.0 (release checklist)#
- [x] Windows end-to-end rerun:
windows-11-armandwindows-2025pass end to end with the fast suite, as domacos-14andubuntu-24.04-arm. - [x] README and CHANGELOG launch-video entries written.
- [x] MCP packages published (2026-09-29): npm
@faviovazquez/showtime-mcp0.2.0, the.mcpbas a release asset ofv0.2.0, the MCP Registry (io.github.FavioVazquez/showtime) and Smithery (favio-vazquezp/showtime). The README's "MCP only" section anddocs/agents.mdlink them. - [ ] Directory listing text for Cursor, Codex and Cline.
- [ ] Carried over from 0.1.0: mirror the GeneralUser GS SoundFont (its author asks projects to host their own copy;
sha256
9575028c…688cfe) and the MuseScore_General SoundFont on a release and pointsetup/manifest.jsonat them; pin size and sha256 for the extended audio-library tier (about 150 entries record theirs on first download).
Changed: verbatim transcription by default, fillers found, speech under music heard#
- Parakeet-TDT 0.6B v3 is the default transcription model (sherpa-onnx, int8, CC-BY-4.0): it writes
"um"/"uh" as words and covers 25 European languages including English and Spanish. It is fetched on
first use (465 MB, announced with its size). Whisper stays for other languages (
--language xxpicks it) and for anyone who asks (--model turbo|small). Why: Whisper small.en, the old default, found 11% of the fillers on PodcastFillers test excerpts (Whisper turbo 40%); the new pipeline finds 81% at 83% precision against the dataset's labels (event match within 200 ms; local evaluation). - A filler scan after ASR decodes voiced pauses no word covers, and words far longer than their
spelling, again on their own, and adds the hesitations it hears (
"filler": true). Backchannels ("mm-hmm", "uh-huh") are words and never cut. Spanish "este" / "o sea" are cut only on request (edit cut --filler-set es-discourse).edit cut --strict-fillerscuts only fillers a second decode confirmed. - Cleaner filler cuts: the pause left where a filler was is capped at 0.2 s, each cut edge moves to the quietest 10 ms within 40 ms, and cuts get a 20 ms equal-power crossfade (was a 30 ms fade out and in). On the benchmark interview the default commands now find and cut all five known "uh" (the benchmark's own scorer credits 4 of 5; the same commands in 0.1.0 removed none).
- Speech under music: transcribing a video showtime rendered reads the dry narration stem the render
kept (the mixer now writes
<mix>.voice.wav; edit renders record the speech before the music bed). Other footage: when the music is within ~8 dB of the voice, the voice is separated first with UVR's MDX-Net model (ONNX, no PyTorch, 67 MB on first use), unless the bed is ducked under the speech. On a speech-over-music test set this took word errors from 6.6% (small.en) to 4.4%, and at equal loudness from 19.7% to 7.3%. - CrisperWhisper 2.0 is available as an opt-in "max accuracy" model (
--model crisper): its weights are for non-commercial use, so it asks for--accept-licenseonce; it needs PyTorch, which no longer ships for Intel Macs, so it says so there instead of trying.
Added: faster test runs (--changed, long files in parts, big machines)#
run_all.py --changed [REF]runs only the test files a change can affect,--explainsays why each was picked. It combines what each test really used in earlier runs (recorded while the suite runs: every repository file its Python and Node processes import or open), a static scan (imports, named paths,showtime <cmd>routing, template names) and a short override table for docs and manifests; a file it cannot place runs everything. On the 64-core build machine an edit to the transcriber picks the footage, packaging and portability tests plus the hygiene check; one such run took 27 s.- Long files run in parts on machines with many cores (from 9 processes at once): tests that share state stay together, tests the plan does not know run in the first part, and each part is checked to have run its tests exactly once. Laptops and CI runners keep running whole files.
-j autouses big machines: up to half the cores (was at most 8), fewer when free memory is short; there each test's model threads are sized to its share of the machine. The fast suite on 64 cores went from 219 s to 131 s at-j 32(159 s while other jobs loaded the machine); the HTML export file, which runs alone, is 78 s of that.
Added: one repository for every coding agent, and showtime install --agent#
- Manifests next to
.claude-plugin/(which is unchanged): an Agent Plugins 1.0plugin.json+mcp.jsonat the root (Codex, Copilot, Kiro; the MCP server starts from./skills/showtime/mcp/server.mjsin the plugin folder),gemini-extension.json(Gemini CLI,${extensionPath}),mcp_config.json(Antigravity, which installs the root plugin from a clone) and the Copilot crew incom.github.copilot/agents/, because Copilot takes agents only from there once a rootplugin.jsonexists. Cursor and Devin read.claude-plugin/and need nothing new.scripts/build_agents.pyregenerates the crew copies fromagents/*.md. showtime install --agent <name>for Codex, Copilot, Cursor, Devin, Gemini CLI, Antigravity, OpenCode, Cline, Kilo, Kiro, Zed, Goose, Amp, Factory, Qwen and Claude Code: links the skill where the agent looks (Kiro, Cline, Qwen and Antigravity have their own folders), writes the ten crew agents in the agent's format (Codex TOML, OpenCodemode: subagent, Gemini tool names andtimeout_mins: 60, Kiro JSON; Claude-only fields and model aliases dropped) and adds the MCP server as<SHOWTIME_HOME>/bin/showtime mcp. It merges config files (one.before-showtimebackup) and leaves a file it cannot parse alone with the entry printed;--print,--project,--uninstall,--list. The skill carries a copy of the crew (setup/agents/) so a skill installed on its own has it too.- Real agent tests ("make a 5-second test video that says hello", qa with no FAIL): Codex CLI
0.158.0 (plugin), Cursor CLI (plugin folder), Devin CLI 3000.11.3 (local plugin), OpenCode 1.18.33
(
showtime install). Per-agent install steps:docs/agents.md.
Added: a portable core (the stable command, background runs, MCP task ids, a sandbox-aware doctor)#
~/.showtime/bin/showtime(.cmd/.ps1on Windows) is written by setup and repaired bydoctor. It runs the skill recorded in<home>/skill-path, finds a skill that moved, follows the newest version and keeps a copy of a skill that runs from npm's cache, so it keeps working when a plugin update changes the skill folder. Setup prints how to put it onPATHand never edits a shell file.- SKILL.md and the crew agents no longer depend on Claude Code's variables. Paths are relative to the skill
folder, SKILL.md has a
compatibility:line, no command is built from${CLAUDE_SKILL_DIR}or${CLAUDE_PLUGIN_ROOT}(other hosts leave them empty, so${CLAUDE_SKILL_DIR}/bin/showtimebecame/bin/showtime), and the crew agents callshowtimefromPATH.check_release.pyenforces it. showtime <cmd> --backgroundandshowtime status <run> [--wait S] [--cancel] [--runs]for hosts that kill a command after a few minutes; a "still running" line every 45 s when stderr is not a terminal.- The MCP server answers
initializein milliseconds and runs long tools as background runs: after about 20 s a call returns a task id andstatus {task}reports progress and the result (Claude Code keeps waiting and gets the result in one call).showtime mcpstarts it; a${...}placeholder a host did not fill counts as unset, and the projects folder is never inside the plugin. doctorknows agent sandboxes: whether~/.showtimeand the work folder are writable and whether the network is reachable, and it names the setting to change for Codex, Antigravity, Cursor, Copilot's cloud agent, Gemini CLI or Devin.SHOWTIME_HOMEis the escape hatch (relative values, a project's.showtimefound without any variable, uv and npm caches moved in when their folders are read-only).- Windows. The home lookup searches every home folder Windows can name (
USERPROFILE,HOME,HOMEDRIVE+HOMEPATH, the account's real profile);showtime version --jsonsays how the home was chosen. - Tests:
tests/test_portable.py,tests/test_mcp.py.
Changed: a smaller first download, and everything else on first use#
- The default install is about 770 MB on Linux x64 (about 3 GB before), 560 to 610 MB on macOS and Windows,
with a Chrome, Edge or Chromium already installed (about 100 MB more without one).
showtime setup --planlists every component per platform (--platform) with its size, source, sha256 and when it is fetched;--urlslists the pinned files for pre-seeding;--fullinstalls everything now (6.6 to 7.2 GB, for machines that will be offline;--seed DIRreuses files);--fetch NAMESfetches chosen first-use components;--pruneremoves what older versions left behind.doctorlists the first-use components. - Kokoro is one fp16 timestamped model (163 MB) instead of two fp32 files (651 MB), loaded with an in-memory guard for half-precision 0/0 phase bins.
- Whisper (small.en and its engine, about 500 MB) is fetched on the first transcription, Manim and
ManimGL before the first scene, icons one at a time (a few KB each, pinned versions), the audio library
in parts (a 41 MB starter part, category parts when a search finds fewer than three results), and the
browser is an installed Chrome/Edge/Chromium first, else the pinned Chrome Headless Shell (about 100 MB,
sha256, resumable); the full Chromium is fetched only for
--headedruns or when the shell cannot start. - Every first-use fetch prints "fetching X (N MB) for Y" first, is pinned by size and sha256, resumes, and fails with
exit code 3 and the exact command when the machine is offline. A shared Hugging Face cache is reused
(sha256-verified, hard-linked;
SHOWTIME_SHARED_HF_CACHE=0opts out). imageio's ffmpeg is a last-resort fallback only. The Windows ffmpeg is BtbN's GPL shared build (86 MB) before Gyan's essentials (115 MB). - Upgrading from 0.1 needs no action. A home with the old fp32 Kokoro keeps working (
doctorwarns and offersshowtime setup, thensetup --prune); nothing that worked stops working. - Docs:
references/onboarding.mdhas the first-use table; the README anddocs/agents.mdstate the new sizes.
Added: a produced-music catalog, credits that write themselves, more sound#
lib/st/audio/music_catalog.json: 249 produced tracks (Scott Buckley 128, Kevin MacLeod 82, Wikimedia Commons 20, Internet Archive 19), each pinned by URL, bytes and sha256, tagged by shelf, mood, energy, tempo, use and vocals, with a measured ending, quiet intro and highlight offset. Nothing is bundled or re-hosted: a mix (oraudio music fetch <id>, orsetup --full) downloads a track from its creator's site the first time, announces its size and caches it in~/.showtime/music.- A quality gate: every track was downloaded, decoded and measured (format, bandwidth, stereo, clipping, hiss,
crackle, loudness range, silences, length, rhythm, ending); 16 failed and were removed. Jamendo artists were
removed too (its own terms add conditions to commercial use); Jamendo is only reachable with
--source jamendoand a warning. - Commands:
audio music search|pick|fetch|info|veto|stats|presets|check|openverse,audio packs list|fetch,audio credits.pickranks by energy, length fit and a featured track; a catalog track skips its near-silent lead-in, or starts at its loudest stretch with"offset": "highlight".audio music openversesearches Freesound and Wikimedia Commons live (CC BY and CC0). - Credits. Every render that uses a catalog track writes the creator's credit into
credits.txt, a description block inshare.txtand the end-card line, and prints a note (Scott Buckley's Content ID only claims uncredited uses). A CC BY sound without credit text fails the mix, andqawarns when Content ID-protected music lacks its credit inshare.txt. - Sound packs: 35 extra packs (34 CC0, 1 CC BY: applause, cheering, rain and thunder, camera shutter, pencil and marker writing, clicks, and more) installed the first time a mix or search needs them.
- Docs:
references/music.md(a produced track, a composed bed or none),references/audio.md, the workflows. Tests:tests/test_music.py.
Added: end-to-end platform tests, Windows on Arm#
scripts/e2e.pyruns everything through the user's entry point on any OS: setup, doctor, a voiced render with qa, HTML export, transcription, captions (SRT/VTT and burned in), an MCP handshake and the fast suite, with per-step logs and a summary; a step whose output has a Python traceback fails even with exit code 0.scripts/e2e-windows.ps1wraps it for a standard user on Windows 10/11 (entry points, execution policy, a BOM script, a path with a space, a results zip)..github/workflows/e2e.yml(manual) runs it onmacos-14,ubuntu-24.04-arm,windows-11-armandwindows-2025. What ran where is listed under Requirements in the README.- Windows on Arm:
arch()reports arm64 even from an emulated x64 Python; setup builds the venvs with x64 CPython 3.12 (ctranslate2, opencv-python, av and numba have no Windows arm64 wheels) and recreates a venv of the wrong architecture; downloads without a win-arm64 build fall back to win-x64. The native arm64 ffmpeg is tried first, then the x64 builds; a failing ffmpeg now says why (exit code with its Windows status name, stderr tail or a time-out), anddoctorsays why it passed one over. phonemizer's espeak-ng exit hook no longer prints aPermissionErrorthere (the folder is removed on the next run).
Added: MCP packages and housekeeping#
server.json(MCP Registry) andpackages/npm(@faviovazquez/showtime-mcp: the skill folder plus two bins,showtime-mcpfor the stdio server andshowtimefor setup),scripts/build_packages.py(npm package and a reproducible.mcpbbundle),llms-install.md(step-by-step install for agents such as Cline). Nothing is published yet.- The preview and server commands need a per-session key. They answer only requests that carry the server's
random 256-bit key (the printed link has
k=<key>, traded for an HttpOnly SameSite=Strict cookie); anything else gets a plain 403 that says where the link is. Before, any web page open in the browser could read project files overhttp://127.0.0.1(the server sentAccess-Control-Allow-Origin: *).site capturenames showtime's own server instead of calling its 403 a bot check. - CI runs the Intel Mac only nightly and by hand; pushes and pull requests run Linux, Windows and Apple
Silicon.
check_releaserefuses links to the old Codex documentation address.
Changed: wording, README, docs and site for every agent#
- The tagline is "A local video studio for your coding agent. Describe a video. Your agent directs. Your machine
renders." Claude Code is named in its own install section and where it is factually the host. README, its art
(hero, pipeline, what runs where, crew hand-offs), plugin and extension descriptions,
brand.json, the site and the docs say so; the CLI's help,setupanddoctorsay "your coding agent" instead of Claude. - README: a "Works with" section (tested agents, the ones that should work, the install for each, the MCP-only
route), new sizes and first-use downloads, the music catalog, the platform list with what actually ran where, and
a "New in 0.2.0" list. Docs:
docs/agents.mdis part of the docs map and the site. doctorlabels a plugin install by the agent that owns the folder ("installed as a plugin for Codex") and the row isagent skill(it wasclaude skill). SKILL.md says the launcher is<skill folder>/bin/showtimeand that a plugin root has nobin/(an agent looked for it there in testing).
0.1.0: final pass (changes after the first complete build)#
Changed: pre-publish pass (brand media, requirements, platform status)#
- The 4-second sound logo ships as MP3 only (
assets/brand/motion/sound-logo.mp3); the 1.1 MB WAV master is no longer in the repository. The CLI keeps its own 79 KB WAV cut (skills/showtime/lib/st/sounds/sound-logo-short.wav), because the players it falls back to on Windows (System.Media.SoundPlayer) and Linux (aplay) play WAV only. - The brand stings are release assets.
sting.mp4andsting-square.mp4moved toexamples/_brand/and are published with the examples' media release (publish_media.pysends every video inexamples/_brand/there, whatever its size);BRAND.mdandbrand.jsonlink to the release URLs. - yt-dlp is no longer installed. Nothing in showtime used it; it and the five packages only it
needed (brotli, brotlicffi, mutagen, pycryptodomex, websockets) left
requirements.in, the lock, setup's import check anddoctor. - Docs: the core audio library is about 249 MB and takes about 10 to 15 minutes to fetch; the rembg entry
in
setup/manifest.jsonnames its default model (isnet-general-use, about 170 MB); the Windows Node.js command iswinget install OpenJS.NodeJS.LTS --source winget(without--source, winget can also query the Microsoft Store source and stop to ask the user to accept its terms). - CI.
checks.ymlruns the quick release checks (check_release, the skill structure test) on every push and pull request.ci.ymlsets up the core tier and runs the fast suite with-j autoon Ubuntu, Windows and Apple Silicon (macos-14), plus the Intel Mac once the repository is public; changes that only touch Markdown,docs/,assets/readme/,site/or.out-of-scope/skip it. The showtime runtime (ffmpeg, models, SoundFont, Node packages, Python venv) is cached per OS, keyed on the setup manifest, the requirements and Node locks,setup.pyand the Python version. One run per branch at a time (a newer push cancels the older run); the nightly tier runs only on a public repository or by hand; a manual run can pick runner images and test files. - The HTML export test holds up on small CI runners. On a 2-core runner with a software GPU, its real-time playback checks failed for reasons of speed (the driver's 1.5 s window took 10 s on Windows while the video played on to its end). It now records how long that window took and the player's transport events; on a machine too slow to play in real time those checks skip with the reason, after everything that does not depend on speed has run. On 2 cores or fewer, "the picture never runs ahead of the sound" allows the player's own extrapolation of a coarse audio clock (at most 0.25 s).
- README diagrams show their wide art on github.com. Each
<picture>had a(max-width: 700px)source for a tall phone layout, but GitHub's sanitizer keeps onlyprefers-color-schemein<source media>, so the narrow art won on desktops too. The README anddocs/README.mdnow pick only between the light and dark wide art, which scales down on phones, and the 16-narrow-SVGs are gone fromassets/readme/(the site uses the wide art as well). - Platform status. Windows x64 has been tested end to end (Windows Server 2025, as a standard user) and Apple Silicon in CI (GitHub's macOS 14 arm64 runners: core setup and the fast suite, which includes real renders); the README's Requirements, status table and badges say so ("tested: Intel Mac · Apple Silicon · Linux x64 · Windows x64"), and that Windows 10/11 desktop editions and Linux arm64 have not been run yet. A run on a physical Apple Silicon Mac is still welcome.
- Site link previews use absolute URLs.
og:imagewas a relative path, which link previews (Slack, X, iMessage) cannot load. Every page now has an absoluteog:image,twitter:imageandog:urlbuilt fromsite_urlinsite/config.json(or the repository's GitHub Pages address). - CI runs the fast suite in three shards per OS.
run_all.py --shard I/Nruns one of N parts of the test files, split by fixed per-file weights (SHARD_WEIGHTS, fast-suite seconds from a CI run; longest first into the lightest part), so every machine computes the same split and each file runs exactly once; inside a shard two files run at a time (-j 2, also on the 3-core Apple Silicon runner, whereautowould pick 1).ci.ymlruns shards 1/3, 2/3 and 3/3 as separate jobs on Ubuntu, Windows, Apple Silicon and, on the public repository, the Intel Mac (macos-15-intel: themacos-13image is retired); shard 1 also runs the render smoke and the path-with-spaces shims. On one 2-core runner the suite took 23 to 31 minutes per OS. - The HTML export test runs alone (
SERIALinrun_all.py). Its player checks watch playback in real time; on the public 4-core runners, sharing the machine with another file's Chrome left the player drawing 1 to 4 frames in the 1.5 s window on Windows and the Intel Mac. A file inSERIALcounts double when the shards are balanced. Running alone was not enough on the Windows image (1 frame drawn while the clock ran on), so on a CI runner (CIset) those real-time checks skip with the reason when the browser draws fewer than 5 frames in the window or the sound is still loading, as they already did on 2 cores; everything before them (requests, errors, duration, soundtrack) still runs, and on a desktop they always run.
Changed: the examples have their own repository#
- The 22 examples, the launch film,
examples/MEDIA.jsonandscripts/publish_media.pymoved to showtime-examples, and the media release (every example file over 10 MB and every.mov) is that repository's. This repository is the plugin people install, well inside the plugin directory's size and file limits. Links to an example point at showtime-examples; the README art (assets/readme/) stays here. site/build.pyreads the examples from--examples DIR, else from a clone next to this one (examples_dirinsite/config.json); the Pages workflow checks showtime-examples out and downloads its release.scripts/check_release.pyhas adirectorycheck: the plugin directory's limits (files, sizes, symlinks, Windows-safe names,.gitattributes, README, LICENSE, plugin.json fields, a default for every userConfig option). It runs by default where there is noexamples/folder.- The README says what showtime runs and downloads, and what goes over the network.
Fixed: final release pass (parallel renders, talking heads, seeks, exports, boards)#
- Parallel renders no longer collide in the shared audio cache. Temp files are unique per process
(
common.part_path) and every cache fill (synth hits, typewriter clicks, composed beds) holds a per-entry lock (common.cache_lock), so three renders of one project mix at once and get identical audio. Font, voice and decode caches use the same temp names. - A failed mix is loud. When
showtime audio mixfails for a mix with synth/compose tracks or ducking, render stops with the error instead of shipping the simple built-in mixer's version (which drops those sounds); a files-only fallback warns withAUDIO FALLBACK. The mix is retried once and has a 20-minute timeout; timed-out child processes are killed (SIGKILL after 5 s). - The simple mixer cannot hang. One ffmpeg graph with every file as an input (the same file twice,
tracks 50 s apart) deadlocked intermittently in ffmpeg 9 (4 of 45 parallel runs); each track is now
rendered to an aligned stem and the stems are summed (0 of 45). It also honours a track's
dur. - qa: a speaker holding still is not a frozen picture. A long hold in camera footage (noise in every
part of the frame plus local motion) with sound is
held_shot(INFO); without sound it is afrozenWARN. Motion-graphics holds (also under grain or typing) and any hold in a project that plays no<video>are judged as before. - Synth scores resume at their level on a realtime seek. Envelopes written after their start time (building a voice can outlast the 60 ms lead) faded the note in over half a second; compressors start released.
- review-pack finds the cuts inside a chapter (project clip starts and cuts seen in the picture).
- export html fits footage under the limit. A single file over
--max-mbbecause of video clips gets those clips re-encoded (2-pass, for the export only) at the bitrate that fits;--fit offstops with the breakdown instead. - Stills of a
<video>land on the right frame when the clip loads late (added by a handler, a new src): the seek waits for its data and computes the loop point then; aseekedfrom an earlier seek no longer ends the wait;snapwarns when a video missed its frame. - Studio board sticky bars stay opaque in WebKit (opaque fallback before
color-mix(), 96 % over the blur, own layer above videos). - Composed beds carry their license.
audio composewrites<file>.license.json(generated, no attribution); older beds are recognised by their beats file. - Benchmark judges look at the pictures. Judges get the exact frame paths (they only have Read and cannot list folders), the harness records which images each judge opened, and a judgment that did not open every video's frames (or says it could not) fails. HTML deliverables are judged from stills of their screen recording, not from screenshots of the start screen.
- Dependencies: BtbN ffmpeg builds re-pinned to
autobuild-2026-09-27-13-04(n9.0.2; the old tag is rotated out), simple-icons 16.33.0. Node advice: 24 or 22 LTS (the fast suite passes on Node 24.21.0).
Added: a companion site, a visual crew, and diagrams that explain#
site/: a static companion site built bypython3 site/build.py(landing page with the hero film, a gallery that plays every example with filters by use case, one page per example with its HTML video embedded, the crew, and every guide inreferences/as a page with a sidebar and search). No framework, no trackers, fonts served from the site. The gallery previews each example on hover with a silent 4-second clip cut at build time (site/content/previews.json)..github/workflows/pages.ymlbuilds it and downloads the example videos from the media release at deploy time, since they are not in git.- README: the crew as an illustrated cast of ten (all optional, said so on the card) and a diagram of who hands what to whom; diagrams for the pipeline, what runs where, studio mode, the anatomy of an HTML video and the output formats, each in light and dark and in a narrow layout for phones; the launch film as the hero (its poster links to the release asset until the MP4 is attached in GitHub's editor), and a hidden benchmark section.
- The launch film (
examples/_launch/): 40 s in 16:9, 1:1 and 9:16 plus an HTML video, published as release assets with the example media; a silent 6-second teaser loops in the site's hero, and "Watch the film" plays it with sound. Music: "With These Hands" by Scott Buckley, CC BY 4.0; per the composer's terms the audio ships only inside the film, never as a separate file.assets/readme/social/launch-1280x640.jpgis the social preview and the site'sog:image. examples/README.md: a wall of all 22 previews, then one card per example with its loop, the sentence that made it, what it shows and its links.docs/README.md: the programme as a map, and "choose your path" as cards with a preview of an example made with each workflow.
Changed: the README is a show, with a gallery and a docs map#
The README now opens with an animated curtain (SVG, CSS only, light and dark versions, still for
reduced motion), a three-step quick start, an animated Claude Code session that ends on showtime's real
completion card, and a gallery of all 22 examples grouped by use case with looping previews (animated
WebP made with showtime deliver exports --targets webp-small, in assets/readme/). Every example's
request is there as a copy-ready prompt. Deep dives (every command, the crew, studio mode, the audio
toolkit, Manim, the HTML player's keys), install details and troubleshooting fold away. New
docs/README.md maps every guide in skills/showtime/references/ by what you are
making. examples/README.md has the same grouping, with lengths, formats, watch links (release assets
via {{RELEASE_URL}}) and HTML videos. Platform status is stated as tested (Intel Mac, Linux x86_64)
versus supported and in testing (Apple Silicon, Windows, Linux arm64).
Changed: calmer, more premium defaults where the blind benchmark found weak spots#
A blind benchmark against other video workflows (six tasks, a human judge) put showtime first on four; the viewer's comments on its own outputs drove these fixes. Each names what the session actually did.
- Music taste. A data report and a math explainer got beds the viewer called "like the Sims" and
"a DIY YouTube video". Root causes: the
datatemplate shippedcorporate-minimalat 110 bpm with abuild→dropsection map (snare roll, crash, tom fills, reverse cymbals, a glockenspiel motif, then a whoosh, a paper swipe, a thock and a ding on top); the docs routed explainers and "premium" to that style; andambient-pad, which the math session tried first before switching to the corporate bed, went silent every other bar. Now:- two new restrained styles,
underscore(strings, contrabass, a soft felt-piano pulse, no drums, no melody; the newaudio composedefault and the bed for explainers, data, reports and math) andminimal-pulse(warm pad, sub bass, a muted pluck ostinato, a soft kick; tech and data); - restrained styles (
underscore,minimal-pulse,ambient-pad,piano-emotional,corporate-minimal) never add section crashes, drum fills, snare rolls, risers or reverse cymbals;corporate-minimallost its glockenspiel (a sparse piano motif instead) and slowed to 104 bpm; - fix: a chord held for two bars (
ambient-pad,underscore,cinematic-build,dark-tension,deep-house) was cut at the end of its first bar, leaving the pad and bass silent for the second; the 45 s ambient bed's 10th-percentile level goes from -58.9 to -22.6 dBFS. Library beds composed by the old composer are re-rendered byshowtime audio lib generate(COMPOSER_REV), and the generated tier gains fourunderscore/minimal-pulsebeds (28 in all); - the
datatemplate: anunderscorebed (intro/verse/chorus/outro, no drop) and one soft chime on the closing number; thefilmtemplate's score lost its claps and whoosh; references/music.mdsection 1: a taste rule (restraint reads as premium; no music is an option), a content → style table and a "what not to pick unless asked" list (mallet and plucked leads, claps and shaker under data, build/drop maps under charts, game-UI effects); routing indata-story.md,launch-video.md,tutorial.md,footage-edit.md,manim.md,tones.md,sound-design.mdfollows it; SKILL.md has a red flag for it;- ducking under a voice defaults to 12 dB (was 10): the bed sits about 16 dB under speech.
- two new restrained styles,
- Crowded chart numbers. A data story's 18-bar decade chart put the negative bars' value labels on
top of the category labels ("−0.27" over "1900") and ran neighbours 4 px apart;
showtime checksaid nothing because text overlap only fired past 15 % glyph overlap and nothing measured "too close". Bar charts now leave room under the lowest negative bar, plan value labels per state from the settled values (shrink slightly, then hide the least important; highlighted, annotated, max, min, first and last always show; labels fade across morphs;valueLabels: "all"keeps every one,falsehides them), thin category labels that don't fit their slot, and slide an hbar value label past arefline.checkhas a newlabels_crowdedwarning for SVG labels that touch or sit closer than 0.15em (label_gap_eminruntime/thresholds.json; notes while a chart is still animating). Same chart, before/after: 18 → 14 labels, tightest gap 4 px → 94 px, 3 → 0 labels on the axis. - Captions for vertical shorts. The reel's viewer said the subtitles "were not the best". The
session used the short template's
bold-popkaraoke: ALL CAPS with a heavy outline, 2-3 word cards that broke mid-phrase and ended on weak words ("EMPTY LINES BEFORE" / "SORTING"), an active-word scale that made neighbours collide ("NATURALSORT"), nine emphasis terms, and two-line cards centred on 64 % that grew up into the content. Now: a newclean-popstyle (sentence-case heavy sans, thin edge and soft shadow, the spoken word turns the accent) is the component default and the short template's; cards are phrase-aware (never end a card or line on an article, preposition, conjunction or auxiliary, in en/es/fr/pt/de; no one-word cards;minShowrespected); the lower block hangs from 62 % and grows down inside the safe box; the pop is 1.04 and emphasised words no longer stay scaled; at most one emphasised word per card (check warns past five terms). Burned ASS captions (edit render,showtime captions) use the same no-weak-ending rule.bold-popstays for loud, hype pieces. - Punch-ins on jump cuts. The filler-cut session followed
footage-edit.md("zoom: 1.12on alternate ranges hides jump cuts") and the viewer disliked the zoom in and back out at every cut. The recipe now leaves jump cuts alone by default;editing.md,story.mdand the vertical recipe explain when a punch-in is right (one scale held per sentence or section, on request).edit checkandedit renderflag a scale that keeps bouncing at cuts (punch_bounce). - HTML report transitions. The
datatemplate shipped three unrelated handoffs (push left,blur-dissolve,morph-warp) that the report session kept; the viewer found them nonsensical and chopped. The template now uses one calm family (a dissolve out of the title, dips between charts);transitions.mdgains rules for data stories and reports and "never transition mid-sentence". - Fact discipline in launch videos. A reviewer could not verify four small specifics in the launch
video (a backup file name, a line count, a code excerpt, and a mid-animation frame that looked like an
unsorted result). The first three came from running the tool, but nothing on disk said so.
story.mdsection 6 now treats specifics as claims (a doc quote or an evidence file under<job>/work/evidence/, else obviously generic), warns off incidental specifics, and requires every held frame to be true on its own;launch-video.md,changelog-video.mdand the researcher follow it.
Added: small touches at the terminal (brand mark, completion card, opt-in sound)#
A person running showtime in their own terminal now sees the Curtain Call mark (a two-line curtain in
velvet and gold) above showtime --help, setup and doctor, and a three-line completion card after
render, export html, manim render, edit render and deliver exports: what was made (length, size),
where it is, the qa verdict when one is recorded for that exact file, and the one next command. With
SHOWTIME_SOUND=1 or the new plugin option sound (default off), a 1.8 s cut of the sound logo
(skills/showtime/lib/st/sounds/sound-logo-short.wav, 79 KB) plays when a command that ran over 20 s
finishes; it uses a player the system already has and never delays or fails the command. All three stay
off when the output is not a terminal (so Claude's tool calls, pipes and logs are unchanged), with --json,
NO_COLOR, TERM=dumb, CI or SHOWTIME_COLOR=never; the logic lives in lib/st/delight.py and its Node
twin scripts/lib/delight.mjs. Why: the plan's delight pass, without adding a step or changing any
machine-readable output.
Also: the most common deliver and qa errors (file not found, has no video stream, could not decode,
poster time outside the video, wrong cover/thumbnail format, an export that would overwrite its input,
not a showtime project) now carry a fix: line with the exact next step, and a bad option in a Node
command (render, export, ...) says fix: run ... --help for every option, with examples like the
Python commands. edit render shows its "next:" line inside the card at a terminal.
Changed: fixes from the batch-2 examples (friction log, examples 12-22)#
Each item was hit while making examples 12-22; the fix is in code with a test unless marked docs.
- Transitions. A transition window snaps to frames like clip edges: a scene start written as
6.6667or53.434(within 1 ms after a frame) no longer shows the incoming scene alone for one frame before the transition (examples 12, 14, 17; check and qa both missed it). Layers placed after the incoming scene (a map inset, labels) stay above every transition window instead of dropping under the scenes (example 19).wipe leftis the straight wipe (a direction used to be ignored for the default diagonal). Filmdipdraws the outgoing scene until the cut and the incoming one after it (a title card without a ground showed the last shot through the dip).ridged-burnembers are round points, not square blocks. Docs: text in displacement shaders, WebGL windows over--alphascenes, a shader transition inside a canvas film. - check. The text audit sees text under
pointer-events: none(lower thirds, overlays: it was reported "covered" and must_show FAILed), measures SVG text through its transforms and viewBox (a label inscale(2)was reported at half size), and keeps the full text of each line (must_show missed words after character 80). A low-contrast finding names the failing span ("1" in "1import json ...", a line number). Numbers alone (axis ticks, years) skip the reading-time rule. An overlay page (<body data-overlay>or showtime.json"overlay": true) reports gaps as notes, notdead_air. The seek-order error hints at handlers that keep state between seeks; the timers warning says when the call came from a library.check --page other.htmland--sizewrite towork/check-<page>[-<WxH>]/. - One page, several sizes. snap, check and studio frames follow a page's own (render already did)
ST.config({width, height})when showtime.json sets none (a square page was captured stretched to 1920x1080).render|check|snap --size 1080x1920|9:16renders one page at another size for a run;render --job J --size 9:16writes<job>/1080x1920.mp4, a variant.snap --pagegets its own folder,snap -o still.jpgwith one--atwrites that file, and--widthcan upscale. - Alpha renders. Themes' scene and stage fills are transparent under
--alpha(a steps overlay came out as a dark plate unless the page reset--scene-bg); a background a scene sets itself still paints. - Components. chart:
valueLabels: falsealso hides hbar race values; a race title that only changes after " · " swaps without fading (it pulsed at 0.25 s steps);locale. count-up follows<html lang>(13,7on a Spanish page) orlocale. A component a page moduledefine()s afterindex.jsmounted the page is mounted (it warned "unknown component"); docs: awaitctrl.readybefore touching a component's DOM. caption-karaokegroup: "phrase"for fast speech, and highlight-box blends the ink with the gliding box (the next word vanished for 3 frames on dark grounds). lower-third:in(entrance seconds),position: top, larger type in 9:16, accent above a themed plate. feature-gridcap; notificationstop(and page CSStopwins); logo-reveal and end-cardbloom: false. code-block line numbers and diff gutters clear 4.5:1 by default. ThemediaReadyfallback timer is cleared (every page with a<video>warned about a timer). Barecode, kbd, preuse the theme's mono face; the paper theme falls back to Space Grotesk for glyphs its display face lacks (the ʻokina). - Studio. Board shortcuts work after using the Compare wipe (a range input kept focus and swallowed
the keys). An artifact export (
studio export --target artifact) names a file left out of the page and where it is in the job folder instead of a dead placeholder, and says when the viewer does not keep reactions across reloads; a plain export prints the--target artifactline to use for an artifact. The feedback digest after the build asks for a ship decision, not "lock and build". - Capture.
demo recordlogs navigated URLs with query values, fragments and credentials redacted (a studio board link carried its key into events.json) and logs typing into a password field as dots. - Docs. Word-timed reveals on DOM pages from
voice cues,retimedoes not rescaleanimation-delay, demo actions glide before they press, still-hold rules on text scenes and dark frames, the dom template's four scenes in the launch workflow, a one-country map recipe, multi-part packs and the decisions note on a studio board, browser-frame fonts on pages without a theme. - qa and the job ledger. qa checks only a video's own caption sidecars inside a job; the job's
captions count only for its latest final with the same aspect, and
--captionsreplaces auto-found files (a 9:16 cut was judged by the 16:9 captions). Every too-fast cue and over-long line is listed in one run. qa keeps a verdict per file, so checking an export no longer makes the final look unchecked. For size-capped platforms (github, chat, web) the master's size is a note when a capped export exists, with the command that checks the export. A render, edit render,captions --burnor poster bake in a job becomes the latest final only when it is an.mp4namedfinal*(or a copy derived from the current final); alpha overlays, bumpers and second aspects are recorded as variants, with the command that promotes one (job note --auto).captions --burninto a job updates the latest final and says so.job discardkeeps caption files and lists what moved. - review-pack.
--lufs(and it reuses the last qa--lufs/--platformof the file); overlay clips are no longer scenes (film acts and showtime.json chapters come first); everyCUEkey is read, even several on one line. - Captions and footage. The per-line character limit applies to each line (45-character French
lines); the boxed style draws one continuous plate per caption (darker bars between karaoke runs);
captions --size-scale.footage denoisekeeps channels and exact length and reports what the strength did;footage grade --comparerefuses a video file name;footage probereportsyuva420pfor VP9 alpha.brand init --from <repo>adopts the repo's own brand.json and skips examples and fixtures;brand showchecks fonts live.showtime cleanfrees Manim build caches and drafts. - Docs. Dark themes and slow pushes in qa, comparing a stabilised clip, podcast speakers from a
published transcript,
transcribe --from/--tofor long episodes. - From the re-render of examples 01-07 on Linux.
demo record --platform mac|windows|linux(or the script'soptions.platform) sets the user agent,navigator.platformanduserAgentData, so an app that picks "⌘K" or "Ctrl K" from the OS records the same on any machine. autozoom: an explicit focus (demo.focus, a--hintsfocus) sets its shot; clicks and typing inside its span no longer shrink it to the editor's fit zoom; a take written asautozoom-2.mp4says so again at the end.export html --audio-file <wav|mp4>embeds exactly that sound (a shipped video's soundtrack when the voice WAVs are gone) instead of the score and the mix.checknotesposter_not_bakedwhen the showtime.json poster will not be baked into frame 0. Docs: Supertonic re-synthesis moves line timings, the app's own clock during slow takes,review-pack <file> --project. - HTML export speaks the page's language.
export html --lang(default: showtime.jsonlang, the page's<html lang>, the narration'slang:, else en) sets<html lang>and the player's own words (Play, Replay, chapters, Sound on, Starts at, the key help, messages) in English, Spanish, French, Portuguese and German, from one table in the player; other languages get English controls. A Spanish export showed "Play" and "9 chapters · Sound on". - review-pack text crops hold whole lines. Bold or tightly set words merged into one blob and were thrown away, so crops showed fragments ("l is a f"); word-shaped blobs now count, and each line is grown sideways over the rest of its ink (specks such as snow or dust do not extend it).
- Manim. The scene cache covers helper classes (a changed layout class shipped the old layout from
cache);
key_mapmatches declared keys with spaces; inside a line that says the word,at("half")is the word, not a line named "half", andmanim checkwarnscue_ambiguous; check's still-hold rule is qa's (2.5 s warn, 6 s error, subtle plays do not count, holds join across scenes) and takes--aspect;refine(start=, tag=);-o <job>/final.mp4writesposter.jpg; ManimGL renders report their length; pattern templates get a narration.md and the job's title. Docs: VGroup re-parenting,remove(VGroup), a wide diagram turned for 9:16. - Audio.
audio composewarns when the tempo bends over 3 % or a section gets a 2-beat bar and suggests a bpm (or snapped markers) that fits, and notes a marker merged into the ending hit. Masking warnings skip designed layers (same file stem, id prefix or synth type; a riser whose hit is its end) and group per family; tracks takelayer/texture. Atypewritermix track clicks on the page typewriter's own timeline. The mix report records the SoundFont used; a copied library file keeps its license (matched by size and sha256, or a sidecar) and a music file with none warns; ambience search includes sfx loops;audio fitsays which bar it ends on and--ending songsplices in the track's own ending. - Voice.
voice ipaapplies the lexicon to the phrase line and finds the project's lexicon; grouped numbers and decimal commas read correctly in es/fr/pt/it/de ("60 000", "13,7"); the ʻokina is silent;--fitsays every line re-synthesizes. Docs: a bare year after "the", af_nicole's pace. - Deliver. A master already under a size cap is encoded for quality within the cap's bitrate
instead of being inflated toward it (2.5 MB became 11.9 MB); a bare
--max-mb Ncaps only original/github/chat/web and loops (every target when none is listed) andtarget:Ncaps one; exports follow the job's loudness target (--lufs); x and linkedin keep a 1:1 or 4:5 master's aspect. - Render.
--alpha animation: QuickTime Animation.mov(lossless RGBA, several times smaller than ProRes for flat graphics; ProRes stingers were 27-32 MB). A final above 25 Mb/s at 1080p gets a hint (animated grain). Docs: crf 18 for masters kept in a repository. - Assets.
assets cutout --max-size(default 2048); Commons fetches use a standard thumbnail width (3840 default,--max-size,--quality orig) recorded in the sidecar, with a Retry-After-aware backoff on HTTP 429;assets media fetch <url> --license ... --source-page ...for any file; an author-check note on Commons fetches;credits --allsays which lines to add by hand. - Data, retime, export, MCP.
data import --whereand--names COL=Label, and it writes the data when the scene has no chart yet;retimeleaves overlays alone (not asection.scene,data-overlayor open-ended), writes a scene edge that falls within 1 ms after a frame at that frame, and says it does not rescaleanimation-delay;export html --job J -o name.html; MCPrendertakespageandalpha,export_htmlthe embed options,deliver_exportscaps andlufswith an accurate file list.
Added: large example media ship as release assets#
Example renders made the repository heavy (about 490 MB of the 680 MB under examples/). Any file over
10 MB and every .mov (ProRes masters) under examples/ is now a GitHub release asset: listed in
examples/MEDIA.json (path, bytes, sha256, a unique flat asset name, the reason) and excluded from git by
a managed block at the end of .gitignore. scripts/publish_media.py verifies the manifest against the
files (also run by check_release.py: an unlisted or un-ignored large file is an error, a re-render that
changed a size is a warning), --refresh rewrites the manifest and the block, --links prints markdown
links for a README (https://github.com/<repo>/releases/download/<tag>/<asset>), and --upload stages
the files under their asset names and runs gh release upload <tag> ... --clobber (--dry-run prints
it). Nothing under examples/ was moved or deleted. At the time of writing: 30 files (497.5 MB) are
release assets, 960 files (192.8 MB) stay in git. tests/run_all.py no longer reports the repository's
own top-level folders (benchmarks/, examples/ ...) as files a test wrote into the repository.
Fixed: ManimGL on a Linux server without a display#
Importing ManimGL opens a display (pyglet), so on a server with no DISPLAY setup reported "does not
import" and renders failed. showtime now runs ManimGL under xvfb-run by itself when there is no
display (setup's check, showtime manim render); when Xvfb is missing, setup, doctor and the render say
so with the install line per distribution. Setup summaries also drop Python's ^^^^ traceback markers,
which were all a failed import showed. Found on the first Linux run.
Fixed: two tests that assumed the author's machine#
test_export packed an emoji that only a machine that had fetched it before has: it now installs it
the way the export's own warning tells a user to (skipped when offline). Its player-vs-snap frame check
asks the stage for a video frame before the screenshot, because headless Chrome on Linux can keep
compositing a paused video's previous frame after a seek.
Fixed: alpha renders keep their colours in an editor#
render --alpha prores and --alpha webm converted the captured RGB frames with FFmpeg's default BT.601
matrix but tagged the ProRes file BT.709, so Premiere or Resolve, decoding as tagged, shifted every colour
(a brand red #B3121F came back as #C1221D, gold #E9B949 as #EEB744). Every render output (H.264,
ProRes 4444, VP9) is now converted with the BT.709 matrix and carries full BT.709 tags, the WebM included;
the alpha_prores/alpha_webm encode presets and Manim's ProRes path do the same. Found by the critic on
example 20's lower thirds; test_render.py checks the decoded colour of a ProRes render.
Fixed: words and math on one line share a baseline, size and weight (Manim), and critics look at type full size#
Example 22's opening title "Why πr²?" set the math 34 % of the cap height below the words, a size
smaller and much lighter; a boxes-based arrange(aligned_edge=DOWN) did it (the "y" descender), and
the critic and every check missed it because contact sheets shrink it away.
st_manim:mixed_line("Why", tex(r"\pi r^2"), "?")puts words and math on one baseline measured on the glyphs, scales the math to the text's x-height and sets it in\boldsymbol(plus a thin outline beside 800-900 weights);align_baseline(a, b),baseline(),x_height();counter()keeps its baseline while it counts;eq(bold=True).showtime manim checkmeasures words-plus-math and words-plus-number lines on screen:baseline_mismatch(> 4 % of the cap height),xheight_mismatch(> 12 %),mixed_type(placed by hand).showtime review-packcuts the largest text lines out of full-size frames (frames/text-*.png); CRITIC.md,references/review.mdand the critic brief add a type detail pass.- Example 22 and the
exampleManim template rebuilt with the helpers.
Fixed: HTML exports keep their styles and fonts under a host's strict CSP#
Published as a claude.ai artifact, a DOM export (the data template, examples/12-energy-report) showed
its charts but every piece of text tiny and in a fallback serif. The viewer's Content-Security-Policy
allows inline styles but not blob: stylesheets, and the stage document linked every stylesheet (the
theme, components.css) and every font as a blob: URL: the theme's tokens, container-type and
sizes were gone, so text fell back to 16 px serif in a 1920x1080 frame. Canvas films lost their fonts
the same way (sizes held, the typeface did not). Now the stage writes every packed stylesheet as
inline <style> text (imports inlined, also for links created by script) and turns every packed
@font-face (and new FontFace(url)) into a FontFace built from the font's bytes, which no
font-src rule applies to. Fonts or stylesheets that still fail are reported in the console and in
showtimePlayer.warnings, and on screen with #st-debug. test_export opens a data-template export
in a sandboxed frame on another origin under an inline-styles-only, no-fonts CSP at phone size. The
example HTML files (_html/, 11-tutorial-series-tidepool/episode-0*/final.html,
12-energy-report/us-power-mix.html) were re-exported.
Added: README loops, PDF import, partial transcription; MCP server moved into the manifest#
- Image loops for READMEs and docs.
showtime deliver exports <video> --targets webp,gifmakes a silent, forever-looping animated WebP and a GIF fallback (960 px, 15 fps, under 5 MB);webp-small/gif-smallmake showcase loops (480 px, 12 fps, under 1.5 MB).--from/--topick the window,--width/--fpsoverride the defaults, and a loop over its cap (--max-mbor the target's) is encoded again smaller (WebP quality, then frame rate, then width) until it fits. GIFs are two-pass (palettegenstats_mode=diff, Bayer dithering,diff_mode=rectangle) at rates GIF delays can keep exactly. Files:exports/<stem>.loop.webp,.loop.gif,.loop-small.*. Why: the README hero and showcase loops were being made by hand with bare ffmpeg (references/platforms.md). - PDF import.
showtime doc extract <file.pdf> [-o dir]writestext.md(one section per page),images/(embedded images; JPEGs copied byte for byte, repeats kept once),figures.md(captions tied to their image or marked vector, plus the credit lines the PDF prints, with a note that a credit line is not a license),pages/renders anddoc.json. Default folder:<job>/sources/<name>/. New core dependencypypdfium2==5.13.0(PDFium; Apache-2.0/BSD-3-Clause; wheels for macOS arm64/x86_64 13+, Windows x64/arm64, Linux x86_64/aarch64 glibc and musl). Runshowtime setuponce to add it to an existing install;showtime doctorchecks it. - Transcribe part of a long file.
showtime transcribe <media> --from 12:30 --to 18:00(seconds or mm:ss) runs ASR on that stretch only. Word times stay on the file's clock, the transcript records"range"andduration= the range's end (so cut plans drop everything outside it), it is written as<name>.<from>-<to>.json, and the cache is keyed by the range.packmarks it "(part)". - MCP server declared in
.claude-plugin/plugin.json(mcpServers, same command,${CLAUDE_PLUGIN_ROOT}and${user_config.*}substitution). The repo-root.mcp.jsonis gone: Claude Code also read it as a project server for anyone who opened the repository itself, where${CLAUDE_PLUGIN_ROOT}does not resolve, so contributors were offered a server that timed out. Plugin users see no change. - Studio board chrome (top bar, favicon, the "preparing the first round" state) uses the Curtain Call mark, wordmark and colours (Stage bar with gold accents in dark mode, House cream in light). The content area keeps its neutral palette, so concepts in a user's brand are judged on neutral ground.
Changed: canvas-film QA sees callouts, dims and captions (tutorial review)#
A frame-by-frame review of the tutorial series (example 11) found callout cards covering note titles, a card
off the frame, the step band letting the zoomed app show through, and backdrop slivers in zoomed shots, and
showtime check had reported none of it: its canvas findings were ~450 contrast warnings per episode,
mostly text under deliberate dims.
Film.frameInfo()now recordscovers(callout cards with their anchor, captions, the step band, spotlight dims,F.box({dims: true})scrims) and each text's draw order.showtime check: a callout card over text drawn before it istext_overlap("hidden under the callout"); a card pointing off the frame or cut by its edge is the newcallout_off_target; text under a dim is not judged for contrast (onedimmed_textnote); text on a caption plate counts as spoken captions (noshort_text); text hidden under the band or a caption is skipped. Checked in the dense pass too.F.stepBandis opaque by default and picks white or the ground for its badge number, whichever reads better on the accent (white on a light accent failed contrast).- Player start screen: the poster's brightness under the title block is sampled, so a light poster under a dark scrim gets the deep scrim, now sized to the title block (the kicker was unreadable).
showtime snap: long--everysheets from 1080p masters shrink each frame before laying out the sheet (a 93-frame sheet crashed the lab page); any CLI command now exits after reporting an error instead of hanging on a crashed browser.- Example 11 re-rendered: callouts moved into empty or dimmed space, spotlit tour stops, a camera clamp that keeps zoomed shots on the app, the recap cleared before the outro, a readable intro subtitle, longer keycap holds, poster frames that show the result.
Added: Manim module for math and diagram explainers#
Exact math animation (equations, proofs, graphs, grid transforms) was the one kind of explainer the HTML and canvas projects could not do well, so Manim is now a first-class project type.
showtime manim new|render|check|cues(lib/st/cli_manim.py, orchestration inlib/st/manim_run/). Renders use Manim Community (themanimextra, pinned>=0.21,<0.22) with PyAV encoding; drafts are 480p15 with a contact sheet of each scene's last frame, finals 1080p30 withposter.jpg. The frame is set explicitly for 9:16, 1:1 and 4:5 (the short side is always 8 units; Manim's default squeezes vertical renders to about 40 %). Each scene is cached by a hash of its own code, the cues it uses, the theme and the size.--alphagives VP9-alpha WebM or ProRes 4444.st_manim, an original helper library for scene files: brand theme frombrand.json(colours, a semantic hue set that stays text-safe and distinct from the emphasis colour, fonts registered from files),eq()that splits equations into parts and colours each concept the same everywhere,morph()(terms crossing the equals sign travel on an arc), narration beats (beat,at,fit, frame-snappedhold,mark("poster")), layout regions for every aspect, glow, highlight box, background-coloured backstroke, a LaTeX-free count-up, and four patterns (equation walkthrough, plane transform, graph build with a faint preview, approximation refinement).- Narration sync: cues come from
voice/timeline.json(or are estimated fromnarration.mduntil the voice exists); every play and wait is snapped to whole frames so cues land exactly and scenes sit on the voice's clock. The voice is muxed as is, or placed line by line throughshowtime audio mixwhen scenes drift or a music/effects mix is given. showtime manim check: cue words the narration never says, late reveals, holds over 4 s with nothing moving, on-screen word budgets, one concept in two colours, safe area, and LaTeX availability with the exact install line per OS.templates/manim/: a narrated two-scene example ("odd numbers build squares", check-clean) and five pattern starters. Guide:references/manim.md.- Optional
manimglextra: ManimGL 1.7.2 (the released OpenGL build) in its own venv, used for scene files that importmanimlib. The kit and checks are Manim Community only. showtime doctorrows for manim, LaTeX (packages and per-OS FIX lines) and manimgl; setup smoke-tests the manim install and prints the cairo/pango build line per OS when it fails.
Added: plugin executables, settings, MCP server and progress monitor#
- Running the CLI from the skill. SKILL.md now runs
"${CLAUDE_SKILL_DIR}/bin/showtime" <cmd>(Claude Code substitutes the skill's folder), so the command works from any working directory with no PATH setup. The plugin deliberately has no top-levelbin/: claude.ai and Cowork (including organization sync) refuse to install plugins that have one. - Plugin settings (
userConfig, all optional with defaults): default narration voice and language, open studio boards in the browser, a CPU limit (max_workers) and the install folder. Claude Code passes them to the MCP server, which saves them to~/.showtime/plugin-settings.json; the launcher turns that file intoSHOWTIME_VOICE,SHOWTIME_LANG,SHOWTIME_OPEN_BROWSER,SHOWTIME_MAX_WORKERS/SHOWTIME_THREADSdefaults (environment and flags still win). The voice resolver, render worker choice andstudio openhonour them;showtime version --jsonshows what is in effect. - MCP server (
skills/showtime/mcp/server.mjs, registered in.claude-plugin/plugin.json): 18 tools that wrap the CLI with validated paths, no shell, progress notifications, cancellation, trimmed output and a saved full log. It speaks both the 2026-07-28 per-request protocol and the olderinitializehandshake, and returns the summary as text (Claude Code showsstructuredContentto the model in place of the text, so machine facts go in_meta). Config for Claude Desktop, Cursor and Codex inreferences/mcp.md. - Progress monitor (
monitors/monitors.json): long commands append milestones to~/.showtime/logs/progress.jsonl(SHOWTIME_PROGRESS_LOG=0turns it off); the monitor reports jobs still running after 45 s, each further 25% and the end, and stays quiet about short ones. - Tests:
tests/test_mcp.py(both protocol eras, validation, doctor -> new -> render preview -> qa over stdio, settings reaching the CLI, manifest wiring, monitor).
Changed: fixes from the eleven example videos (friction log)#
Each item was hit while making examples/; the fix is in code (with a test) unless marked docs.
- Render. A poster is baked into frame 0 only when it looks like the opening (
--poster-bake auto|force|off): a mid-video poster over an opening that builds from empty used to flash for one frame on autoplay and every loop; qa now WARNsposter_flash. Encode defaults can live in showtime.json"render"(crf, preset, format, poster ...), and a final more than twice the size of the previous one warns. "final.mp4 exists; writing final-2.mp4" is an info line, not a warning. The offline score page is no longer closed while it renders (it shipped silent finals on busy machines); a failed soundtrack is retried and then fails the render unless--allow-silent. The voice-over-score gap is measured (audio.voice_over_score_db). Studio renders keep their scratch in<job>/work/renders/. ETAs are measured from the first unit of work. - Sizes are decimal MB everywhere (render, qa, exports, html export), like upload limits.
- Exports.
deliver exports --max-mb N(two-pass, lands under the cap) and targetsoriginal,github,chat,webat the master's size; padded exports record their picture so qa judges black and frozen stretches inside it; exports carry the edit report (upscale lineage).--pad-colordefaults to the project's background. - snap takes a rendered video (
showtime snap final.mp4 --at 4.2), snaps to the nearest frame, keeps the requested time in file names and says when two times share a frame;--compare other.mp4writes a before|after sheet. - review-pack. Rounds count critic answers (FINDINGS.md), so a newer final or an interrupted pack
rebuilds the same round;
cuts.jpgshows every frame around each cut; other deliverables in the job and missing context are named in CRITIC.md; the video's own .srt ships in the pack; labels draw accented letters. review.md: what to do with no sub-agent tool, and how to verify polish after a "ship" verdict. - check uses qa's still-hold rule (
freeze_noise_dbin thresholds.json), measures overlaps on glyph ink, names the edge insafe_zone, addsedge_marginandcontrol_stripfor landscape, reads SVGfill, treats contrast measured mid-entrance as a note, names fallback glyphs, surfaces component warnings, and raises its render estimate for<video>layers and busy machines. - Captions. An .srt/.vtt input keeps its cues and line breaks (ASS and sidecars agree;
--regroup); cues shorter than 0.7 s (0.4 s karaoke) merge instead of flashing; short sentence tails join the cue before; cues snap to EDL cuts; line breaks avoid function words and split names;--max-wordsreaches the sidecars; a biggersizelowerschars; qa WARNscaption_flashand prints the shortest cue; only a sidecar in the job folder re-points the job's captions; a variant's own .srt wins in qa.edit render --captions <style>starts from that style's defaults;--caption-position. - caption-karaoke:
keepphrases,skipLines, short cards merge (minShow). - Voice. Sidecars store portable paths (relative,
showtime:/showtime-home:prefixes);--fitlands exactly on the target and warns above x1.1;voice cueswrites aVOtable for canvas cue tables. - Audio.
gain_pointsandsection_gainautomation; akeystrokestrack clicks in sync with a demo recording;audio fit --from; a fitted track withoffsetuses a shifted beat grid (it was off by the offset); the mix report listsend_hit/sections/downbeats, per-sfxabove_bed_db, warns on a quiet first section and masked effects, and uses relative paths.m.faderamps from the automated level;m.ramp,m.duckUnder.showtime scorewrites into the job, never ashowtime-out/inside the project, and reports the voice-over-bed gap for narrated projects. - Components. chart: negative values and a zero line, file options honoured (decimals ...), tick
decimals,
+/−signs, hard ranks for ties,count,highlightAt,ref,dots,--chart-mutedon:root; count-up reserves the widest counting value and lifts "°C"; ken-burns fades only mid-scene, CSS object-fit,mask; browser-frame scrolls measured after decode, keeps children over a screenshot,camera: frame; kinetic-typestyle: none; ripple strengtha;peakalignment for flash/glitch; film transitions takeblur;F.relocate. Clip edges within 1 ms of a frame land on it. - Capture and demo.
demo.type(text, opts)types into the focused field (and logs its box); frames are really captured at--dpr;demo.wide(); autozoom: no bridging across chapters,--hints,--cursor-offset,--keys-pos,--key-glyphs,<name>.keys.json, corner room, shot list in the output. site capture keeps an app's own dialogs, lists hidden overlays with sizes, skips in-app quotes as testimonials and white/black buttons as the primary colour. NASA media: third-party notices in the description change the licence (JunoCam processing, ESA/Hubble), results show full ids, size, length and file size (--min-size,--max-duration,--max-mb), and each quality is cached separately. - Footage. Transcripts default to the job (a new
<name>-editjob when there is none), never next to the user's footage;footage trim(excerpts and seek-friendly proxies,--webm);footage scenes --everytakes--from/--toand a file-o;edit checkreports near-contiguous ranges; upscale messages say when 1080x1920 from 1080p is unavoidable, andoutput.allow_upscaleaccepts it; the face tracker seeds on the median of the first second with a zoom-scaled dead zone. - Jobs.
showtime job discardmoves an unused render out of the job; a hand-setnextis dropped by the next stage or render; a delivered job says "nothing required"; job names resolve from inside a job; studio approvals are read from feedback.json (they stayed "not approved" after later board revisions). - Templates.
showtime newno longer copies the template's README.md into the project; JSON files are written with normal permissions (not 0600). - Studio.
studio decideappends the next D-nnn;studio frame --storyboardfills storyboard thumbs; the board opens on the picked concept (or one with an animatic);feedback --newlists only what is new, and removed questions keep their names from earlier board revisions;previewno longer reports a slow start (building the mix) as a failure. - Release.
check_release.pyfails on committed videos over 20 MB and on published*.work/folders;.gitignoreignores everywork/folder.
Added: the crew (specialist sub-agents)#
- Ten plugin sub-agents in
agents/(showtime:creative-director,brand-designer,scriptwriter,storyboard-artist,motion-designer,sound-designer,voice-director,editor,researcher,critic). Each is thin: frontmatter with an explicit tool list (never the Agent tool, so members cannot dispatch others), model and effort per role (judgment roles inherit the session model, production roles use sonnet), the non-negotiable rules inline, a pointer to its brief, and a return contract (DONE,DONE_WITH_NOTES,NEEDS_INPUT,BLOCKED). Why: studio and publish-bound jobs have independent pieces (concepts, brand, script, scenes, sound, voice, fact check, critique) that run better in parallel with fresh contexts, while the main session stays the only voice to the user. - Host-neutral role briefs in
skills/showtime/references/crew/pluscrew/rules.md(shared hard rules, reading order, return contract), so the linked dev install and other agent hosts use the same text through a generic sub-agent, or the director follows the brief inline. references/crew.md: the director's playbook (when to dispatch whom per mode, theTASK.mdskeleton, parallel patterns for the pitch round, build and fix loop, merging scene fragments withdata-start="#prev-scene", a CPU budget for heavy commands, token cost). Quick mode stays inline except long-video scene builders and, when publish-bound, the researcher and critic.- SKILL.md: a short Crew section with the phase table (studio bullet and two studio red flags tightened
to stay within the word budget);
modes.mdsection 4,review.mdsection 3 andstudio.mdlink the roles. scripts/check_release.pygains anagentscheck (frontmatter, name = file name, description at most 300 characters starting "showtime crew.", no Agent tool, known model/effort/color values, no fields plugin agents ignore, every${CLAUDE_PLUGIN_ROOT}path exists, brief and rules pointers, return contract, one agent per brief) and scans agent bodies for unknownshowtimecommands;tests/test_skill_structure.pyruns it and proves each rule fails on a bad sample.
Added (from end-to-end trial runs of the skill)#
showtime export html <project>: a project as one self-contained interactive HTML video (player with poster, chapters, keyboard, fullscreen; liveST.scoreor an embedded AAC/Opus soundtrack;--folderfor hosting;--max-mbbudget, 16 MB by default). Frames match the render. Why: videos are shared as web pages and artifacts too, not only as MP4 (references/html-export.md, demos inexamples/_html/).showtime voice script --fit SECONDS: lands a whole narration on a length (shared speed change within 0.85-1.15x, then shorter pauses, then "cut about N words"); a short script is padded with silence. Why: a 15 s vertical explainer came out at 16.55 s and only hand-cutting words fixed it.showtime retime <project> --from-voice voice/timeline.json(--map,--pad,--keep-captions): scene lengths from the narration, one voice track per line, ducking, and music sections, effects, poster and caption words moved with their scenes. Why: those values were being copied from the slots by hand, scene by scene.showtime data inspect|import: a CSV/TSV/JSON table becomes chart data (--chart bar|hbar|line|race,--scenewires it into the page). Why: every data story needed a hand-written conversion.showtime new --job JOB: records the project in the job (a project folder inside a job is recorded without it).job note --project, and ananimaticoutput kind for renders under<job>/studio/.showtime renderwrites<work>/logs/render.logand prints its path.
Changed#
edit render: a range that continues the same source where the previous range ended (a reframe-only split) joins without the 30 ms edge fades, which dipped continuous room tone by 14-18 dB for ~20 ms.showtime check --dead-airdefaults to 2.5 s, the same rule as qa'sfrozen, and both readruntime/thresholds.json; an end hold up to 4 s is afinal_holdnote. Why: check passed a 4.2 s still that qa then flagged, a full render later.checkreports text hidden under a badge, notes layout and contrast seen only mid-transition (and judges the settled frame), groups repeatedsmall_textnotes, and listsscenesandtransitionsin report.json.new --durationandretimewarn when a scene is stretched more than 1.5x; retimed transitions stay at 0.35 s or more.showtime captionsandvoice script'svo.srtgroup within qa's limits (42/32 characters, 20 characters/s; one shared module), karaoke styles write one event per group, a sidecar written inside a job becomes the job's captions, and the command says when the video already burns captions.deliver poster|exports|thumbaccept a job (its latest final);poster --bakerecords the baked file as the latest final, bakes once and never overwrites.edit checkaccepts a job.- Footage tools (
footage scenes,reframe,grade,denoise,stabilize,luts --preview,view) write into the job'swork/, never beside the user's footage; reframes and EDL renders warn when a source is enlarged more than 1.5x. - qa: new
resolutionandupscalewarnings; it checks the job's latest captions; after a WARN the job's next command is a fix, not an export. review-pack takes scene frames from the planned scenes instead of detecting cuts in the pixels, and CRITIC.md carries the seven judging questions. - Components:
end-cardshows a logo next to the name,browser-frame/device-framedrift slowly on a still screenshot (data-drift), chart labels keep a readable minimum size and callouts clear the value labels, count-up prints "+160%".studio frame --titlecreates a missing concept.
0.1.0 — first complete build#
The first version that makes whole videos end to end on one machine: HTML/canvas motion graphics rendered frame by frame, narration, music and sound design, footage editing from transcripts, captions, and platform exports. Everything runs locally; no accounts or API keys.
Install and health#
showtime setup(stdlib-only installer, idempotent and resumable): static ffmpeg/ffprobe per platform, a Python 3.12 virtualenv built with uv from pinned requirements (PEP 508 markers for Intel macOS pins), pinned Node packages including Playwright, and sha256-checked models. Tiersminimal(CI),core(default) andfull; optional extras with--with;--listand--estimateshow sizes and time before anything is downloaded. Re-running with--with Xkeeps the tier and extras already installed.showtime doctor: real checks (a test encode, venv imports, Node packages, a headless browser with WebGL, every model file, the espeak-ng self-test) as PASS/WARN/FAIL, each problem with a one-line fix;--jsonfor scripts;--report [JOB]writes a redactedbug-report.mdlocally (never uploaded).- Entry points
bin/showtime(POSIX sh),bin/showtime.cmdandbin/showtime.ps1only find a Python and run the stdlib launcher, which resolves the skill from its own location (a copied or plugin-installed tree works the same as a checkout). - Grouped
showtime --helpwith examples per group; every command has--helpwith examples. Errors print what went wrong, why and the exact fix; tracebacks only with--debug. Colour turns off automatically when output is not a terminal (or withNO_COLOR). Running a command before setup prints exactly what to install, with size and time. showtime version [--json],showtime paths,showtime new <template> <dir>.
Make (HTML/canvas projects)#
- Stage runtime with a strict time contract (every frame is a pure function of time) and a virtual-time shim, so renders are frame-exact and repeatable.
showtime render(parallel headless Chrome capture, H.264 BT.709 encode, offline audio, loudness to -14 LUFS, poster bake),preview(browser player with scrubber, frame step and synced audio),check(pre-render QA: timers, failed requests, contrast, system fonts, overlap, safe zones),snap(stills and contact sheets),score(render only the Web Audio score),server.- Canvas film toolkit and a Web Audio synth/score API; 20 DOM motion components, 12 CSS and 11 WebGL
scene transitions, 6 themes;
showtime motionlists them andshowtime codeprepares highlighted code. Templates:dom,film,short,tutorial,data.
Audio and voice#
showtime audio: procedural composer (16 styles, sections on downbeats, stems, MIDI, beat grid), 56 synthesized sound-effect types, a local library of permissively licensed music/effects/ambience with search and automatic credits, beat analysis, fit-to-length,mix.jsonrendering with ducking and hit alignment, metering and mastering. The default SoundFont bank is FluidR3Mono (MIT).showtime voice: Kokoro, Supertonic and Piper voices with word timings (forced alignment), script-to-timeline narration with pauses, pronunciations and per-line caching, mastering to -16 LUFS.
Footage, capture and assets#
showtime transcribe(word-level, local Whisper/Parakeet, speakers, audio events),pack,edit cut|check|render|view(edit by transcript, frame-exact),captions(five styles, SRT/VTT, burn-in),footage scenes|reframe|denoise|stabilize|view|grade|luts|probe|autozoom.showtime site capture|component|record(screenshots, copy, brand colours and fonts, assets, consent banners declined),showtime demo record|init(scripted app walkthroughs on a virtual clock),showtime autozoom,showtime assets font|icon|emoji|media|cutout|credits.
Deliver#
showtime deliver exportsfor YouTube, X, LinkedIn, Reels, TikTok, Shorts and square, with blur-pad or crop for aspect changes, duration limits and loudness;deliver poster(auto-picked, baked into frame 0 or attached as cover) anddeliver thumb.- Final audio is AAC 256 kb/s, mastered 0.5 dB under the true-peak ceiling, and the encoded peak is
re-checked (
ff.ensure_true_peak) and repaired by re-encoding only the audio. Why: ffmpeg's AAC encoder at 192 kb/s produced a +3.9 dBTP burst on limited speech whose WAV was clean.
Project hygiene#
CONTEXT.mdglossary,.out-of-scope/decisions,CONTRIBUTING.md, a pull-request template,.gitattributesfixing line endings per file type,scripts/check_release.pyandtests/test_skill_structure.py, CI on macOS arm64/x86_64, Ubuntu and Windows (every pull request: structure, unit, a short render and the defect suite; nightly: everything).