The Render Pipeline
ViralSync's render pipeline distributes video rendering across multiple cloud workers using a scene-based execution model. This page explains each phase, what status and phase values you'll see in the API, and how to interpret timing.
Pipeline overview
POST /v1/render/jobs
│
▼
┌─────────────┐
│ 1. Compile │ Parse movie JSON → create render plan
└──────┬──────┘
│
┌──────▼──────┐
│ 2. Dispatch │ Create one scene job per scene
└──────┬──────┘
│ (parallel)
┌──────▼──────────────────────────┐
│ 3. Execute (per scene) │ FFmpeg renders each scene → intermediate MP4
│ scene_001 scene_002 scene_003 │
└──────┬──────────────────────────┘
│ (all scenes complete)
┌──────▼──────┐
│ 4. Assemble │ FFmpeg concat → final video
└──────┬──────┘
│
┌──────▼──────┐
│ 5. Finalize │ Upload to R2, extract metadata
└──────┬──────┘
│
outputUrl available in job response
Phase details with API status mapping
| Phase | status | phase | What's happening |
|---|---|---|---|
| Compile | running | planning | Movie JSON is parsed. Render plan written to render-plans/{jobId}/plan.json. |
| Dispatch | running | dispatching | Child render.scene jobs created and queued. |
| Execute | waiting | rendering | Scene workers running in parallel. Parent waits. Progress aggregated from child jobs. |
| Assemble | running | assembling | All scene clips concatenated via FFmpeg concat. |
| Finalize | running | uploading | Final MP4 uploaded to Cloudflare R2. Thumbnail extracted. |
| Done | done | — | outputUrl is populated. Render minutes quota updated. |
| Failed | failed | — | error field explains the failure. No render minutes consumed. |
Scene parallelism and quality presets
The number of scene workers active simultaneously is controlled by the qualityPreset field in renderOptions:
| Preset | Parallel scene workers | Wall-clock speedup | Best for |
|---|---|---|---|
stable | 2 | Moderate | Debugging; predictable logging |
balanced | 3 | Good | General use (default) |
fast | 4 | Better | Short videos (≤5 scenes) |
turbo | 6 | Best | Long videos on Business+ plans |
Example: A 12-scene video on turbo preset starts 6 scene workers simultaneously. The first 6 scenes render in parallel; as each finishes, the next queued scene starts. Wall-clock time ≈ 2 × (time to render a single scene).
You can also override with requestedConcurrency (integer 1–32):
"renderOptions": { "requestedConcurrency": 5 }
Actual concurrency is always min(requestedConcurrency, sceneCount).
Timing expectations
These are approximate benchmarks for a 1080p video with typical media layers (video background + text overlays):
| Phase | Typical duration |
|---|---|
| Compile | < 1 second |
| Dispatch | 1–5 seconds |
| Scene render (per scene) | ~0.5–3× real-time (a 10s scene takes 5–30s) |
| Assemble | ~5–30 seconds (proportional to total video duration) |
| Upload | 3–15 seconds (depends on file size) |
Total for a 10-scene, 5-second-per-scene video (50s total) on balanced preset:
- Dispatch: ~3s
- Scene render: ~4 rounds of parallel workers × ~10s per round = ~40s
- Assemble + upload: ~20s
- Total: ~60–80 seconds wall-clock time
Text-only scenes render significantly faster (~0.2× real-time). Video and image layers with transforms take longer.
Scene caching
If you re-render a movie where some scenes are unchanged, the renderer detects identical scene content and reuses the previously rendered intermediate clip — skipping those scene jobs entirely.
Caching is keyed on a deterministic hash of the scene's full content (layers, sources, timing, transforms). Any change to any layer property invalidates the cache for that scene only.
This means iterating on the end of a long video (editing scene 20 of 20) is much faster than re-rendering from scratch.
Storage locations
| Artifact | Location |
|---|---|
| Render plan | render-plans/{jobId}/plan.json (internal R2) |
| Scene intermediates | intermediates/{jobId}/scene_{index}.mp4 (internal R2) |
| Final output | outputs/{jobId}/output.mp4 (public R2, returned as outputUrl) |
Scene intermediates are cleaned up after successful assembly. The final output URL is available for the duration defined by your plan's output retention policy.
Technical limits
| Parameter | Limit | Notes |
|---|---|---|
| Scene job timeout | 30 minutes | Per scene. Exceeded → RENDER_TIMEOUT error. |
| Assemble job timeout | 30 minutes | For the concat phase. |
| Max scenes per job | Unlimited | Worker pool scales dynamically. |
| Max scene parallelism | 32 | Hard cap; preset/plan limits apply below this. |
| Max video duration (Free/Pro) | 10 minutes | Plan limit, not engine limit. |
| Max video duration (Business) | 20 minutes | — |
| Max video duration (Enterprise) | 120 minutes | — |
| Max resolution | 3840 × 2160 (4K) | Enterprise. Plan-specific limits apply. |
| FPS range | 1–60 | Any integer value. |
What happens when a scene fails
- The
render.scenechild job transitions tofailed - The parent
render.moviejob transitions tofailed - The
errorfield on the parent job references the failed scene - No partial output is produced; no render minutes are consumed
- Retry the parent job by submitting a new render job — cached scenes will not be re-rendered
See Error Reference → for SCENE_RENDER_FAILED, RENDER_TIMEOUT, and other render-specific error codes.