How to hardcode vtt or srt subtitles into a video
- Step 1Load your video — Drop or browse to the file. Accepted containers: MP4, MOV, MKV, WebM, AVI, M4V, TS. The burner reads the video, an
.srt/.vtt, and produces an MP4 — it does not need the source to already contain any subtitle stream. - Step 2Pick the SRT or VTT to hardcode — In the Subtitle file panel, click Pick and choose your caption file. Names ending
.vttare parsed as WebVTT; all other extensions are parsed as SRT. The file is processed locally in the in-browser FFmpeg filesystem. - Step 3Choose a style preset — Set Style preset to one of default, tiktok, youtube, subtle, or bold. default applies no override (FFmpeg's plain white); the others apply a fixed ASS
force_style(font, size, colour, outline, shadow, bottom margin) to every cue. - Step 4Run the hardcode — Click Run Burn Subs. FFmpeg burns the cues into each frame via the
subtitlesfilter, re-encodes the video with libx264 (-preset medium -crf 20), and copies the audio stream. Progress appears in the render dashboard. - Step 5Download the MP4 — The output is always an MP4 (H.264 video, copied audio) with the captions permanently part of the picture. Download from the result card.
- Step 6Verify across players — Open the file in two different players (e.g. your OS player and a browser
<video>tag). The captions should be identical and unremovable in both — that's the proof the subtitles are truly hardcoded, not soft.
Soft subtitles vs hardcoded captions
Why hardcoding is the reliable choice when you don't control the player. This tool produces the right-hand column.
| Aspect | Soft subtitles (sidecar / muxed) | Hardcoded (this tool) |
|---|---|---|
| Player support | Depends on player; many web/TV players ignore them | Universal — part of the frame |
| Survives re-encode/upload | Often stripped by social platforms | Always — it's pixels |
| Toggle on/off | Yes (if the player supports it) | No — permanent |
| Extra file to ship | Yes (sidecar) or relies on mux | No — single self-contained MP4 |
| Editable later | Edit the SRT, no re-encode | Re-burn from the SRT (re-encode) |
| Styling | Player-dependent | Baked from your chosen preset |
Format detection, encode, and limits
Verified against the processor and tier-limits config.
| Property | Value |
|---|---|
| Subtitle format detection | Extension .vtt → WebVTT; otherwise → SRT |
| Video input | MP4, MOV, MKV, WebM, AVI, M4V, TS |
| Output | MP4 · H.264 (libx264, CRF 20, preset medium) |
| Audio | Copied (-c:a copy) — not re-encoded |
| Styling controls | 5 presets (default applies no override) |
| Free / Pro / Pro + Media | 1 GB / 10 GB / 100 GB per file; 1 / 5 / 50 files |
Cookbook
Hardcoding recipes covering both SRT and VTT, the default vs styled presets, and the limits of what this tool can and can't change.
Hardcode an SRT with the default preset
The cleanest hardsub: no styling override, FFmpeg renders the SRT in its plain white default. Best when the captions should be unobtrusive and universal.
Options: Subtitle file = movie.srt Style preset = DEFAULT FFmpeg (effective): -i movie.mp4 -vf subtitles=sub.srt -c:v libx264 -preset medium -crf 20 -c:a copy out.mp4 Result: plain white captions baked in, audio untouched.
Hardcode a WebVTT file
VTT is detected by extension and fed to the same subtitles filter — no manual conversion to SRT needed.
Options: Subtitle file = lecture.vtt (detected as WebVTT) Style preset = YOUTUBE FFmpeg force_style (youtube): FontName=Roboto,FontSize=22,PrimaryColour=&H00FFFFFF, BackColour=&H80000000,BorderStyle=4,Alignment=2,MarginV=40 Result: caption-strip box style burned into the video.
Self-contained deliverable for a client
When a client may open the file in any random player, hardcoding guarantees they see captions without needing the sidecar SRT.
Goal: one MP4 that always shows captions. 1. Load deliverable.mov 2. Pick deliverable.srt 3. Preset = DEFAULT (neutral) 4. Run -> deliverable-out.mp4 Ship the single MP4; no sidecar file required.
Mislabelled subtitle file fails the burn
Format is decided by extension, not content. A WebVTT renamed to .srt (or vice versa) can confuse the parser and fail FFmpeg.
Problem: captions.srt actually contains WebVTT (WEBVTT header) -> subtitles filter errors -> 'Subtitle burn failed: ...' Fix: Match extension to content: WebVTT -> .vtt, SubRip -> .srt
Edit captions and re-hardcode
Hardsubs are permanent, but the SRT is plain text — to change wording or timing, edit the SRT and re-run; the video is only re-encoded on the new run.
1. Edit movie.srt in a text editor 2. Re-pick movie.srt, keep preset 3. Run -> fresh out.mp4 with updated captions The previous output is unaffected; you get a new file.
Edge cases and what actually happens
Source already has a soft subtitle track
Ignored — uses your fileThe burner does not read a subtitle stream embedded in the source video; it burns the SRT/VTT you explicitly pick. To hardcode an existing embedded track, first pull it out as SRT with subtitle-extractor, then bring that file here.
Subtitle file extension doesn't match its content
Burn failsFormat is chosen by extension (.vtt → WebVTT, else SRT). A WebVTT body saved as .srt (or the reverse) makes FFmpeg's subtitles filter error out with a Subtitle burn failed message. Keep extension and content consistent.
Empty subtitle slot
Error · blockedRunning without a caption file throws Pick an .srt or .vtt file. Both a video and a subtitle file are required.
Cues use ASS styling tags inside the SRT
Preset-dependentOn the default preset the subtitles filter honours inline styling the cue carries; on a styled preset the force_style override is applied uniformly to every cue, which can override per-cue intent. Pick default if you want the SRT's own styling respected.
Non-Latin glyphs and the preset font
Font-dependenttiktok/subtle use Arial, bold uses Impact, youtube uses Roboto. Glyphs absent from the chosen font render as boxes. For broad script coverage, default leans on FFmpeg's fontconfig fallback — verify the output before delivering.
Video has no audio
Preserved-c:a copy simply has nothing to copy; the hardcode still succeeds and outputs a video-only MP4.
File larger than the tier cap
Size capFree allows 1 GB per file, Pro 10 GB, Pro + Media 100 GB (streamed). There is no duration limit — only file size and batch count. A long high-bitrate master may need a higher tier.
Output is always MP4 even from an MKV source
By designRegardless of input container, the hardcoded result is muxed to MP4 with H.264 video and copied audio. If you need a different container afterwards, run the MP4 through video-transcoder.
Re-encode is unavoidable for hardsubs
Expected — CRF 20You cannot paint text into frames without re-drawing them, so a re-encode is intrinsic. CRF 20 keeps it visually lossless; the audio copy means only the video stream pays the cost.
Frequently asked questions
What does 'hardcode subtitles' actually mean?
It means burning the captions into the video frame so they become part of the picture — also called hard-subbing or burning. Unlike soft subtitles (a sidecar file or a removable track), hardcoded captions can't be toggled off and show on every player, because they're pixels.
Does it accept both SRT and VTT?
Yes. The tool reads files ending in .vtt as WebVTT and everything else as SRT, then feeds either to FFmpeg's subtitles filter. Keep the correct extension so the right parser is used.
Will the captions be permanent?
Yes — that's the point of hardcoding. They're rendered into the frame and cannot be removed by the viewer or stripped by a player. To change them you edit the source SRT and re-burn, which produces a new file.
Is my audio re-encoded?
No. Audio is stream-copied with -c:a copy, so it stays bit-for-bit identical. Only the video is re-encoded (libx264, CRF 20) to paint in the captions.
What output format do I get?
Always an MP4 with H.264 video and copied audio, regardless of the input container. If you need MKV or WebM afterwards, transcode the MP4 with the video-transcoder.
Can I keep the styling already in my SRT?
Use the default preset, which applies no force_style override — FFmpeg then renders the cues with their own/default styling. The other presets impose a uniform look on every cue.
Is anything uploaded?
No. The whole pipeline runs in your browser through FFmpeg.wasm. The video and subtitle file never leave your machine; the burned MP4 is handed straight back to you.
Why did my burn fail with a subtitles error?
The most common cause is a subtitle file whose extension doesn't match its content (a WebVTT saved as .srt, or a malformed SRT). Match the extension to the format and ensure the file is valid SubRip/WebVTT, then re-run.
How large a file can I hardcode?
Free tier: 1 GB per file, one file. Pro: 10 GB. Pro + Media: 100 GB with streaming. There's no length limit — only file size and batch count.
Can I burn subtitles into an MKV and keep it as MKV?
The burn always outputs MP4. To end up with MKV, hardcode here, then convert the MP4 with video-transcoder, which supports MP4/MOV/MKV/WebM/AVI containers.
How do I hardcode subtitles that are already inside a video?
First extract the embedded track to SRT using subtitle-extractor, then pick that SRT here to burn it. This burner does not read the source video's own subtitle stream.
Will hardcoding hurt quality?
It requires one re-encode at CRF 20, which is visually lossless for most content, and leaves the audio untouched. Starting from a high-quality source keeps the hardsubbed result crisp even if a downstream platform re-compresses.
Privacy first
Every JAD Video tool runs entirely in your browser via WebCodecs and FFmpeg (WebAssembly). Your video files never leave your device — verified by zero outbound network requests during processing.