Home › Tools › TTML / DFXP to SRT Converter

TTML / DFXP to SRT Converter

Turn TTML and DFXP captions (.ttml, .dfxp, .xml) into SubRip (.srt) in your browser — including offset times such as 10s, 100ms and 30f.

Drop a subtitle file here, or

Your file never leaves your device

This tool is plain JavaScript. There is no upload step and no server-side processing, so confidential captions, unreleased content and client material stay on your machine.

What this does

TTML is the format streaming platforms actually deliver. Netflix, Amazon, Disney+ and the BBC all ship captions as TTML or one of its profiles — DFXP and IMSC — which is why a caption file pulled from a broadcaster will not open in VLC and will not hand off cleanly to a video editor. The cues are XML <p> elements with begin and end attributes, and the times inside them can be written four different ways. This converter reads all four and writes SubRip.

How to use it

  1. Paste the .ttml, .dfxp or .xml file into the box, or drop it onto the page.
  2. Every <p> element is read, its begin time and its end or dur time are converted to milliseconds, and inline markup is stripped from the text.
  3. Copy the result or download it as a .srt file.

Good to know

  • Profiles accepted: TTML 1.0, DFXP and IMSC. All three share the same <tt> / <body> / <div> / <p> structure, so one parser covers them.
  • If a cue declares begin but no end, the dur attribute is used instead. A cue with neither is skipped rather than given a guessed duration.
  • <br/> becomes a real line break. Nested <span> elements are unwrapped and their text kept — the tts:fontStyle, tts:color and tts:textDecoration styling on them is dropped, because SubRip has nowhere to put it.
  • Regions, positioning and background images (smpte:backgroundImage) do not survive. TTML can place a caption anywhere on the frame; SubRip cannot.
  • The frame rate is read from the header only, not from per-cue overrides. Files that switch frame rate mid-stream are vanishingly rare, but they would need splitting by hand.

Four ways TTML writes a timestamp

Clock time with milliseconds — 00:01:02.350. This is the common case and the one closest to SubRip. A comma separator (00:01:02,350) is accepted as well, because some exporters emit it by mistake.

Clock time with frames — 00:01:02:11. The fourth field is frames, not milliseconds, so it only means something once it has been divided by the frame rate. That rate comes from ttp:frameRate on the root <tt> element; when the file does not declare one, 30 fps is assumed. A file authored at 25 fps and read as if it were 30 fps drifts by roughly 17 percent, which is the single most common reason TTML-to-SRT timing is right at the start of a file and wrong at the end.

Offset times — 10s, 100ms, 1.5m, 3h. These are durations rather than points on a clock, and TTML permits them in begin as well as in dur. The converter understands h, m, s and ms.

Frame and tick offsets — 30f and 100t. Frames are divided by the frame rate, ticks by the rate declared as ttp:tickRate. Both turn up in broadcast files where timing is authored on a frame grid rather than in wall-clock seconds.

Entities are decoded. Because the source is XML, an ampersand arrives as &amp; and a quote as &quot;. Named and numeric entities are both decoded before the text reaches SubRip, so what you see in the output is what the caption was meant to say.

Worked example

Input — .ttml
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:frameRate="25">
  <body><div>
    <p begin="00:00:01.000" end="00:00:04.000">Hello there.</p>
    <p begin="00:00:04.200" dur="2.8s"><span tts:fontStyle="italic">Music playing</span><br/>Sign on wall: EXIT</p>
  </div></body>
</tt>
Output — .srt
1
00:00:01,000 --> 00:00:04,000
Hello there.

2
00:00:04,200 --> 00:00:07,000
Music playing
Sign on wall: EXIT

The second cue had no end attribute — its dur of 2.8s was added to the begin time. The span was unwrapped and the br became a real line break.

FAQ

Why does the timing drift towards the end of the file?
Almost always the frame rate. If the file uses hh:mm:ss:ff timestamps and declares no ttp:frameRate, the converter assumes 30 fps. A file authored at 25 fps then accumulates error in proportion to the timecode — small at the start, obvious by the end. Open the file, read the rate from the root element, and if it is not 30 you will want to re-time the result with the resync tool.
Can I convert the other way, from SRT to TTML?
Not here. SRT to TTML is a rarer direction, and the parts TTML is usually chosen for — regions, positioning, named styles — have to be authored deliberately. Producing a bare TTML wrapper with no styling would not save you the work.
The output is empty even though the file opens in a player
The parser looks for <p> elements carrying a begin attribute. Files that put the timing on <span> children, or that store cues in a non-standard element, have nothing to read. Check the first few lines of the body section.
Does this upload my caption file anywhere?
No. The file is parsed in JavaScript on your device and never leaves it. You can load the page, disconnect from the network, and the converter still works.
Why did my italics disappear?
SubRip carries only <i>, <b> and <u>, and TTML expresses italics as a tts:fontStyle attribute on a <span> or through a named style in a <style> block. Inline spans are unwrapped rather than translated, so the words survive and the emphasis does not.

Troubleshooting

  1. No cues found at allThe file needs <p> elements that carry a begin attribute. A TTML file whose cues were emptied out, or a file that is really a plain XML config with a .ttml extension, will produce nothing. Open it in a text editor and look for the word begin.
  2. Every cue is a few seconds off, and the error growsA frame-rate mismatch. Times written as hh:mm:ss:ff were divided by the wrong rate. Note the ttp:frameRate value in the file header and re-time the output — the ratio presets on the resync tool cover the common 23.976 / 24 / 25 / 30 combinations.
  3. The text contains stray ampersands or question marksThe source used an entity the decoder does not know, or the file is not UTF-8. Re-save it as UTF-8 from the editor that produced it. Unrecognised named entities are left as written rather than guessed at.
  4. Two languages came out in one cueThat is how the file was authored — one <p> containing both languages, usually separated by a <br/>. Split them into separate files if you need one language per track; the converter preserves the text exactly as written.
  5. The player shows the subtitles but they look plainExpected. SubRip has no font, colour or positioning fields, so all of that is discarded on the way through. Keep the original TTML as the master if appearance matters, and treat the SRT as a delivery or review copy.

Related tools

Last updated 2026-09-21. All conversions happen locally in your browser.