> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BintzGavin/helios/llms.txt
> Use this file to discover all available pages before exploring further.

# Captions

> SRT and WebVTT subtitle support with automatic parsing and synchronization in Helios

Helios provides built-in support for captions and subtitles using standard SRT and WebVTT formats. Captions are automatically synchronized with your timeline and can be easily styled and positioned.

## Caption formats

Helios supports two standard caption formats:

* **SRT** (SubRip): Simple, widely-supported format with timecodes in `HH:MM:SS,mmm` format
* **WebVTT**: Modern web standard with timecodes in `HH:MM:SS.mmm` or `MM:SS.mmm` format

### SRT format

```srt theme={null}
1
00:00:00,500 --> 00:00:02,000
Hello, welcome to Helios!

2
00:00:02,000 --> 00:00:03,500
This is a subtitle example.
```

### WebVTT format

```vtt theme={null}
WEBVTT

1
00:00:00.500 --> 00:00:02.000
Hello, welcome to Helios!

2
00:00:02.000 --> 00:00:03.500
This is a subtitle example.
```

## Loading captions

Captions can be loaded when creating a Helios instance or added later.

### During initialization

```typescript theme={null}
import { Helios } from '@helios-project/core';

const srtContent = `
1
00:00:00,500 --> 00:00:02,000
Welcome to the video

2
00:00:02,000 --> 00:00:04,000
Enjoy the content!
`;

const helios = new Helios({
  duration: 10,
  fps: 30,
  captions: srtContent // Can be SRT or WebVTT string
});
```

### Setting captions at runtime

```typescript theme={null}
// Load from string
helios.setCaptions(srtContent);

// Or use parsed caption objects
import { parseSrt, parseWebVTT } from '@helios-project/core';

const cues = parseSrt(srtContent);
helios.setCaptions(cues);
```

## Accessing captions

Helios provides two caption-related signals:

```typescript theme={null}
// All captions in the composition
const allCaptions = helios.captions.value;

// Currently active captions at the current frame
const activeCaptions = helios.activeCaptions.value;
```

### Caption cue structure

<ParamField path="id" type="string">
  Unique identifier for the caption cue
</ParamField>

<ParamField path="startTime" type="number">
  Start time in milliseconds
</ParamField>

<ParamField path="endTime" type="number">
  End time in milliseconds
</ParamField>

<ParamField path="text" type="string">
  Caption text content (may contain newlines)
</ParamField>

## Displaying captions

Create a caption component that reacts to the active captions:

```typescript theme={null}
import { useEffect, useState } from 'react';
import { Helios } from '@helios-project/core';

function CaptionOverlay({ helios }) {
  const [activeCaptions, setActiveCaptions] = useState([]);
  
  useEffect(() => {
    return helios.subscribe((state) => {
      setActiveCaptions(state.activeCaptions);
    });
  }, [helios]);
  
  return (
    <div style={{
      position: 'absolute',
      bottom: '10%',
      left: '50%',
      transform: 'translateX(-50%)',
      textAlign: 'center',
      color: 'white',
      fontSize: '24px',
      textShadow: '2px 2px 4px rgba(0, 0, 0, 0.8)',
      padding: '8px 16px',
      backgroundColor: 'rgba(0, 0, 0, 0.7)',
      borderRadius: '4px'
    }}>
      {activeCaptions.map((cue) => (
        <div key={cue.id}>
          {cue.text.split('\n').map((line, i) => (
            <div key={i}>{line}</div>
          ))}
        </div>
      ))}
    </div>
  );
}
```

## Parsing utilities

Helios provides utilities for parsing and working with caption files.

### parseSrt()

Parses SRT format caption content.

```typescript theme={null}
import { parseSrt } from '@helios-project/core';

const cues = parseSrt(srtContent);
// [
//   { id: '1', startTime: 500, endTime: 2000, text: 'Hello' },
//   { id: '2', startTime: 2000, endTime: 3500, text: 'World' }
// ]
```

<ParamField path="content" type="string" required>
  SRT formatted caption content
</ParamField>

Returns: `CaptionCue[]`

### parseWebVTT()

Parses WebVTT format caption content.

```typescript theme={null}
import { parseWebVTT } from '@helios-project/core';

const cues = parseWebVTT(webVttContent);
```

<ParamField path="content" type="string" required>
  WebVTT formatted caption content (must start with "WEBVTT")
</ParamField>

Returns: `CaptionCue[]`

### parseCaptions()

Automatically detects and parses SRT or WebVTT format.

```typescript theme={null}
import { parseCaptions } from '@helios-project/core';

const cues = parseCaptions(content); // Auto-detects format
```

<ParamField path="content" type="string" required>
  Caption content in SRT or WebVTT format
</ParamField>

Returns: `CaptionCue[]`

### stringifySrt()

Converts caption cues back to SRT format.

```typescript theme={null}
import { stringifySrt } from '@helios-project/core';

const srtContent = stringifySrt(cues);
```

<ParamField path="cues" type="CaptionCue[]" required>
  Array of caption cues to serialize
</ParamField>

Returns: `string` - SRT formatted content

### findActiveCues()

Finds captions active at a specific time.

```typescript theme={null}
import { findActiveCues } from '@helios-project/core';

const currentTimeMs = (frame / fps) * 1000;
const active = findActiveCues(cues, currentTimeMs);
```

<ParamField path="cues" type="CaptionCue[]" required>
  Array of caption cues to search
</ParamField>

<ParamField path="timeMs" type="number" required>
  Time in milliseconds
</ParamField>

Returns: `CaptionCue[]` - Captions active at the specified time

## Common patterns

### Loading captions from file

```typescript theme={null}
import { Helios } from '@helios-project/core';

async function loadComposition() {
  const response = await fetch('/captions/video.srt');
  const srtContent = await response.text();
  
  const helios = new Helios({
    duration: 10,
    fps: 30,
    captions: srtContent
  });
  
  return helios;
}
```

### Styled caption component

```typescript theme={null}
function StyledCaptions({ helios }) {
  const [activeCaptions, setActiveCaptions] = useState([]);
  
  useEffect(() => {
    return helios.subscribe((state) => {
      setActiveCaptions(state.activeCaptions);
    });
  }, [helios]);
  
  if (activeCaptions.length === 0) return null;
  
  return (
    <div className="caption-container">
      {activeCaptions.map((cue) => (
        <div key={cue.id} className="caption-text">
          {cue.text.split('\n').map((line, i) => (
            <div key={i} className="caption-line">{line}</div>
          ))}
        </div>
      ))}
    </div>
  );
}
```

```css theme={null}
.caption-container {
  position: absolute;
  bottom: 10%;
  left: 50%;
  transform: translateX(-50%);
  max-width: 80%;
  text-align: center;
}

.caption-text {
  display: inline-block;
  padding: 8px 16px;
  background: rgba(0, 0, 0, 0.8);
  border-radius: 4px;
  color: white;
  font-size: 24px;
  line-height: 1.4;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
}

.caption-line {
  margin: 4px 0;
}
```

### Animated captions

```typescript theme={null}
import { transition, Easing } from '@helios-project/core';

function AnimatedCaptions({ helios }) {
  const [activeCaptions, setActiveCaptions] = useState([]);
  const frame = useVideoFrame(helios);
  const fps = helios.fps.value;
  
  useEffect(() => {
    return helios.subscribe((state) => {
      setActiveCaptions(state.activeCaptions);
    });
  }, [helios]);
  
  return (
    <div className="caption-container">
      {activeCaptions.map((cue) => {
        const currentTimeMs = (frame / fps) * 1000;
        const cueProgress = (currentTimeMs - cue.startTime) / (cue.endTime - cue.startTime);
        
        // Fade in/out at start and end
        let opacity = 1;
        if (cueProgress < 0.1) {
          opacity = cueProgress / 0.1;
        } else if (cueProgress > 0.9) {
          opacity = (1 - cueProgress) / 0.1;
        }
        
        // Slide up slightly at start
        const y = cueProgress < 0.2 ? (0.2 - cueProgress) * 50 : 0;
        
        return (
          <div
            key={cue.id}
            style={{
              opacity,
              transform: `translateY(${y}px)`,
              transition: 'transform 0.3s ease-out'
            }}
          >
            {cue.text}
          </div>
        );
      })}
    </div>
  );
}
```

### Multi-language captions

```typescript theme={null}
import { useState } from 'react';

function MultiLanguageCaptions({ helios }) {
  const [language, setLanguage] = useState('en');
  const [captionsByLanguage] = useState({
    en: parseSrt(englishSrt),
    es: parseSrt(spanishSrt),
    fr: parseSrt(frenchSrt)
  });
  
  useEffect(() => {
    helios.setCaptions(captionsByLanguage[language]);
  }, [language, helios]);
  
  return (
    <>
      <select value={language} onChange={(e) => setLanguage(e.target.value)}>
        <option value="en">English</option>
        <option value="es">Español</option>
        <option value="fr">Français</option>
      </select>
      <CaptionOverlay helios={helios} />
    </>
  );
}
```

### Caption search and navigation

```typescript theme={null}
function CaptionTimeline({ helios }) {
  const captions = helios.captions.value;
  const fps = helios.fps.value;
  
  const seekToCaption = (cue) => {
    const frame = (cue.startTime / 1000) * fps;
    helios.seek(frame);
  };
  
  return (
    <div className="caption-timeline">
      {captions.map((cue) => (
        <button
          key={cue.id}
          onClick={() => seekToCaption(cue)}
          className="caption-marker"
        >
          {cue.text.substring(0, 30)}...
        </button>
      ))}
    </div>
  );
}
```

### Export captions

```typescript theme={null}
import { stringifySrt } from '@helios-project/core';

function ExportCaptions({ helios }) {
  const exportToFile = () => {
    const captions = helios.captions.value;
    const srtContent = stringifySrt(captions);
    
    const blob = new Blob([srtContent], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    
    const a = document.createElement('a');
    a.href = url;
    a.download = 'captions.srt';
    a.click();
    
    URL.revokeObjectURL(url);
  };
  
  return (
    <button onClick={exportToFile}>
      Export Captions
    </button>
  );
}
```

### Creating captions programmatically

```typescript theme={null}
import { CaptionCue } from '@helios-project/core';

function generateCaptions(segments: string[], intervalSeconds: number): CaptionCue[] {
  return segments.map((text, i) => ({
    id: `${i + 1}`,
    startTime: i * intervalSeconds * 1000,
    endTime: (i + 1) * intervalSeconds * 1000,
    text
  }));
}

const captions = generateCaptions(
  ['First caption', 'Second caption', 'Third caption'],
  2 // 2 seconds each
);

helios.setCaptions(captions);
```
