Rainbow Captions Blog Download

.ytt format:a working reference

A reference for YouTube's timed-text format — the XML behind styled captions, saved as .ytt and known inside YouTube as format 3 or SRV3. Everything below is what the format accepts in practice, including the values that look reasonable and get silently dropped.

The shape of a file

<timedtext format="3">
  <head>
    <pen id="1" b="1" fc="#FEFEFE" fo="254" et="4" ec="#000000"/>
    <wp id="0" ap="7" ah="50" av="90"/>
    <ws id="0" ju="2"/>
  </head>
  <body>
    <p t="1000" d="1600" wp="0" ws="0" p="1">Today we fix that.</p>
  </body>
</timedtext>

The head declares reusable styles: pen for text, wp for where the caption window sits, ws for how the text is aligned inside it. The body is the list of caption lines, each pointing at the styles it uses by id.

<pen> — the text style

AttributeValuesMeaning
fs0–7Font: 0 default sans, 1 Courier New, 2 Times New Roman, 3 Lucida Console, 5 Comic Sans MS, 6 Monotype Corsiva, 7 Carrois Gothic SC. 4 is undefined.
szintegerSize. 100 is YouTube's default; the scale is non-linear (see below).
b, i, u"1"Bold, italic, underline. Written only when true.
fc#RRGGBBText colour. Pure #FFFFFF is commonly written as #FEFEFE.
fo0–254Text opacity. 255 is stripped — fully opaque is 254.
bc, bo#RRGGBB, 0–254Background box colour and its opacity. bo="0" removes the box.
et1–4Edge: 1 hard shadow, 2 bevel, 3 glow, 4 soft shadow. There is no outline type — a dark glow is used as one.
ec#RRGGBBEdge colour. Defaults to #222222 at the text's opacity.
of0–2Offset: 0 subscript, 1 normal, 2 superscript.
rb0–5Ruby annotation (furigana): 0 none, 1 base, 2 parenthesis, 4 text before, 5 text after.

The size formula

The value in sz is not the size you mean. It moves four times faster than the real scale:

sz    = (1 + (scale − 1) × 4) × 100
scale = 1 + (sz / 100 − 1) / 4

So sz="100" is 100%, sz="200" is 125%, and sz="50" is about 87%. YouTube's default caption size is roughly 4.6–4.8% of the frame height, which is the size sz="100" renders at.

<wp> — the window position

AttributeValuesMeaning
ap0–8Anchor point: 0 top-left, 1 top-centre, 2 top-right, 3 middle-left, 4 centre, 5 middle-right, 6 bottom-left, 7 bottom-centre, 8 bottom-right.
ah0–100Horizontal position.
av0–100Vertical position.

Positions are not raw percentages of the frame. YouTube maps them into a safe area:

screen% = value × 0.96 + 2

av="0" lands at 2% from the top and av="100" at 98%. A converter going the other way uses value = (screen% − 2) / 0.96.

<ws> — the window style

AttributeValuesMeaning
ju0–2Justification: 0 left, 1 right, 2 centre.
pd0–3Print direction; 0 is horizontal left-to-right.
sd0–1Scroll direction, for vertical text.

<p> and <s> — lines and segments

AttributeValuesMeaning
p.t≥ 1 msStart time. Some Android players ignore a line that starts at 0.
p.d> 0 msDuration.
p.wp, p.wsidWindow position and window style to use.
p.pidPen for the whole line.
s.pidPen for one segment inside the line.
s.t≥ 0 msSegment start, relative to the line — this is how word-by-word timing works.

The quirks that cost an afternoon

Where this information comes from

The format is not documented publicly by YouTube. The reference behaviour comes from YTSubConverter, the open-source converter whose source is the de-facto specification, checked against real caption tracks downloaded from YouTube and against what the players actually render. Rainbow Captions writes the same format — you can build tracks by hand from the tables above, or let the app handle the layering, the guards and the per-word timing.

Let the app write the XML

Rainbow Captions handles the pens, the layered outlines, the guard characters and the per-word timing, in 170 languages. Free to download, first 10 languages free.

Download for Mac