debug-export
Troubleshoot video export failures including codec issues, layer rendering problems, audio sync errors, and browser compatibility.
SKILL.mdAutoAnimation/.claude/skills/debug-export/SKILL.md
GitHubDebug Export
Diagnose and fix video export failures in ProAnimate, covering codec compatibility, layer rendering pipeline issues, audio mixing errors, and browser-specific problems.
Purpose
Video export involves rendering hundreds of frames via Canvas2D or PixiJS, encoding them with WebCodecs or MediaRecorder, and muxing with audio. Many things can go wrong at each stage. This skill helps systematically identify and fix export issues.
Steps
Check the export progress status:
idle-- export not started.preparing-- loading assets (images, Lottie, Three.js, templates). If stuck here, an asset failed to load.rendering-- frame-by-frame rendering. If stuck, a specific layer is hanging.encoding-- flushing the encoder. If stuck, the encoder is backpressured or errored.complete-- export finished. CheckoutputUrlandoutputSize.error-- fatal failure. Checkprogress.error.
Diagnose asset loading failures (stuck in
preparing):preloadImages()loads all character sprites, media URLs, and background images. CORS errors cause failures -- check console forAccess-Control-Allow-Originerrors.preloadLottieAnimations()initializes Lottie instances. Invalid JSON or missing lottie-web causes failures.preloadThreeScene()creates a headless Three.js renderer for 3D characters. WebGL context creation can fail if too many contexts exist.preloadHTMLTemplates()creates offscreen iframes for HTML templates. CSP violations or template errors cause failures.preloadRiggedCharacters()loads rigged character mesh data and bone animations.
Diagnose frame rendering failures (black frames, wrong content):
- Black frames: The Canvas2D renderer (
renderFrame()) draws layers in order: background fill, Lottie, media, shapes, video, characters, text, overlay Lottie, captions. If a layer throws, subsequent layers may not render. - Missing characters: Check that
imageCachecontains the sprite URLs. Character rendering usesdrawImage()with the cachedHTMLImageElement. - Wrong viseme/emotion: The renderer looks up viseme and emotion at the current frame from
visemeTimelineandemotionTimeline. Verify these timelines have correct frame ranges. - Missing text overlays: Text rendering uses
ctx.fillText(). Check font loading -- custom fonts must be loaded before rendering starts. - Missing templates: HTML template layer renders from iframe screenshots. If the iframe did not load in time, the frame is blank.
- Scale issues: When export resolution differs from canvas resolution,
scaleX/scaleYis applied. CheckneedsScalecomputation.
- Black frames: The Canvas2D renderer (
Diagnose GPU renderer failures (PixiJS path):
- If
isWebGL2Supported()returns true, the export tries the PixiJS renderer. - If PixiJS init fails (e.g., WebGL context lost), it falls back to Canvas2D with a console warning.
- Check:
[videoExport] GPU renderer init failed, falling back to Canvas2D. - Force Canvas2D by disabling GPU: set
isWebGL2Supportedto return false.
- If
Diagnose codec / encoder errors:
- WebCodecs path:
VideoEncoder.configure()can fail with unsupported codec. H.264 (avc1.640028) requires Chrome 94+. VP8 (vp8) is more broadly supported. - Encoder error callback: Check for
[videoExport] Video encoder error:in console. Common: encoder closed unexpectedly (usually from memory pressure). - Backpressure: If
encodeQueueSize > 8, the export waits. If this persists, the encoder is too slow for the frame rate. - MediaRecorder path: Falls back when WebCodecs unavailable (Firefox). Uses
canvas.captureStream(0)with manual frame requests. VP8 preferred over VP9 for quality.
- WebCodecs path:
Diagnose audio issues:
- No audio:
collectAudioSources()returned empty. Checkprops.audioUrlandprops.dialogueCharacters. - Audio mixing failed:
mixTracks()threw. Check console for[videoExport] Audio mixing failed:. Common cause: invalid audio URL (404 or CORS). - Audio out of sync: In MediaRecorder path, audio plays in real-time via
AudioBufferSourceNodebut frames render at variable speed. WebCodecs path does not have this issue since audio is encoded separately. - Audio encoding failed:
AudioEncodererror. AAC (mp4a.40.2) for MP4, Opus for WebM. Check browser support.
- No audio:
Diagnose memory issues:
- Large exports (4K, long duration) can exhaust browser memory.
VideoFrameobjects must beclose()d after encoding. A leak here causes rapid memory growth.- Three.js resources must be disposed via
disposeThreeScene(). - HTML template iframes must be removed via
disposeHTMLTemplates().
Key Files
| Purpose | Path |
|---|---|
| Main export function | src/services/videoExport.ts |
| Canvas2D frame renderer | src/services/canvas2dRenderer.ts |
| PixiJS GPU renderer | src/services/pixiExportRenderer.ts |
| Audio mixer | src/services/audioMixer.ts |
| GIF export | src/services/gifExport.ts |
| Transparent export | src/services/transparentExport.ts |
| Remotion composition | src/remotion/VideoComposition.tsx |
| Composition builder | src/services/compositionBuilder.ts |
| Export panel UI | src/components/panels/ExportPanel.tsx |
Common Issues
- "Encoder was closed unexpectedly": Memory pressure caused the browser to close the VideoEncoder. Reduce resolution or export fewer frames.
- Export produces 0-byte file: The muxer was finalized without any video chunks. Check that the encoder received at least one frame.
- Black frames on first few frames: Asset preloading did not complete before rendering started. This should not happen since preloading is awaited, but check for race conditions.
- "Export cancelled (AbortError)": User cancelled or the
AbortSignalwas triggered. Expected behavior. - Firefox exports as WebM when MP4 requested: Firefox does not support WebCodecs.
resolveMediaRecorderMime()falls back to WebM. CheckExportResult.didFallback === true. - GIF file too large: Reduce quality: increase
frameSkipto 3, lowermaxWidthto 320, disabledithering. - Canvas tainted: An image loaded without CORS was drawn to the canvas, making
getImageData()fail. Ensure all image URLs have proper CORS headers.
Examples
Check WebCodecs support:
import { isWebCodecsSupported, isMp4Supported } from '@/services/videoExport'
console.log('WebCodecs:', isWebCodecsSupported())
console.log('MP4:', isMp4Supported())
Force Canvas2D renderer (bypass GPU):
// In pixiExportRenderer.ts, temporarily return false:
export function isWebGL2Supported(): boolean { return false }