Add subtitles to HLS streams
Adaptive streams without captions fail accessibility requirements and lose viewers. “Lectern” (an online education platform like Coursera) needs every lecture to ship with subtitle tracks so students can follow along in noisy environments or different languages.
API
ittybit adaptive \
-i https://lectern-app.com/courses/bio-201/lecture-4.mov \
--format hls \
--subtitle https://lectern-app.com/courses/bio-201/lecture-4.vtt \
--subtitle-label Englishconst task = {
input: 'https://lectern-app.com/courses/bio-201/lecture-4.mov',
kind: 'adaptive_video',
options: {
format: 'hls',
subtitles: [
{
url: 'https://lectern-app.com/courses/bio-201/lecture-4.vtt',
label: 'English',
language: 'en',
default: true,
},
],
},
};
const res = await fetch('https://api.ittybit.com/jobs', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(task),
});
const data = await res.json();import requests
task = {
"input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
"kind": "adaptive_video",
"options": {
"format": "hls",
"subtitles": [
{
"url": "https://lectern-app.com/courses/bio-201/lecture-4.vtt",
"label": "English",
"language": "en",
"default": True,
},
],
},
}
res = requests.post(
"https://api.ittybit.com/jobs",
headers={"Authorization": f"Bearer {api_key}"},
json=task,
)
data = res.json()TASK='{
"input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
"kind": "adaptive_video",
"options": {
"format": "hls",
"subtitles": [
{
"url": "https://lectern-app.com/courses/bio-201/lecture-4.vtt",
"label": "English",
"language": "en",
"default": true
}
]
}
}'
curl -X POST https://api.ittybit.com/jobs \
-H "Authorization: Bearer $ITTYBIT_API_KEY" \
-H "Content-Type: application/json" \
-d "$TASK" The HLS manifest references the VTT file as a subtitle track. Players that support HLS (Safari, hls.js, Video.js) show a captions toggle automatically.
CLI
ittybit adaptive \
-i lecture-4.mov \
-o lecture-4.m3u8 \
--subtitle lecture-4.vtt \
--subtitle-label English \
--subtitle-language en
Multiple languages
Add several subtitle tracks to the same stream:
{
"input": "https://lectern-app.com/courses/bio-201/lecture-4.mov",
"kind": "adaptive_video",
"options": {
"format": "hls",
"subtitles": [
{
"url": "https://lectern-app.com/captions/lecture-4-en.vtt",
"label": "English",
"language": "en",
"default": true
},
{
"url": "https://lectern-app.com/captions/lecture-4-es.vtt",
"label": "Espa\u00f1ol",
"language": "es"
},
{
"url": "https://lectern-app.com/captions/lecture-4-fr.vtt",
"label": "Fran\u00e7ais",
"language": "fr"
}
]
}
}
Each track appears as a selectable option in the player’s caption menu.
VTT format requirements
| Requirement | Detail |
|---|---|
| Format | WebVTT (.vtt) only |
| Encoding | UTF-8 |
| Timestamps | Must align with the source video timeline |
| Hosted | The URL must be publicly accessible at task time |
When to use subtitle tracks vs burned-in captions
| Approach | Use case |
|---|---|
| Subtitle tracks (this guide) | Multi-language, togglable, accessible |
| Burned-in (hardcoded) | Social media clips, guaranteed visibility |
Subtitle tracks keep the text separate from the video, so viewers can toggle them on or off and switch languages without re-encoding.