Understanding the EDL JSON Format: How to Structure an Edit Decision List for video-use
The EDL JSON format in video-use is a structured JSON document that defines video sources, cut ranges, color grading, overlays, and subtitles to drive the entire automated rendering pipeline.
The browser-use/video-use repository treats the Edit Decision List (EDL) as the single source of truth that powers every stage of post-production, from segment extraction and color grading to overlay compositing and subtitle burning. Mastering the EDL JSON format is essential for anyone automating video workflows with this tool, as the schema directly dictates how helpers/render.py constructs the final output.
Core Structure of the EDL JSON Format
A valid EDL file is a JSON object with seven top-level fields. The specification is documented in SKILL.md (lines 268-287) and consumed by the render pipeline in helpers/render.py.
Root-Level Fields
The EDL requires these mandatory and optional fields:
version– Integer schema version (currently1).sources– Object mapping short identifiers (e.g.,"C0103") to absolute or relative video file paths.ranges– Ordered array of cut objects defining which segments to extract from which sources.grade– Color grading instruction: either a preset name, a raw ffmpeg filter string, or"auto"to trigger per-segment analysis.overlays– Optional array of animation clips to composite on top of the final video.subtitles– Optional path to an SRT file burned in after overlays.total_duration_s– Float representing the computed total length of the final video in seconds.
The ranges Array
Each object in ranges defines a single cut with precise timing and metadata:
{
"source": "C0103",
"start": 2.42,
"end": 6.85,
"beat": "HOOK",
"quote": "…",
"reason": "Cleanest delivery, stops before slip at 38.46"
}
The source value must match a key in the sources object. Timestamps are in seconds and support floating-point precision for frame-accurate cuts.
How the Render Pipeline Consumes the EDL
The render.py module transforms the ELD JSON into a series of ffmpeg commands through four distinct stages.
Grade Resolution and Filter Conversion
First, resolve_grade_filter (lines 66-85 in helpers/render.py) interprets the grade field:
"auto"– Returns the sentinel"__AUTO__"to triggerauto_grade_for_clipduring segment extraction.- Preset name – Looks up the corresponding ffmpeg filter string via
grade.get_preset. - Raw filter – Passes the string directly to ffmpeg unchanged.
Per-Segment Extraction with Audio Fades
For each entry in ranges, extract_all_segments builds a command that:
- Seeks to the start timestamp using
-ssfor fast, accurate seeking. - Scales output to 1080p (or 720p for draft previews) while preserving portrait orientation via
is_portrait_source(lines 73-78). - Applies HDR tone-mapping if the source uses PQ/HLG via
TONEMAP_CHAIN(lines 95-117). - Injects the resolved color grade filter.
- Adds a 30ms audio fade-in/out using
afadeto eliminate clicks at cut points (lines 88-90).
Lossless Concatenation
After extraction, concat_segments (lines 67-82) uses the ffmpeg concat demuxer to join all MP4 segments with stream copy (-c copy), avoiding generational loss between cuts.
Overlay and Subtitle Composition
If overlays or subtitles are present, the pipeline builds a final filter graph that:
- Inserts each overlay clip at its specified
start_in_outputtime (PTS shift). - Applies the
subtitlesfilter last, using the style constants defined inSUB_FORCE_STYLE(lines 51-56) to ensure readability on social platforms. - Writes the final output with
+faststartfor web streaming optimization.
Critical Implementation Details
Several hardcoded behaviors in helpers/render.py affect how you should structure your EDL:
- HDR Handling – Sources detected as HDR via
is_hdr_sourceautomatically receive tone-mapping to prevent oversaturation on SDR displays. - Portrait Orientation – Vertical videos are identified via
is_portrait_sourceand scaled accordingly to maintain aspect ratio. - Auto-Grade Logic – When
gradeis"auto", the pipeline executesauto_grade_for_clip(lines 34-38) to analyze histogram and contrast per segment. - Subtitle Priority – The SRT specified in
subtitlesis always burned in last, appearing over both video and overlays. - Animation Overlays – Each overlay object requires
file,start_in_output, anddurationfields to position the animation correctly on the timeline.
Code Examples
Building an EDL Programmatically
import json
import pathlib
edl = {
"version": 1,
"sources": {
"C0103": "/abs/path/C0103.MP4",
"C0108": "/abs/path/C0108.MP4",
},
"ranges": [
{
"source": "C0103",
"start": 2.42,
"end": 6.85,
"beat": "HOOK",
"quote": "…",
"reason": "Cleanest delivery, stops before slip at 38.46."
},
{
"source": "C0108",
"start": 14.30,
"end": 28.90,
"beat": "SOLUTION",
"quote": "…",
"reason": "Only take without the false start."
},
],
"grade": "warm_cinematic",
"overlays": [
{"file": "edit/animations/slot_1/render.mp4", "start_in_output": 0.0, "duration": 5.0}
],
"subtitles": "edit/master.srt",
"total_duration_s": 87.4
}
path = pathlib.Path("edit/edl.json")
path.write_text(json.dumps(edl, indent=2))
Rendering from the Command Line
python helpers/render.py edit/edl.json -o final.mp4 --build-subtitles
Loading and Inspecting an EDL
with open("edit/edl.json") as f:
edl = json.load(f)
first_cut = edl["ranges"][0]
print(f"First cut: {first_cut['source']} from {first_cut['start']}s to {first_cut['end']}s")
Summary
- The EDL JSON format is the declarative blueprint that
video-useuses to drive automated video editing. - Key fields include
sources,ranges,grade, and optionaloverlays/subtitles. - The render pipeline in
helpers/render.pyresolves grades, extracts segments with 30ms audio fades, concatenates losslessly, and composites overlays. - HDR sources are automatically tone-mapped, and portrait videos are preserved during scaling.
- Subtitles are burned last in the filter chain, ensuring they appear above all other visual elements.
Frequently Asked Questions
What values are valid for the grade field in an EDL?
The grade field accepts three types of values: a preset name (like "warm_cinematic") that maps to a predefined ffmpeg filter, a raw ffmpeg filter string for custom color correction, or the string "auto" to trigger per-segment histogram analysis via auto_grade_for_clip in the render pipeline.
How does video-use handle portrait-oriented source videos?
When extracting segments, the pipeline checks is_portrait_source (lines 73-78 in helpers/render.py) and adjusts the scale filter to maintain the vertical orientation, ensuring the output matches the original aspect ratio rather than forcing a landscape crop.
Can I use relative paths in the sources object?
Yes. The sources mapping accepts both absolute paths and relative paths. The render pipeline resolves these paths relative to the working directory where render.py is executed, though absolute paths are recommended for CI/CD environments to avoid ambiguity.
Why is there a 30-millisecond audio fade applied to every segment?
The 30ms fade-in and fade-out (implemented via afade on lines 88-90 of helpers/render.py) prevents audible clicks and pops that occur when cutting audio at non-zero crossings, ensuring clean transitions between concatenated clips without re-encoding the audio stream.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →