# How the 30ms Audio Fade Rule in render.py Prevents Audible Pops at Segment Boundaries

> Discover how the 30ms audio fade rule in render.py prevents audible pops at segment boundaries. Learn how linear fades smooth discontinuities for seamless audio.

- Repository: [Browser Use/video-use](https://github.com/browser-use/video-use)
- Tags: technical-explanation
- Published: 2026-07-04

---

**The 30ms audio fade rule in [`render.py`](https://github.com/browser-use/video-use/blob/main/render.py) eliminates audible pops by applying linear 30-millisecond fade-in and fade-out filters to every extracted segment, ensuring waveform discontinuities at cut points are smoothed to zero-crossings before lossless concatenation.**

The **browser-use/video-use** repository implements a strict set of render pipeline heuristics designed to produce professional-quality video output. Within [`helpers/render.py`](https://github.com/browser-use/video-use/blob/main/helpers/render.py), Rule 3 mandates that every audio segment undergo a precise 30ms fade-in at the start and 30ms fade-out at the end. This specification prevents the sharp transients that create audible clicks when segments are stitched together.

## Why Abrupt Audio Cuts Cause Pops

When raw audio streams are trimmed without processing, the waveform is often severed mid-cycle. This creates a **discontinuity**—an instantaneous jump in amplitude from a positive or negative value to zero. The human ear perceives this sudden energy shift as a click or pop. Standard lossless concatenation methods preserve these discontinuities, passing the artifacts directly into the final output.

## How the 30ms Fade Rule Works in render.py

### Rule 3: The 30-Millisecond Heuristic

According to the header comments in [`helpers/render.py`](https://github.com/browser-use/video-use/blob/main/helpers/render.py), the third render pipeline heuristic explicitly requires a 30ms fade on every extracted segment. This duration represents a broadcast-standard compromise: long enough to smooth waveform transitions, yet short enough to preserve percussive attacks and conversational pacing.

### Calculating Fade Points in extract_segment

The `extract_segment` function (lines 152-190 in [`helpers/render.py`](https://github.com/browser-use/video-use/blob/main/helpers/render.py)) computes the fade-out start point to ensure exactly 30ms of fade at the end:

```python
fade_out_start = max(0.0, duration - 0.03)      # [render.py#L188]

```

This calculation prevents negative start times on segments shorter than 30ms. The function then constructs an FFmpeg **afade** filter chain:

```python
af = f"afade=t=in:st=0:d=0.03,afade=t=out:st={fade_out_start:.3f}:d=0.03"

# [render.py#L189-L190]

```

The filter applies a linear amplitude ramp from 0 to full volume over the first 30ms, and from full volume back to 0 over the final 30ms.

## Implementation Details: From Filter Chain to Final Output

### Per-Segment Audio Processing

Each segment is rendered as a standalone MP4 with fades baked into the audio stream. The FFmpeg command generated by `extract_segment` applies the fade filter via the `-af` argument:

```bash
ffmpeg -y -ss 12.345 -i source.mp4 -t 4.567 \
       -vf "<video-filters>" \
       -af "afade=t=in:st=0:d=0.03,afade=t=out:st=4.537:d=0.03" \
       -c:v libx264 -preset fast -crf 20 -c:a aac -b:a 192k segment_001.mp4

```

Because the fades are rendered into the compressed audio track, the segment edges always approach zero amplitude, eliminating the DC offset jumps that cause pops.

### Lossless Concatenation of Faded Segments

After individual segments are processed, the `concat_segments` function (lines 667-682 in [`helpers/render.py`](https://github.com/browser-use/video-use/blob/main/helpers/render.py)) assembles them using FFmpeg's **concat demuxer** with stream copying:

```bash
ffmpeg -y -f concat -safe 0 -i _concat.txt -c copy -movflags +faststart final.mp4

```

The `-c copy` flag performs lossless concatenation without re-encoding. Since each segment already contains the 30ms fades at its boundaries, the final video inherits seamless audio transitions without additional processing overhead.

## Code Examples

To render a complete video with the 30ms fade rule applied to all segments:

```bash
python helpers/render.py edl.json -o final.mp4

```

This command processes the Edit Decision List (EDL) through `extract_segment` for each cut, applies the fade filters, and concatenates the results. The output contains no audible pops at segment boundaries, even when source material is cut mid-syllable or mid-beat.

## Summary

- **30ms linear fades** are mandated by Rule 3 in the [`render.py`](https://github.com/browser-use/video-use/blob/main/render.py) pipeline heuristics.
- The `extract_segment` function calculates fade points using `max(0.0, duration - 0.03)` to handle variable segment lengths.
- FFmpeg's **afade** filter creates smooth amplitude ramps that eliminate waveform discontinuities.
- Fades are **baked into segments** before lossless concatenation via `concat_segments`, ensuring zero processing overhead during final assembly.
- The technique prevents audible pops while maintaining audio fidelity and sync across segment boundaries.

## Frequently Asked Questions

### Why is 30ms the standard fade duration?

The 30-millisecond duration represents a standard broadcast engineering practice. It is sufficiently long to smooth perceptible waveform discontinuities (which typically require 10-20ms to settle), yet short enough to avoid the "swell" effect that would soften percussive attacks or speech transients. According to the source code in `browser-use/video-use`, this value is hardcoded as a heuristic constant to ensure consistent output quality across all rendered videos.

### Does applying fades affect the overall audio quality?

No, the 30ms fade improves perceived quality by preventing **click artifacts** that occur at hard cuts. The linear ramp implemented by FFmpeg's `afade` filter operates on the amplitude envelope without introducing frequency distortion or compression artifacts. Since the fade is applied during the initial segment encoding (not during concatenation), it maintains the original codec parameters and bit rate.

### What happens if a segment is shorter than 60ms?

The `max(0.0, duration - 0.03)` calculation in [`render.py`](https://github.com/browser-use/video-use/blob/main/render.py) line 188 ensures the fade-out start never precedes the segment start. For segments shorter than 60ms, the fade-in and fade-out regions overlap. FFmpeg's `afade` filter handles this gracefully by crossfading the overlapping regions, ensuring the audio never clips while still eliminating pops at the segment boundaries.

### Can I modify the fade duration in render.py?

Yes, but it requires editing the source code. The 30ms value is hardcoded in the `af` filter string construction on lines 189-190 of [`helpers/render.py`](https://github.com/browser-use/video-use/blob/main/helpers/render.py). Changing the `0.03` values in the f-string would adjust the fade duration, though this would deviate from the repository's tested heuristics. Any modification should account for the overlap calculation (`duration - 0.03`) to prevent negative start times for short segments.