Out-of-Sync Subtitles: How to Measure the Offset and Shift an SRT by an Exact Number of Seconds
Published 8/5/2026 · 15 min read · Developer tools
Daniel Okonkwo — Front-end developer and tech writer at OneKitly
Web performance · File formats
Checked against 4 sources
Measure the offset before you shift anything. Find one unmistakable line of dialogue near the start, note the exact time it is spoken in the player, subtract the cue's start stamp, and you have the offset in seconds — then repeat the measurement near the end of the film. If the two figures agree, the track carries a constant offset and one shift fixes the whole file: paste the SRT in, enter the offset in seconds (positive delays the subtitles, negative moves them earlier) and take the corrected file. If the second figure is larger than the first, it is not an offset at all, it is drift, and no shift will fix it. Drift almost always means a frame-rate mismatch. A track authored against a 25 fps master and played on a 23.976 fps transfer — 23.976 being 24000/1001 exactly — needs its timestamps multiplied by 1.042708, not moved: the error grows 2.5625 seconds for every minute of running time, which is one second every 23.4 seconds and 3 minutes 50.6 seconds across a 90-minute subtitle timeline. The gentler pair, 24 against 23.976 or 30 against 29.97, is exactly 1.001 — only 5.4 seconds over the same 90 minutes, which is why it gets misdiagnosed as a constant offset for the first half hour. One behaviour to know before you enter a negative value: a cue the shift pushes entirely before zero is removed, not flattened, and an amber line above the output counts how many went. A cue straddling zero keeps its real end and starts at 00:00:00,000. That also makes a large negative shift a workable way to trim an intro — the cues inside it go, everything after keeps its spacing, and you are told what was lost.
Measure the offset at two points instead of guessing it once. If it is constant, one shift fixes the file. If it grows, it is a frame-rate mismatch — 25 fps against 23.976 drifts 2.5625 seconds every minute — and no shift will ever fix that.
Measure the offset, do not eyeball it
Pick a line of dialogue you cannot mistake — a name, a number, a shout, anything with a hard consonant at the start. Play the film, pause on the frame where the sound begins, and read the player's clock. Then find that same line in the subtitle file and read its start stamp. The offset is the video time minus the cue time. If the video says 00:04:12.500 and the cue says 00:04:09,000, the subtitles are running 3.5 seconds early and the offset you need is +3.5 seconds.
Now do it a second time, as close to the end of the film as you can find a clean line. This second measurement is the whole diagnosis, and skipping it is the reason people shift the same file four times and never fix it. Two numbers that agree within a few tenths mean a constant offset: one shift, done. Two numbers that differ by minutes mean the file is not offset at all, it is running at the wrong speed, and every shift you apply will fix one end of the film while making the other end worse.
One practical note on reading the clock: most players show the position to a tenth of a second, and a tenth is well below what an audience notices, so there is no need for frame accuracy here. What you do need is honesty about which instant you measured. Measure the instant the sound starts, not the instant you notice it, and measure the same kind of instant both times — otherwise your two readings differ by your own reaction time and you invent a drift that is not there.
When the offset grows: the frame-rate arithmetic
The same film exists in two families of running speed, and the subtitle file remembers which one it was written against. The film family runs at 24 frames per second, or at 24000/1001 — 23.976 — on any transfer that had to live inside the American colour-television timing. The broadcast family of most of Europe runs at 25. Those are not decorative labels: ffmpeg's own rate abbreviations spell them out as exact fractions, film = 24/1, pal = 25/1, ntsc-film = 24000/1001, ntsc = 30000/1001. A subtitle file made for one and played on the other does not sit in the wrong place, it moves at the wrong speed.
The correction is a multiplication, not an addition. Timestamps written for a 25 fps version, played on a 23.976 fps version, have to be multiplied by 25 ÷ (24000/1001) = 25025/24000 = 1.042708. Read that as a rate of error and it becomes something you can recognise on sight: 2.5625 seconds of drift for every minute of running time, one full second every 23.4 seconds. Across a 90-minute subtitle timeline it accumulates to 230.6 seconds — 3 minutes 50.6 seconds. Turned round, a line that belongs at the 90-minute mark of the 23.976 transfer carries a 25 fps stamp of 86 minutes 18.8 seconds and appears 3 minutes 41.2 seconds early.
The dangerous pair is the small one. Going between 24 and 23.976, or between 30 and 29.97, is a ratio of exactly 1.001, which over the same 90 minutes is 5.4 seconds. For the first twenty minutes that is invisible; by the last reel it is a full sentence out of place. People shift such a file by two seconds, watch the opening happily, and hit the same problem an hour later — because it was never an offset. Two measurements taken an hour apart tell you which of the two you have in about ninety seconds of work.
This tool shifts and does not rescale, and that is worth saying plainly rather than leaving you to discover it. If your two measurements disagree, the file needs every timestamp multiplied, which means a tool that does frame-rate conversion or a short script — and the multiplier is the ratio of your two measured positions, not a value you have to guess. Divide the true video time of the late line by the true video time of the early line, and do the same for the two cue stamps; the ratio of those two ratios is what has to be applied.
What a negative shift actually does here
A negative shift is the common case — subtitles that arrive late need moving earlier — and it is the one place where a subtitle tool has to make a decision that the format does not make for it. A cue at 00:00:01,000 shifted by −5 seconds would start at −4 seconds. SubRip has no way to write that. Three answers are possible: write a negative stamp anyway and let the player choke, drop the cue, or clamp it at zero. This tool picks between the last two, cue by cue, according to what is left after the origin.
The test is the cue's end stamp, which produces two different outcomes. A cue that straddles zero is truncated: at −2.5 seconds, 00:00:01,000 --> 00:00:04,000 comes back as 00:00:00,000 --> 00:00:01,500. It keeps its real end, so it now appears at the very first frame and lasts a second and a half less than it should — visible, but not fatal. A cue pushed entirely before zero is a different matter. At −5 seconds the same cue would end at −1 second: it has nothing left to show, and it is removed from the file. The cues that survive are renumbered from 1, so what comes back is a shorter file rather than a padded one.
Push the whole file back far enough and the cues inside that stretch simply go. A −20 second shift applied to a file whose first three cues sit inside the first twelve seconds returns nothing at all, and an amber line above the output says that every cue now falls before zero. Short of that extreme, the same line carries two counts: how many cues were removed, and how many started before zero and now start at the origin. What the tool will no longer do is lose subtitles without saying so — read the counts before you save over anything, and compare them with what you expected to lose.
That also makes a large negative shift a real trimming device. Cut a thirty-second intro off the front of a video, shift the subtitles by −30 seconds, and the cues that belonged to the intro are removed, the one straddling the cut starts at the first frame with its real end, and everything after keeps its spacing. The count in the banner is the check: if it says four cues removed and you meant to lose two, your offset was longer than the intro.
What comes back out, and what it quietly changes
The output is always SubRip, whatever went in. Cues are renumbered from 1 in file order, timestamps are written as HH:MM:SS,mmm with a comma before the milliseconds, and everything between the timestamp line and the blank line is passed through untouched — including any italic tags or speaker dashes you had. That last point matters: this is a retimer, not a cleaner, and it will not alter your text in any way.
It will also read a WebVTT file, and this is worth knowing because the result is not what an unwary user expects. Paste a .vtt in and the tool finds the cues, throws away the WEBVTT header line, throws away the cue identifiers and any cue settings sitting after the arrow, converts the decimal point to a comma, and hands you SubRip. The times shift correctly. But the file that comes back is no longer valid WebVTT — the header is a hard requirement of the specification, and the positioning settings you may have relied on are gone. If you wanted a shifted .vtt, this is not the way to get one.
Two smaller facts about the edges, both found by feeding the tool its own output. The timestamp reader accepts up to three digits for the hours field, so a stamp like 111:06:41,000 is read back without trouble; the ceiling is 999:59:59,999, beyond which the tool would write hours it can no longer read — a wall no film will ever reach. And the offset is rounded to whole milliseconds at output, so a value of 0.0005 seconds becomes a one-millisecond move rather than nothing. Neither will trouble a film, but both are worth knowing if you are chaining operations.
| Mismatch | Multiplier to apply | Drift per minute | Drift over 90 minutes | How it looks while watching |
|---|---|---|---|---|
| Constant offset (any cause) | 1.000000 — shift instead | 0 s | 0 s | Equally wrong at minute 1 and minute 89 — one shift fixes it |
| 24 fps subtitles on 23.976 fps video (or 30 on 29.97) | 1.001000 | 0.060 s | 5.4 s | Invisible for twenty minutes, a sentence out of place by the end — the one most often misdiagnosed |
| 25 fps subtitles on 24 fps video | 1.041667 | 2.500 s | 3 min 45.0 s | Subtitles run ahead and the gap widens visibly within the first reel |
| 25 fps subtitles on 23.976 fps video | 1.042708 | 2.563 s | 3 min 50.6 s | The classic PAL-to-film mismatch: one second lost every 23.4 seconds |
| 23.976 fps subtitles on 25 fps video | 0.959041 | −2.458 s | −3 min 41.2 s | The same mismatch the other way round: subtitles fall further and further behind |
Frequently asked questions
- How do I measure the offset accurately without frame-stepping the whole film?
- Two measurements are enough, and you only need tenth-of-a-second accuracy. Find a line with a hard consonant onset near the start of the film, pause on the frame the sound begins, and read the player's position; subtract the cue's start stamp from it. Then do the same for a line near the end. If the two offsets agree, apply the average of the two and you are finished. Consistency of method matters more than precision here: always measure the same instant — the beginning of the sound, not the moment you recognise it — because a reaction-time error of half a second in one of the two measurements is enough to invent a drift that does not exist. If your player shows only whole seconds, use one that shows tenths; the difference between a 3-second and a 3.5-second offset is visible on screen.
- My subtitles are perfect at the start and about four minutes early at the end. What is that?
- That is a frame-rate mismatch, and the size of it identifies which one. Roughly four minutes of drift over a feature-length film is the signature of 25 fps subtitles on a 23.976 fps video: the multiplier is 1.042708, the drift is 2.5625 seconds per minute, and across a 90-minute subtitle timeline it totals 230.6 seconds — 3 minutes 50.6 seconds, or 3 minutes 41.2 seconds if you measure it against the video's own clock instead. A shift cannot repair it, because a shift adds the same number to every timestamp while your error is proportional to the timestamp. What you need is a multiplication of every stamp by 1.042708, which is frame-rate conversion, not time shifting. This tool does not do that, and applying a shift will simply move the point at which the subtitles happen to be correct from one end of the film to the other.
- What happens to cues that a negative shift pushes before 00:00:00,000?
- They are removed, and the tool says how many. The decision is made on the end stamp, so there are two outcomes. A cue straddling zero keeps its real end: shifting 00:00:01,000 --> 00:00:04,000 by −2.5 seconds returns 00:00:00,000 --> 00:00:01,500, which shows from the first frame and is a second and a half short. A cue whose end has also gone past zero has nothing left to show and is dropped; the survivors are renumbered from 1, so the output is a shorter valid file rather than one padded with empty entries. An amber line above the output carries the counts — how many cues were removed, how many now start at the origin — and if the shift wipes out everything it says so instead of handing you a file. Negative times are never written. In practice this makes a large negative shift a usable way to trim an intro: compare the removed count with the number of cues you meant to lose before you save over anything.
- Can I paste a WebVTT file in, or does it only take SubRip?
- It reads WebVTT, but it gives you SubRip back, and that conversion is not free. WebVTT is a W3C specification: the file must open with a WEBVTT line, timestamps use a full stop before the three-digit fraction rather than a comma, the hours field is optional, and a cue timings line may carry a settings list after the arrow — alignment, position, region — as well as an optional identifier on the line above. This tool finds the cue timings, shifts them, and writes SubRip: the header is gone, the identifiers are gone, the cue settings are gone, and the full stop becomes a comma. If your source was styled or positioned WebVTT, all of that is discarded silently. If you actually want plain text out of a caption file rather than a retimed one, the dedicated VTT to TXT converter is the honest route.
- Is there an official SubRip specification I can check my file against?
- No, and it is worth knowing that before you argue with a tool about what is valid. The format is whatever the original SubRip program produced, plus whatever players later chose to tolerate. The Library of Congress records it as only partly documented and not well standardised, and lists no specification document at all; its format description gives the timecode shape as hours:minutes:seconds,milliseconds, with the comma, and the arrow as two hyphens and a right angle bracket. There is no registered media type for it either, where WebVTT has text/vtt registered with IANA. In practice this means encoding is a guess — the original program defaulted to a Windows code page, so any parser has to attempt character-set detection — and it means that when two tools disagree about your file, neither of them is wrong by reference to a standard. Test in the player you will actually use.
Articles you may find interesting
All guides →Related tools
SubRip has no specification. The Library of Congress records it as only partly documented and not well standardised, so what counts as a valid .srt file is decided by the player, the encoder or the platform that reads it, and two of them will disagree sooner or later. Everything described here was checked against the tool's actual output, not against a standard, because for this format there is none. WebVTT is different — it has a published W3C specification — but it is still a Candidate Recommendation Draft rather than a finished Recommendation. Keep the original file, run the converted one in the player or uploader you actually intend to use before you throw anything away, and treat any claim about what "all players" do, including the ones on this page, as something to verify on yours.
Sources
- Library of Congress — Sustainability of Digital Formats, format description FDD000569, SubRip Subtitle format (SRT): records the format as "Partial documented" and "not well standardized", lists no specification document, gives the timecode as hours:minutes:seconds,milliseconds (00:00:00,000) and the separator as two hyphens plus a right angle bracket, and notes that SRT has no character-encoding standard so a parser must attempt charset detection
- W3C — WebVTT: The Web Video Text Tracks Format, Candidate Recommendation Draft of 20 May 2026 — the file must begin with the string WEBVTT after an optional byte order mark, the timestamp fraction is separated by a U+002E FULL STOP and carries three digits, the hours component is optional and takes two or more digits, and a cue timings line may be followed by a cue settings list
- FFmpeg — ffmpeg-utils documentation, "Video rate" section: the exact rational values behind the named rates — film = 24/1, pal = 25/1, ntsc-film = 24000/1001, ntsc = 30000/1001 — which are the numbers the drift arithmetic in this article is computed from
- IANA — Media type registration for text/vtt, registered 14 October 2019 with the W3C WebVTT specification as its reference — the contrast with SubRip, which has no registered media type
Spotted a mistake in this article?