> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictory.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Dynamic Captions

> Add dynamic captions to AI-generated videos using the Pictory API. Control caption lines with maxSubtitleLines and sync with AI voiceover.

Dynamic captions are progressive on-screen subtitles controlled by the `maxSubtitleLines` parameter. This parameter determines how many lines of caption text appear on screen at any given time. When AI voiceover is enabled, each caption line is synchronized with the spoken audio, appearing precisely as it is narrated.

## What You Will Learn

<CardGroup cols={2}>
  <Card title="Enable Dynamic Captions" icon="closed-captioning">
    Configure caption lines that appear progressively on screen
  </Card>

  <Card title="Control Line Count" icon="align-justify">
    Use maxSubtitleLines to set the number of visible caption lines
  </Card>

  <Card title="Multi-Language Support" icon="globe">
    Compatible with ElevenLabs, AWS Polly, and Google voices
  </Card>
</CardGroup>

## Prerequisites

Ensure you have the following before proceeding:

* A Pictory API key ([get one here](https://app.pictory.ai/api-access))
* Node.js or Python installed on your machine
* Text content ready for video conversion

<CodeGroup>
  ```bash npm theme={null}
  npm install axios
  ```

  ```bash pip theme={null}
  pip install requests
  ```
</CodeGroup>

## How Dynamic Captions Work

The `maxSubtitleLines` parameter enables dynamic captions at the scene level. When included in a scene configuration, Pictory generates caption lines that appear progressively on screen, limited to the specified number of lines.

### With AI Voiceover

When AI voiceover is enabled alongside `maxSubtitleLines`, caption lines are synchronized with the spoken audio:

1. **Audio Generation:** The story text is converted to speech using the selected AI voice.
2. **Line Timing:** The audio service provides timing data that determines when each caption line appears.
3. **Caption Sync:** Each caption line is displayed on screen at the exact moment it is spoken in the voiceover.
4. **Line Control:** The `maxSubtitleLines` value limits how many lines of text are visible simultaneously.

### Without AI Voiceover

The `maxSubtitleLines` parameter can also be used without voiceover. In this mode, caption lines are distributed uniformly across the scene duration, with each line displayed for an equal amount of time.

<Note>
  When `maxSubtitleLines` is not provided, all caption lines for the scene are displayed on screen at once. To enable progressive caption display, set `maxSubtitleLines` to the desired number of visible lines. For the best results, pair `maxSubtitleLines` with AI voiceover to synchronize caption lines with the spoken audio.
</Note>

## Complete Example

<CodeGroup>
  ```javascript Node.js theme={null}
  import axios from "axios";

  const API_BASE_URL = "https://api.pictory.ai/pictoryapis";
  const API_KEY = "YOUR_API_KEY";

  async function createVideoWithDynamicCaptions() {
    try {
      console.log("Creating video with dynamic captions...");

      const response = await axios.post(
        `${API_BASE_URL}/v2/video/storyboard/render`,
        {
          videoName: "dynamic_captions_demo",

          // Voice-over enables caption sync with spoken audio
          voiceOver: {
            enabled: true,
            aiVoices: [
              {
                speaker: "Rachel",
                speed: 100,
                amplificationLevel: 0,
              },
            ],
          },

          // Scenes with dynamic captions
          scenes: [
            {
              story:
                "Creating professional videos used to take hours of editing, expensive software, and a dedicated production team. With Pictory, you can turn any script into a stunning video in minutes, powered by artificial intelligence.",
              maxSubtitleLines: 2, // Display 2 lines of captions at a time
            },
            {
              story:
                "Our dynamic captions feature automatically syncs with your voiceover, making every video accessible, engaging, and optimized for social media. No manual timing, no tedious adjustments.",
              maxSubtitleLines: 2,
            },
          ],
        },
        {
          headers: {
            "Content-Type": "application/json",
            Authorization: API_KEY,
          },
        }
      );

      const jobId = response.data.data.jobId;
      console.log("✓ Video creation started!");
      console.log("Job ID:", jobId);

      // Monitor progress
      console.log("\nMonitoring video creation...");
      let jobCompleted = false;
      let jobResult = null;

      while (!jobCompleted) {
        const statusResponse = await axios.get(
          `${API_BASE_URL}/v1/jobs/${jobId}`,
          {
            headers: { Authorization: API_KEY },
          }
        );

        const status = statusResponse.data.data.status;
        console.log("Status:", status);

        if (status === "completed") {
          jobCompleted = true;
          jobResult = statusResponse.data;
          console.log("\n✓ Video with dynamic captions is ready!");
          console.log("Video URL:", jobResult.data.videoURL);
        } else if (status === "failed") {
          throw new Error(
            "Video creation failed: " + JSON.stringify(statusResponse.data)
          );
        }

        await new Promise((resolve) => setTimeout(resolve, 5000));
      }

      return jobResult;
    } catch (error) {
      console.error("Error:", error.response?.data || error.message);
      throw error;
    }
  }

  createVideoWithDynamicCaptions();
  ```

  ```python Python theme={null}
  import requests
  import time
  import json

  API_BASE_URL = 'https://api.pictory.ai/pictoryapis'
  API_KEY = 'YOUR_API_KEY'

  def create_video_with_dynamic_captions():
      try:
          print("Creating video with dynamic captions...")

          response = requests.post(
              f'{API_BASE_URL}/v2/video/storyboard/render',
              json={
                  'videoName': 'dynamic_captions_demo',

                  # Voice-over enables caption sync with spoken audio
                  'voiceOver': {
                      'enabled': True,
                      'aiVoices': [
                          {
                              'speaker': 'Rachel',
                              'speed': 100,
                              'amplificationLevel': 0
                          }
                      ]
                  },

                  # Scenes with dynamic captions
                  'scenes': [
                      {
                          'story': 'Creating professional videos used to take hours of editing, expensive software, and a dedicated production team. With Pictory, you can turn any script into a stunning video in minutes, powered by artificial intelligence.',
                          'maxSubtitleLines': 2  # Display 2 lines of captions at a time
                      },
                      {
                          'story': 'Our dynamic captions feature automatically syncs with your voiceover, making every video accessible, engaging, and optimized for social media. No manual timing, no tedious adjustments.',
                          'maxSubtitleLines': 2
                      }
                  ]
              },
              headers={
                  'Content-Type': 'application/json',
                  'Authorization': API_KEY
              }
          )
          response.raise_for_status()

          job_id = response.json()['data']['jobId']
          print("✓ Video creation started!")
          print(f"Job ID: {job_id}")

          # Monitor progress
          print("\nMonitoring video creation...")
          job_completed = False
          job_result = None

          while not job_completed:
              status_response = requests.get(
                  f'{API_BASE_URL}/v1/jobs/{job_id}',
                  headers={'Authorization': API_KEY}
              )
              status_response.raise_for_status()

              status = status_response.json()['data']['status']
              print(f"Status: {status}")

              if status == 'completed':
                  job_completed = True
                  job_result = status_response.json()
                  print("\n✓ Video with dynamic captions is ready!")
                  print(f"Video URL: {job_result['data']['videoURL']}")
              elif status == 'failed':
                  raise Exception(f"Video creation failed: {status_response.json()}")

              time.sleep(5)

          return job_result

      except requests.exceptions.RequestException as error:
          print(f"Error: {error}")
          if hasattr(error, 'response') and error.response is not None:
              print(f"Response: {error.response.text}")
          raise

  if __name__ == '__main__':
      create_video_with_dynamic_captions()
  ```
</CodeGroup>

## Understanding the Parameters

### Scene-Level Configuration

| Parameter                   | Type   | Required | Description                                                                                                       |
| --------------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `scenes[].maxSubtitleLines` | number | No       | Maximum number of caption lines displayed simultaneously. When omitted, all lines are shown on the scene at once. |

### maxSubtitleLines Values

| Value   | Description                                                                        |
| ------- | ---------------------------------------------------------------------------------- |
| Not set | All caption lines are displayed on the scene at once (no progressive display)      |
| `1`     | Displays one caption line at a time. Ideal for short-form content                  |
| `2`     | Standard caption display. Suitable for most content types                          |
| `3`     | Displays more text simultaneously. Recommended for educational or detailed content |
| `4`     | Maximum text density. Best for information-heavy content                           |

<Note>
  When `maxSubtitleLines` is not included in the request, all caption text for the scene is shown at once. Setting this parameter activates progressive caption display. Enabling AI voiceover synchronizes caption lines with the spoken audio.
</Note>

## Line Count by Use Case

### Single Line (maxSubtitleLines: 1)

Suited for short-form social media content where concise, fast-paced text is preferred:

```javascript theme={null}
{
  scenes: [{
    story: "Your content here...",
    maxSubtitleLines: 1
  }]
}
```

**Result:** Clean, single-line captions optimized for quick readability. Ideal for TikTok, Instagram Reels, and YouTube Shorts.

### Two Lines (maxSubtitleLines: 2)

The most common configuration, providing a balance between readability and content density:

```javascript theme={null}
{
  scenes: [{
    story: "Your content here...",
    maxSubtitleLines: 2
  }]
}
```

**Result:** Standard two-line captions suitable for most video formats. Recommended for YouTube, LinkedIn, and marketing videos.

### Three Lines (maxSubtitleLines: 3)

Appropriate for longer sentences or content that benefits from additional on-screen context:

```javascript theme={null}
{
  scenes: [{
    story: "Your detailed content with longer sentences...",
    maxSubtitleLines: 3
  }]
}
```

**Result:** Increased text visibility per frame. Ideal for educational, documentary, or training content.

## Mixed Line Counts

Different `maxSubtitleLines` values can be assigned to individual scenes within the same video:

```javascript theme={null}
{
  scenes: [
    {
      story: "Quick intro text for social media hook.",
      maxSubtitleLines: 1  // Single line for the opening hook
    },
    {
      story: "Now let me explain in more detail how this technology works and why it matters for your business growth strategy.",
      maxSubtitleLines: 3  // Additional lines for the detailed explanation
    }
  ]
}
```

## Aspect Ratio Considerations

The optimal `maxSubtitleLines` value depends on the video aspect ratio:

| Aspect Ratio | Orientation | Recommendation |
| ------------ | ----------- | -------------- |
| `16:9`       | Landscape   | 2-3 lines      |
| `1:1`        | Square      | 2 lines        |
| `4:5`        | Vertical    | 1-2 lines      |
| `9:16`       | Portrait    | 1-2 lines      |

<Note>
  Vertical and portrait formats (4:5 and 9:16) have limited horizontal space, causing longer sentences to wrap more frequently. Use fewer lines for vertical content to maintain readability.
</Note>

## Supported Voice Providers

Dynamic captions with voiceover synchronization are supported across all voice providers:

| Provider   | Example Voices | Caption Sync Method       |
| ---------- | -------------- | ------------------------- |
| ElevenLabs | Rachel, Adam   | Native timing alignment   |
| AWS Polly  | Aria, Matthew  | Via transcription service |
| Google TTS | Eugene, Lisa   | Via transcription service |

<Warning>
  **Compatibility**

  Dynamic captions via `maxSubtitleLines` function with or without AI voiceover. When AI voiceover is enabled, caption lines are synchronized with the spoken audio, appearing on screen as each line is narrated.

  **Supported Workflows:**

  * Text-to-Video (using `story` parameter)
  * Article-to-Video (using `blogUrl` parameter)
  * Image slideshows with text overlays

  **Not Supported With:**

  * `smartLayoutName` or `smartLayoutId` (smart layouts manage text display independently)
  * The `caption` parameter (uses separate text handling)
  * Audio-to-Video workflows (`audioUrl`)
  * Video-to-Video workflows (`videoUrl`)
  * PowerPoint-to-Video workflows (`pptUrl`)
</Warning>

## Best Practices

<AccordionGroup>
  <Accordion title="Match Line Count to Platform">
    Select the line count based on the target publishing platform:

    * **TikTok/Reels:** 1 line for rapid readability
    * **YouTube:** 2 lines for standard viewing
    * **Educational platforms:** 2-3 lines for detailed content
    * **Presentations:** 2 lines for a professional appearance
  </Accordion>

  <Accordion title="Enable Voice-Over for Best Results">
    While dynamic captions function without voiceover, enabling AI voiceover synchronizes caption lines with the spoken audio, providing the best viewer experience:

    ```javascript theme={null}
    {
      voiceOver: {
        enabled: true,
        aiVoices: [{ speaker: "Rachel" }]
      }
    }
    ```
  </Accordion>

  <Accordion title="Consider Reading Speed">
    Account for viewer reading speed when selecting a line count:

    * Fewer lines result in faster comprehension
    * More lines provide additional context but require more processing time
    * Match line count to content complexity
  </Accordion>

  <Accordion title="Coordinate with Font Size">
    Line count and font size are interdependent:

    * Larger fonts require fewer lines to avoid overcrowding
    * Smaller fonts can accommodate additional lines
    * Verify that text remains legible at all sizes
    * Test across different screen dimensions
  </Accordion>

  <Accordion title="Default Behavior Without maxSubtitleLines">
    When `maxSubtitleLines` is not provided, all caption text for the scene is displayed at once. To enable progressive caption display, explicitly set this parameter to the desired line count.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Captions Not Syncing With Audio">
    **Cause:** Caption lines appear on screen but are not synchronized with the voiceover.

    **Resolution:**

    1. Verify that voice-over is enabled in the request
    2. Confirm that `maxSubtitleLines` is set at the scene level
    3. Ensure that smart layouts are not in use, as they manage text display independently
  </Accordion>

  <Accordion title="maxSubtitleLines Not Allowed With Smart Layouts">
    **Cause:** The API returns an error because `maxSubtitleLines` cannot be combined with smart layouts.

    **Resolution:**

    1. Remove `maxSubtitleLines` from the scene configuration, or
    2. Remove `smartLayoutName` or `smartLayoutId` if dynamic captions are required

    Smart layouts and dynamic captions are mutually exclusive. Select one approach per scene.
  </Accordion>

  <Accordion title="maxSubtitleLines Not Allowed With Caption">
    **Cause:** The API returns an error because `maxSubtitleLines` and `caption` cannot be used together.

    **Resolution:**

    1. Remove either the `maxSubtitleLines` or `caption` parameter
    2. Use the `story` parameter with `maxSubtitleLines` for dynamic captions

    The `caption` parameter uses its own text handling and is incompatible with dynamic captions.
  </Accordion>

  <Accordion title="More Lines Displayed Than Expected">
    **Cause:** Subtitles appear with more lines than the configured `maxSubtitleLines` value.

    **Resolution:**

    1. Verify the parameter is spelled correctly: `maxSubtitleLines`
    2. Confirm the parameter is set at the scene level, not the video level
    3. Ensure that smart layouts are not active on the scene
    4. Reduce font size if long words are causing unexpected line wrapping
  </Accordion>
</AccordionGroup>

## Next Steps

Enhance your dynamic captions with these related features:

<CardGroup cols={2}>
  <Card title="Custom Subtitle Style" icon="font" href="/guides/branding-customization/custom-subtitle-style">
    Customize font, color, and background of your captions
  </Card>

  <Card title="Saved Subtitle Styles" icon="closed-captioning" href="/guides/branding-customization/subtitle-styles">
    Apply saved subtitle style presets
  </Card>

  <Card title="Highlight Keywords" icon="highlighter" href="/guides/branding-customization/highlight-keywords">
    Emphasize important words in captions
  </Card>

  <Card title="Smart Layouts" icon="table-layout" href="/guides/smart-layouts-and-subtitles/smart-layouts">
    Use pre-designed layouts with automatic text handling
  </Card>
</CardGroup>

## API Reference

For complete technical details, refer to the following resources:

<CardGroup cols={2}>
  <Card title="Render Storyboard Video" icon="video" href="/api-reference/videos/render-storyboard-video">
    Configure dynamic captions and voice-over settings
  </Card>

  <Card title="Get Job Status" icon="spinner" href="/api-reference/jobs/get-job-by-id">
    Monitor video creation progress
  </Card>

  <Card title="Get Text Styles" icon="font" href="/api-reference/branding/get-text-styles">
    Retrieve available subtitle styles
  </Card>

  <Card title="Get Smart Layouts" icon="table-layout" href="/api-reference/smartlayouts/get-smart-layouts">
    Retrieve all available smart layout templates
  </Card>
</CardGroup>
