Playback Debugging
Targeted workflow for preview stalls, scrub freezes, decode drift, and render-path mismatches.
What To Use First
Section titled “What To Use First”Playback bugs in MasterSelects usually span three layers at once:
- media readiness and browser decode state
- render-loop and target scheduling state
- timeline-to-preview sync and cache behavior
Use the browser monitors and AI bridge tools together instead of guessing from the UI.
For an inspector-dependent slowdown, compare ten seconds of playback with the
same effect enabled and its controls expanded versus collapsed. Use
samplePlaybackFramePacing for whole-page frame gaps and long animation-frame
attribution: render-loop CPU timings alone omit React work triggered by playhead
updates. Raw video callbacks and render counts also do not prove a temporal
effect produced new images; inspect getStats().slitScanPlayback alongside them.
Keep graph discovery tied to authored effect changes and avoid rebuilding
unchanged parameter controls on every playhead tick. Animated values must still
update, including changes made in Nodes.
Browser Surfaces
Section titled “Browser Surfaces”These globals are the fastest way to inspect the live playback path:
window.__WC_PIPELINE__for WebCodecs events, seeks, stalls, and aggregate counterswindow.__VF_PIPELINE__for HTML video / VideoFrame fallback eventswindow.__PLAYBACK_HEALTH__for health state, anomalies, active video status, and recovery helperswindow.__FRAME_PHASES__for per-frame phase timelines and summarieswindow.Loggerfor buffered module logs and redacted summaries
Useful console setup:
Logger.enable('WebCodecsPlayer,PlaybackHealth,LayerCollector')Logger.enable('VideoSyncManager,ParallelDecode,RenderLoop')Logger.setLevel('DEBUG')Additional shortcuts:
Logger.search('device')Logger.errors()Logger.dump(50)Logger.summary()AI Bridge Tools
Section titled “AI Bridge Tools”When the development bridge is available, prefer the structured tools:
getStatsgetStatsHistorygetAudioDiagnosticsgetLogsgetPlaybackTracegetRuntimeDiagnosticspurgePlaybackPathsamplePlaybackFramePacingsimulateScrubsimulatePlaybacksimulatePlaybackPathgetClipDetailsreloadApp
The most useful payload for real playback bugs is usually:
getStatsgetPlaybackTracegetLogsfiltered to playback modules
For crackling, pops, or dropouts during audible playback, capture getAudioDiagnostics during the noise. It reports per-element ready/buffer state, approximate source-time drift, Web Audio context latency/state, routing graph state, and recent audio_drift / audio_drift_correct events.
Signals To Watch
Section titled “Signals To Watch”These fields are the highest-signal indicators across trace, stats, and simulation results:
stalePreviewWhileTargetMoveddecoderResetspreviewFreezeEventspreviewPathCountsentries such asempty-hold,paused-empty-hold, andtarget-empty-holddriftSecondsgetAudioDiagnostics.events.correctionMsgetAudioDiagnostics.mediaElements[].buffered.bufferedAheadSecondsgetAudioDiagnostics.routing.context.baseLatencyMsfirstPreviewUpdateMsFRAME_STALLSEEK_STUCKHIGH_DROP_RATEGPU_SURFACE_COLD
If the preview is black after reload, also confirm the browser media element is actually ready. A valid render path with a cold or unready surface still produces empty frames.
Common Failure Patterns
Section titled “Common Failure Patterns”Black Preview Or Black Source Monitor
Section titled “Black Preview Or Black Source Monitor”- Check browser media
readyStatefirst. - Check
getStats.decoderandplayback.pipeline: the active path can be full WebCodecs, HTML video / VideoFrame (including cache variants), Native Helper, or ParallelDecode. - If external texture import fails, inspect the media readiness and the recorded GPU/runtime diagnostics before assuming a browser-specific fallback.
Scrub Freezes Or Delayed Updates
Section titled “Scrub Freezes Or Delayed Updates”Main preview rendering allows one GPU submission to finish before admitting the next frame. Repeated playback or scrub requests are coalesced; completion requests a fresh render of the current playhead instead of retaining old layers or times. This bounds queued work when an expensive GPU pass runs slower than the timeline and prevents a burst of stale images after releasing the playhead. Export and RAM preview generation retain exact-frame sequencing. GPU loss uses normal device recovery rather than waking a failed queue. This limits latency, not shader cost.
- Inspect
previewFreezeEventsandfirstPreviewUpdateMs. - Check whether RAM preview is stale while the target moved.
- Confirm whether the render loop is idle and requires restarting.
Paused Preview Shows A Neighbouring Frame
Section titled “Paused Preview Shows A Neighbouring Frame”The playhead moved (frame step, ruler, setPlayhead, pause) but the preview shows the frame
before or after it:
- In
__VF_PIPELINE__.events()look forvf_seek_wrong_frame(a frame-exact seek presented another frame, e.g. unusual container timestamps), and comparevf_preview_frametargetTimeMswithdisplayedTimeMs: on a correct paused frame both lie in the same source frame. vf_stop_resyncafter a pause is the frame-exact seek of the frozen video onto the playhead’s frame (offsetMs: how far its clock was off); the playhead itself never moves after the pause.- A paused render request is presented at display cadence; a delay of about 250 ms between
vf_seek_doneand the nextvf_preview_framepoints at a dropped render request. - Background: Frame-exact paused preview.
Drift During Playback
Section titled “Drift During Playback”- Check
driftSeconds, handoff events, and active anomaly flags. - Compare the active decoder/pipeline with the WebCodecs and VF event streams to see where sync diverged.
- Verify whether the issue is clip-specific with
getClipDetails.
Source FPS Higher Than Composition FPS
Section titled “Source FPS Higher Than Composition FPS”- Playback preview is visually locked to the active composition frame rate. A 60 fps video in a 30 fps composition should show about 30 render/preview updates per second, not every decoded source frame.
samplePlaybackFramePacingmay still report the browser media element advancing at the source cadence throughvideoQuality/ video frame callbacks. UserenderLoop.renderCountDelta,stats.fps,stats.targetFps,playback.previewUpdateFps, andvisualTargetFpsto confirm the visible composition cadence.- During playback, the HTML media clock stays continuous to avoid per-frame seeking; visual layer target times are quantized to composition frames for cache/provider selection and deterministic preview presentation.
- Full WebCodecs playback is disabled by the default feature flag, but can be enabled through the persisted settings toggle; it disables the HTML preview fallback.
Export Looks Fine But Preview Is Wrong
Section titled “Export Looks Fine But Preview Is Wrong”- Compare target routing and render-target state in
getStats. - Confirm the issue is not limited to a popup output or independent preview target.
- Check whether cached hold frames or fallback frames are masking a decode problem.
Minimal Repro Routine
Section titled “Minimal Repro Routine”- Reload the app.
- Reproduce once without changing settings.
- Capture
getStats. - Capture
getPlaybackTrace. - Enable targeted logger modules and reproduce again.
- Compare whether the issue happens on the main preview, source monitor, and popup output.
This isolates whether the problem is in decode, render scheduling, target routing, or overlay state.
Source Map
Section titled “Source Map”src/services/playbackHealthMonitor.tssrc/services/playbackDebugStats.tssrc/services/framePhaseMonitor.tssrc/services/wcPipelineMonitor.tssrc/services/vfPipelineMonitor.tssrc/components/preview/Preview.tsxsrc/components/preview/SourceMonitor.tsxsrc/services/aiTools/bridge.tssrc/services/nativeHelper/NativeHelperClient.ts
Cadence measurement across pauses
Section titled “Cadence measurement across pauses”Render-loop cadence samples compare successful presentations only while playback, scrubbing, or continuous rendering requires frames and the tab is visible. Pausing or changing visibility resets that comparison baseline. The first frame after resuming is not compared against a frame shown minutes earlier. Paused preview refreshes therefore do not inflate playback drop counts. This corrects measurement; it does not establish the cause of slow playback on older builds.
