How to Implement Custom Cut Padding Strategies in video-use: Handling Scribe Timestamp Drift
Implement custom cut padding strategies within the 30–200ms window by modifying EDL timestamps before rendering, clamping values between 30ms and 200ms to absorb ElevenLabs Scribe's 50–100ms timestamp drift while preventing audio pops.
The browser-use/video-use repository enforces strict padding requirements to compensate for word-level timestamp inaccuracies in ElevenLabs Scribe transcripts. When building automated video editing pipelines, you must pad every cut edge by 30–200ms to handle the typical 50–100ms drift while maintaining the hard rules defined in SKILL.md. This guide shows you how to implement a custom padding strategy that respects these architectural constraints without modifying the core rendering engine.
Why the 30–200ms Window Matters
The video-use skill defines a hard rule that every cut edge must be padded to absorb timestamp drift and prevent audio artifacts.
Scribe timestamp drift creates the primary requirement. According to SKILL.md (lines 27–29), the word-level timestamps returned by ElevenLabs Scribe can deviate by approximately 50ms, with extremes reaching 100ms. Hard Rule 7 mandates a working window of 30–200ms to absorb this drift without cutting into adjacent words.
Audio pops enforce the minimum boundary. The per-segment extractor in helpers/render.py (lines 87–90) automatically inserts a 30ms fade-in/out using FFmpeg's afade filter. Without at least 30ms of padding, these fades would clip into the actual content, creating audible clicks at cut points.
Where to Apply Padding in the Pipeline
Cuts are represented in the EDL (Edit Decision List) JSON file, structured as an array of ranges with start and end timestamps. This structure is defined at the end of helpers/render.py (lines 66–75).
You must modify these timestamps before they reach the extract_segment function, which performs the actual FFmpeg extraction. The padding values are added to raw word timestamps, then clamped to the allowed 30–200ms range. This approach keeps the core rendering logic unchanged while allowing flexible pre-processing.
Implementing the Padding Algorithm
Create a helper function that adjusts EDL timestamps while respecting neighbor boundaries. The algorithm must clamp padding to the allowed window, prevent segment overlap, and ensure minimum durations.
from copy import deepcopy
def apply_padding(edl: dict, padding_ms: int = 80) -> dict:
"""
Adjust every cut edge in an EDL by `padding_ms` (default 80ms).
- Guarantees a minimum of 30ms and a maximum of 200ms.
- Never overlaps the neighboring cut (keeps segment order intact).
- Works on both start and end times.
"""
MIN_PAD = 30 # ms
MAX_PAD = 200 # ms
padded = deepcopy(edl)
for i, r in enumerate(padded["ranges"]):
# 1. Compute raw padding (clamp to allowed window)
pad = max(MIN_PAD, min(padding_ms, MAX_PAD)) / 1000.0 # seconds
# 2. Pad start – but never before the previous segment's end
if i > 0:
prev_end = padded["ranges"][i-1]["end"]
r["start"] = max(prev_end, r["start"] - pad)
else:
r["start"] = max(0.0, r["start"] - pad)
# 3. Pad end – but never after the next segment's start
if i < len(padded["ranges"]) - 1:
next_start = padded["ranges"][i+1]["start"]
r["end"] = min(next_start, r["end"] + pad)
else:
# No following segment → just add padding
r["end"] = r["end"] + pad
# 4. Guard against negative duration (fallback to minimum 0.02s)
if r["end"] - r["start"] < 0.02:
r["end"] = r["start"] + 0.02
return padded
Key implementation details:
- Clamp the requested padding to the 30–200ms window using
max(MIN_PAD, min(padding_ms, MAX_PAD)). - Never cross into neighboring segments by comparing against
prev_endandnext_start. - Preserve order by working on a deep copy of the EDL dictionary.
Integrating into the Rendering Pipeline
Wire the padding helper into a pre-processing script that runs before render.py. This keeps the mandatory 30ms audio fade (handled automatically by extract_segment) separate from your custom time-window padding.
# helpers/prepare_edl.py
import json
import argparse
from pathlib import Path
from copy import deepcopy
def apply_padding(edl: dict, padding_ms: int = 80) -> dict:
MIN_PAD = 30
MAX_PAD = 200
padded = deepcopy(edl)
for i, r in enumerate(padded["ranges"]):
pad = max(MIN_PAD, min(padding_ms, MAX_PAD)) / 1000.0
if i > 0:
prev_end = padded["ranges"][i-1]["end"]
r["start"] = max(prev_end, r["start"] - pad)
else:
r["start"] = max(0.0, r["start"] - pad)
if i < len(padded["ranges"]) - 1:
next_start = padded["ranges"][i+1]["start"]
r["end"] = min(next_start, r["end"] + pad)
else:
r["end"] = r["end"] + pad
if r["end"] - r["start"] < 0.02:
r["end"] = r["start"] + 0.02
return padded
def main(edl_path: Path, out_path: Path, pad_ms: int = 80):
edl = json.loads(edl_path.read_text())
padded_edl = apply_padding(edl, padding_ms=pad_ms)
out_path.write_text(json.dumps(padded_edl, indent=2))
print(f"Padded EDL written to {out_path}")
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("edl", type=Path)
ap.add_argument("-o", "--output", type=Path, required=True)
ap.add_argument("--pad-ms", type=int, default=80,
help="Desired padding in milliseconds (30-200)")
args = ap.parse_args()
main(args.edl, args.output, args.pad_ms)
Execute the pipeline in two stages:
python helpers/prepare_edl.py edit/edl.json -o edit/edl_padded.json --pad-ms 80
python helpers/render.py edit/edl_padded.json -o final.mp4
The render step automatically applies the mandatory 30ms audio fade via afade in helpers/render.py, so you only manage the time-window padding in your custom logic.
Testing and Verification
Validate your padding implementation using timeline_view.py to visualize cut points against the waveform:
python helpers/timeline_view.py video.mp4 1.2 2.5 -o verify/cut_01.png --transcript edit/transcripts/video.json
Inspect the generated PNG: the cut line should appear slightly before the first kept word (pre-padding) and slightly after the last kept word (post-padding). The distance between the cut line and the nearest word must fall within the 30–200ms range when measured against the waveform.
Customizing Your Strategy
Adapt the padding values to match your content's pacing requirements:
- Faster-paced cuts (montages, quick dialog): Use
--pad-ms 40(still ≥30ms) to maintain tight timing while respecting the minimum fade requirements. - Cinematic, slower pacing: Use
--pad-ms 150to create more breathing room around cuts, staying well under the 200ms maximum. - Dynamic per-segment padding: Pass a dictionary mapping
{segment_index: padding_ms}toapply_paddingand adjust individual ranges based on speaker-change markers or pause detection in the transcript.
Because the helper works on a copy of the EDL, you can implement bespoke logic—such as inferring padding from transcript confidence scores—without touching the core rendering code in helpers/render.py.
Summary
- Clamp padding values to the 30–200ms window defined in
SKILL.md(lines 27–29) to handle Scribe's 50–100ms timestamp drift. - Modify EDL timestamps before they reach
extract_segmentinhelpers/render.py(lines 87–90), which handles the mandatory 30ms audio fade separately. - Prevent overlap by checking neighboring segment boundaries when adjusting start and end times.
- Verify visually using
timeline_view.pyto ensure cut points maintain proper distance from word boundaries. - Keep core helpers untouched by implementing padding logic in a pre-processing script like
helpers/prepare_edl.py.
Frequently Asked Questions
What happens if I use less than 30ms of padding?
Using less than 30ms violates Hard Rule 7 in SKILL.md and risks audio pops. The extract_segment function in helpers/render.py always applies a 30ms afade filter; without sufficient padding, these fades clip into the actual speech content, creating audible clicks at cut points.
Can I exceed the 200ms maximum padding window?
Exceeding 200ms is not recommended as it may cause visible pauses or overlap with adjacent segments. The 200ms upper bound in SKILL.md represents the maximum safe margin for Scribe drift while maintaining tight editing pacing. The apply_padding function automatically clamps values to 200ms to enforce this constraint.
Why modify the EDL instead of changing render.py directly?
Modifying the EDL before rendering keeps the architecture clean and maintainable. The helpers/render.py file handles low-level FFmpeg extraction with specific audio fade requirements; by pre-processing the EDL, you separate timestamp adjustment logic from media processing logic. This allows the rendering engine to remain unchanged while supporting multiple padding strategies across different projects.
How do I handle the first and last segments differently?
The apply_padding algorithm automatically handles edge cases: for the first segment (i == 0), it clamps the start time to 0.0 instead of a previous segment's end. For the last segment, it adds padding without checking a next segment's start time. You can extend this logic to apply different padding values to opening/closing segments by checking i == 0 or i == len(ranges) - 1 in your custom implementation.
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 →