skills/showtime/references/debugging-renders.mdDebugging renders: find the cause before changing anything
Read this when a render fails, flickers, shows black or frozen frames, looks different from the preview,
drops fonts or media, drifts out of sync, or when a fix you already tried did not work. For "the video came
out wrong" reports from the user, start with diagnosing.md (intake and evidence), then come back here.
Rules#
- Evidence first. Name the cause, with the command output that shows it, before editing the project.
A change made on a guess costs a re-render and hides the real fault. Start with the log the render
printed (
log <path>:<video>.work/logs/render.log, with every ffmpeg command, browser page and console errors, blocked requests and the failure stack). - Rule out the machine. Render the matching template with the same flags:
showtime new dom st-probe --duration 3, thenshowtime render st-probe --preview(usefilmfor canvas projects). If the template also fails, the environment is at fault: runshowtime doctor, not a scene edit. - No sleeps. Never "fix" flicker, missing images or blank fonts with a delay or a timeout. Gate
readiness on the thing itself:
ST.waitFor(promise)for fonts, images and data; an awaitedseekedfor video. Tools follow the same rule: wait for a file or a port, never for N seconds. - One change per re-render. Change one thing, re-render only the affected range
(
showtime render <project> --from 4 --to 7 --preview), and compare stills at the same timestamps (showtime snap <project> --at 5.2,6.0) with the ones from before. - Three strikes. After three failed fixes for the same symptom, stop patching. Change the approach (another adapter, move the effect from DOM to canvas, pre-render the element to a video clip, drop the effect) and tell the user what you tried, what each attempt showed, and what you will do instead.
- Guard every layer the bug crossed. When you find a cause, add the check that would have caught it
earlier (a
checkorqarule plus a fixture intests/fixtures/defects/) so it cannot come back quietly.
Signals from the user that you are guessing: "that's not what I see", "it still flickers", "you changed the wrong thing", "it was fine before". The answer is a snap or a contact sheet at the timestamp in question, not another blind edit.
The pipeline and what reveals each hand-off#
A render passes through these boundaries. Find the first one where the output is already wrong.
| Hand-off | What goes wrong there | Command that shows it |
|---|---|---|
| project → server | 404s, wrong paths, remote URLs (renders are offline) | showtime check <project>: missing_file, network, request_failed |
| server → page ready | a promise never resolves, a script throws on load | check: ready_failed names what it is still waiting for; showtime preview <project> shows the console |
| page → seek | a scene handler throws for some t |
check: seek_error; showtime check <project> --find-first error |
| seek → pixels | real-time timers, CSS transitions, unseeded randomness, unawaited video | check: timers, css_transitions, unseeded_random, nondeterministic, unstable_frame; --determinism; --find-first nondeterministic |
| pixels at one time | wrong layout, blank text, missing font glyphs | showtime snap <project> --at t compared with preview at the same t; check: font_*, text_*, safe_zone, low_contrast (canvas films included) |
| timeline | black gaps between clips, stalls | --find-first black, --find-first frozen --min 1 |
| capture → encode | missing or retried frames, wrong frame count | render.json (frames, warnings, timings), work/diagnostics/ |
| encode → file | codec, pixel format, colour tags, faststart, duration | showtime qa <final.mp4> |
| audio: score / mix → master → mux | silence, clipping, wrong loudness, drift | showtime score <project> (music only), audio/mix.report.json, showtime audio meter <file>, qa: loudness, true_peak, clipping, silent_gap, av_length |
| master → platform files | wrong size, too long, too big | showtime qa <export> --platform reels (and the other targets) |
Bisect in time, not in code#
Video faults live at a moment. showtime check <project> --find-first <probe> scans the timeline coarsely,
then halves the interval until it has the exact frame, and saves that frame and the last good one:
| Probe | Finds | Typical cause |
|---|---|---|
black |
first near-black frame | a clip window gap (data-start/data-dur), a video that failed to load |
frozen |
first frame of a still stretch of at least --min seconds |
a paused timeline, a missing adapter, a gap after re-timing scenes |
nondeterministic |
first frame that differs when reached by a jump vs by stepping, or after 150 ms | a timer, Date.now() at load, Math.random(), a video not awaited |
error |
first time where the seek handler throws | an index out of range, data that runs out |
Narrow it with --from/--to and --step. Exit code 1 means a bad frame was found.
Determinism#
A frame must be a pure function of t. showtime check probes 4 frames by default; --determinism probes 12
and also loads the page a second time (like a second render worker) and compares hashes. Differences of 1-2
levels on edges are GPU rasterisation noise and are reported as notes, not errors. Real differences come from:
setTimeout/setIntervaldriving visuals (they run on real time),- CSS transitions (use keyframes or a timeline),
Math.random()inside a draw or seek function (createST.rand(seed)once; Film:F.rng/F.hash),- media not awaited (
ST.waitFor,<video data-st>), - anything read from the wall clock at load.
Symptom → first move#
| Symptom | First move |
|---|---|
| Flicker or jitter | showtime check <project>; look for timers, unseeded_random, css_transitions |
| Looks different from the preview | --determinism; snap vs preview at the same t; font_not_embedded |
| Text in the wrong font | check fonts section; load files with @font-face or /_lib/@fontsource..., await them |
| Black or empty stretch | --find-first black, then the clip windows around that time |
| Nothing moves for seconds | --find-first frozen, or qa frozen with its timestamp |
| Audio out of sync | the mix report's hit offsets; qa av_length; re-render the range, never retime to hide it |
| Audio too quiet, loud or distorted | qa loudness/true_peak/clipping; showtime audio meter on each stem |
| Render crashes or times out | showtime doctor; retry with --workers 1 or --gpu off; work/diagnostics/ |
| Slow | render.json timings, check's estimate and heavy_effects note (large blurs, backdrop filters) |
When the cause is found and fixed, re-run the command that showed it and quote its new output. Then run
showtime qa on the new file before calling the video ready.