Docs

Rendering is wired end to end.

kavio render selects the fast FFmpeg-direct path for eligible image sequences and keeps browser-overlay rendering as the full-fidelity path for richer compositions.

Implemented Pieces

React Frames

@kitsra/kavio-react renders a component to static markup for each frame and captures it with the same Playwright and FFmpeg pipeline. Kavio JSON still owns timing, dimensions, exports, base media, and audio. Select browser-overlay explicitly; effects, hydration, browser state, and Remotion APIs do not run. See the React rendering guide for the API, determinism rules, and performance guidance.

Intended Pipeline

  1. Validate the composition and prop rows.
  2. Expand template rows across export presets into stable jobs.
  3. Capture frames through the browser renderer.
  4. Build an inspectable FFmpeg plan.
  5. Encode media outputs and write metadata.
  6. Clean temporary files according to job policy.

Picture In Picture

Overlapping video layers become FFmpeg picture-in-picture planes beneath browser-rendered graphics. TypeScript authors can use pictureInPicture(...) for responsive corner placement, proportional sizing, edge inset, muted playback, and stacking defaults. Add the full-frame base video first and declare inset audio explicitly when it should be mixed.

Outputs And Quality

The current render path supports mp4, webm, mov, gif, still-image png, and png-sequence output. PNG sequences stream browser captures to a fresh directory as frame-00000.png, frame-00001.png, and so on. Use the comparison helper to gate full-video and selected-frame SSIM/PSNR before accepting a visual change. Browser-overlay remains the recommended mode for complex transitions, text, masks, and arbitrary keyframes. In batches, later compatible browser-overlay jobs reuse worker-local Chromium processes; timings.browserLaunches reports 0 when that reuse occurs. Within a loaded composition, the browser renderer also reuses deterministic plain-text, image, and shape DOM layers. Videos, captions, animated or keyframed layers, and transition participants remain frame-dynamic, and composition reloads clear the reused layer state.

Linear full-frame image transition tracks can stay on FFmpeg-direct for fade/crossfade, directional wipe, slide and push, circular iris and expand-mask, default clockwise clock-wipe, zoom, blur dissolve, squeeze, letterbox reveal, and constrained black/white color handoffs. Directional cover/reveal, corner diagonal wipes, grayscale dissolves, and default-count bar wipes also stay native. Composition inspection reports browser and FFmpeg support per transition window. Other strengths, colors, shapes, directions, or easing continue through the browser-overlay renderer for fidelity.

Opaque graphics-only browser renders encode the background-painted Kavio stage as the primary PNG stream. This preserves every captured transition frame instead of letting a faster synthetic background advance while a secondary overlay is still being produced. Hybrid source-video and custom React renders retain overlay compositing.

Portable Transition Support

JSON stores semantic transition names rather than FFmpeg filter names. Core evaluation and the browser renderer are the fidelity baseline; auto selects FFmpeg-direct only when the complete image track and transition parameters have an equivalent native path.

JSON / builderBrowser behaviorFFmpeg-direct mapping
cover({ direction })Incoming layer moves over a stationary outgoing layer.coverleft, coverright, coverup, or coverdown.
reveal({ direction })Outgoing layer moves away from a stationary incoming layer.revealleft, revealright, revealup, or revealdown.
diagonalWipe({ corner })Deterministic polygon reveal from one of four corners.wipetl, wipetr, wipebl, or wipebr.
grayscaleDissolve()Opacity fade with a grayscale-to-color ramp.fadegrays.
barWipe()Directional parallel bars with optional row/column counts.Default counts use directional slice filters; custom counts use browser-overlay.

CLI and MCP inspection attach a capability object to every transition window. The native result includes the exact filter; an unsupported result explains why the browser fallback is required.

{
  "transitionType": "diagonalWipe",
  "renderSupport": {
    "browser": { "supported": true },
    "ffmpegDirect": { "supported": true, "filter": "wipetr" }
  }
}