Sync & Drift Guide Subtitles & Scripts

Diagnosing and Fixing Constant Offset vs. Frame-Rate Drift in Audio and Subtitles

How to distinguish fixed millisecond latency from variable frame rate (VFR) and 23.976 vs. 24.000 fps clock drift, and repair both without losing quality.

When dialogue or subtitles fall out of sync with picture, editors often waste time nudging clips by hand without identifying whether the root cause is a static latency offset, a fractional NTSC clock mismatch, or a Variable Frame Rate (VFR) recording from a smartphone or screen-capture utility.

Step 1: Measure Error at Two Timeline Points

Locate a sharp transient near the start of the recording (such as a clapperboard slate or a hard consonant plosive like "P" or "B") and measure the sync discrepancy \(\Delta t_1\). Then locate a transient near the end of the recording and measure \(\Delta t_2\):

  • If \(\Delta t_1 = \Delta t_2\): You have a constant offset (for example, wireless microphone ADC delay or a trimmed intro slate). Apply a single positive or negative millisecond shift using -itsoffset in FFmpeg or the Subtitle Timestamp Shifter.
  • If \(\Delta t_2\) grows linearly with time: You have clock rate drift between a 23.976 fps (\(\frac{24000}{1001}\)) stream and a true 24.000 fps or 25.000 fps stream, or a 44.1 kHz vs. 48 kHz sample-rate header mismatch.
  • If sync fluctuates randomly after pauses: Your source footage uses Variable Frame Rate (VFR), where frame intervals expand during static desktop or phone camera scenes.

Step 2: Converting VFR Footage to Constant Frame Rate (CFR)

Non-linear editors and subtitle timers assume every frame has an identical duration. Before editing screen recordings or mobile footage, transcode VFR sources to strict Constant Frame Rate using FFmpeg:

ffmpeg -i vfr_recording.mp4 -vf "fps=fps=30" -vsync cfr -c:v libx264 -crf 18 -c:a copy cfr_fixed.mp4

Step 3: Correcting 1000/1001 NTSC Pull-Down Drift

A 1-hour recording captured at true 24.000 fps played back on a 23.976 fps timeline accumulates \(3600 \times 0.001 = 3.6\text{ seconds}\) of drift. For audio tracks, use the atempo or asetrate filter; for subtitle tracks, multiply every timestamp by \(1.001\) or \(0.999\) in the Subtitle Timestamp Shifter.