---
title: "Convert SRT to WebVTT in C#"
url: https://recursiveai.org/onemedia/cookbook/convert-srt-to-webvtt/
description: "Convert SRT to WebVTT in C# with OneMedia: one call, the format read from the content, and a report of every style WebVTT can’t hold (strike-through, fonts) or approximates (colours, position). The output loaded as a <track> in Chrome. A preview."
version: "0.6.0-preview.1"
---

# Convert SRT to WebVTT in C#

`Subtitles.Convert("in.srt", "out.vtt")` converts a subtitle file and returns a `ConversionReport` of everything the target format dropped or approximated. The input format is read from the content, the output format from the extension; SRT, WebVTT, ASS/SSA, TTML/DFXP, SBV and LRC all work the same way. Subtitles are a preview in 0.6.0-preview.1.

```shell
dotnet new console -o SrtToVtt && cd SrtToVtt
dotnet add package RecursiveAI.OneMedia --version 0.6.0-preview.1
cat > styled.srt <<'EOF'
1
00:00:00,500 --> 00:00:02,000
<i>A flower opens.</i>

2
00:00:02,200 --> 00:00:03,600
<font color="#ff8000">Orange</font> and <b>bold</b>

3
00:00:03,800 --> 00:00:05,000
{\an8}<s>Struck</s> in <font face="Georgia" size="28">Georgia</font>, at the top
EOF
```

`styled.srt` uses the styling SRT allows: italics, a colour, bold, strike-through, a font, and `{\an8}` to place a cue at the top. Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneMedia;
using RecursiveAI.OneMedia.Captions;

// The format is read from the content and the target from the extension.
ConversionReport report = Subtitles.Convert("styled.srt", "styled.vtt");
Console.WriteLine($"{report.Source} to {report.Target}: "
                  + (report.IsLossless ? "nothing lost" : $"{report.Losses.Count} loss(es)"));
foreach (ConversionLoss loss in report.Losses)
    Console.WriteLine($"  cue {loss.CueIndex}: {loss.Kind} {loss.Feature}: {loss.Message}");

Console.WriteLine();
Console.Write(File.ReadAllText("styled.vtt"));
```

```text
srt to webvtt: 4 loss(es)
  cue 2: Dropped Strike: Strike-through is not carried by webvtt.
  cue 2: Dropped Font: Font family and size are not carried by webvtt.
  cue 1: Approximated Color: Colour mapped to the nearest WebVTT default colour class 'yellow'.
  cue 2: Approximated Alignment: Top alignment written as line 0.

WEBVTT

00:00:00.500 --> 00:00:02.000
<i>A flower opens.</i>

00:00:02.200 --> 00:00:03.600
<c.yellow>Orange</c> and <b>bold</b>

00:00:03.800 --> 00:00:05.000 line:0 align:center
Struck in Georgia, at the top
```

Italics and bold carry over as they are. WebVTT has no strike-through and no fonts, so those are dropped and reported. Its colours are eight named classes, so the orange becomes the nearest one, `yellow`; the top placement becomes `line:0`. Cue indexes count from 0. `report.IsLossless` is true when nothing was dropped or approximated, as for a plain SRT file.

## Checked in Chrome

The VTT file was loaded as a `<track>` on MDN’s CC0 flower clip in headless Chrome 149, and the cues read back through the `TextTrack` API:

```text
start  end   line  align   text
0.5    2     auto  center  <i>A flower opens.</i>
2.2    3.6   auto  center  <c.yellow>Orange</c> and <b>bold</b>
3.8    5     0     center  Struck in Georgia, at the top
```

Chrome reads all three cues with their times and text, and the third cue’s `line:0`: with the track showing, at 4.2 s the cue is drawn at the top of the frame. (0.4 wrote `line:0,start`, which Chrome ignores, so the cue stayed at the bottom; 0.6 writes `line:0`, and the changelog says it was checked in Chromium 140, Chrome 155, Firefox 141 and WebKit 26.)

## From the command line

`onemedia subs styled.srt --to styled.vtt --report` converts the file and lists every loss; `--offset 1.5` shifts all times by 1.5 s.

## Limits

- **A preview.** The README on speed: “subtitle parse and convert at 20,000 and 100,000 cues, against SubtitlesParser and Subtitle Edit’s library, come out at a geometric mean of 0.47× (x64) and 0.52× (arm64) of the fastest comparator, worst row 1.21×; SRT and large ASS parsing are close to Subtitle Edit’s time, not ahead” ([x64](https://recursiveai.org/onemedia/docs/bench-x64-0-5/), [arm64](https://recursiveai.org/onemedia/docs/bench-arm64-0-5/)). The API may change before 1.0.
- **What each format holds** is in the fidelity table on the [subtitles page](https://recursiveai.org/onemedia/docs/subtitles/): ASS and LRC times are rounded to 10 ms, LRC has no end times, SBV is plain text. The report lists every case.
- **Encodings.** A file with a byte order mark, or valid UTF-8, is read as such; otherwise as windows-1252 unless you pass an encoding (`onemedia subs … --encoding`), with a note in the defects. WebVTT is always written as UTF-8.
- **Times are not shifted** unless you ask: `SubtitleAlignment.Shift(document, offset)` moves every cue.
