> ## 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.

# Media Elements (Video & Image Overlays)

> Place videos and images on top of any scene: picture-in-picture clips, product shots, screenshots, and AI-generated visuals, sourced from a URL, stock search, or a text prompt

This guide shows you how to add **video** and **image** elements to your scenes. Media elements are overlays placed on top of a scene's background, for example a product demo playing picture-in-picture in a corner, a screenshot next to your narration, or an AI-generated illustration.

They live in the same scene-level `elements` array as [shape and text elements](/guides/advanced-features/scene-elements), and can be freely mixed with them.

## What You'll Learn

<CardGroup cols={2}>
  <Card title="Three Ways to Add Media" icon="layer-group">
    Bring your own URL, search the stock library, or generate with AI
  </Card>

  <Card title="Positioning and Sizing" icon="up-down-left-right">
    Anchor presets, exact placement, and automatic fit-to-screen sizing
  </Card>

  <Card title="Playback Settings" icon="film">
    Control looping and audio muting for video elements
  </Card>

  <Card title="Color Overlays" icon="droplet">
    Tint an individual element, independent of the scene background
  </Card>
</CardGroup>

## Before You Begin

Make sure you have:

* A [Pictory API key](https://app.pictory.ai/api-access)
* Basic understanding of [scene configuration](/guides/text-to-video/text-to-video-basic) in the Pictory API

<Note>
  Media elements are processed by the v3 storyboard engine. Any scene that includes an `elements` array is automatically routed to v3; you do not need to set `storyboardVersion` yourself.
</Note>

## How Media Elements Work

Set `type` to `"video"` or `"image"`, then tell Pictory where the media comes from. Every media element uses **exactly one** of three sources:

| Source field   | What it does                               | Best for                                                    |
| -------------- | ------------------------------------------ | ----------------------------------------------------------- |
| `visualUrl`    | Uses the file at the URL you provide       | Your own footage, product shots, screenshots                |
| `searchFilter` | Searches the stock library with your query | Quick, relevant b-roll and imagery without hosting files    |
| `aiVisual`     | Generates the media from a text prompt     | Visuals that do not exist yet: illustrations, concept shots |

```jsonc theme={null}
"scenes": [
  {
    "story": "Your scene narration here.",
    "elements": [
      // 1. Your own video, playing picture-in-picture
      {
        "type": "video",
        "visualUrl": "https://example.com/videos/product-demo.mp4",
        "position": "bottom-right",
        "width": "30%"
      },

      // 2. A stock image found by search
      {
        "type": "image",
        "searchFilter": { "query": "growth chart on laptop screen" },
        "top": "10%",
        "left": "6%"
      },

      // 3. An AI-generated illustration
      {
        "type": "image",
        "aiVisual": {
          "prompt": "minimalist illustration of a rocket launching from a laptop",
          "model": "seedream3.0"
        },
        "position": "center-right",
        "width": "25%"
      }
    ]
  }
]
```

* A maximum of **20 elements** per scene is allowed (shapes, text, and media combined).
* Provide exactly one of `visualUrl`, `searchFilter`, or `aiVisual` per media element; sending two or more is rejected.

## Source 1: Your Own Media (`visualUrl`)

Point `visualUrl` at any publicly reachable video or image file. Pictory reads the file's duration and dimensions automatically, so the element keeps its natural proportions.

| Field       | Type   | Required | Description            |
| ----------- | ------ | -------- | ---------------------- |
| `type`      | string | Yes      | `"video"` or `"image"` |
| `visualUrl` | string | Yes      | URL of the media file  |

```jsonc theme={null}
{ "type": "video", "visualUrl": "https://example.com/videos/demo.mp4", "position": "bottom-right", "width": "30%" }
```

## Source 2: Stock Library Search (`searchFilter`)

Describe what you want and Pictory finds a matching visual in the stock library. For media elements, `searchFilter` takes a single field:

| Field                | Type   | Required | Description                                                                                 |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------------- |
| `type`               | string | Yes      | `"video"` or `"image"`. This also decides whether stock videos or stock photos are searched |
| `searchFilter.query` | string | Yes      | What to search for, in plain language                                                       |

```jsonc theme={null}
{ "type": "image", "searchFilter": { "query": "team celebrating in office" }, "position": "top-left", "width": "25%" }
```

The search also respects the request-level `language` field, so non-English queries work naturally.

<Note>
  If the search finds no suitable match, the element is simply left out of the video. The rest of the scene still renders and the request does not fail. Prefer specific, visual queries ("growth chart on laptop screen") over abstract ones ("success").
</Note>

## Source 3: AI-Generated Media (`aiVisual`)

Generate the element from a text prompt. The `aiVisual` object accepts the same fields as [background AI visuals](/guides/ai-generated-visuals/background-images):

| Field                    | Type   | Required | Description                                                                                                                       |
| ------------------------ | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `aiVisual.prompt`        | string | No       | What to generate. If omitted, the scene's narration guides the generation                                                         |
| `aiVisual.model`         | string | Yes      | Image models: `flux-schnell`, `seedream3.0`, `nanobanana`, `nanobanana-pro`. Video models: `pixverse5.5`, `veo3.1_fast`, `veo3.1` |
| `aiVisual.mediaStyle`    | string | No       | `photorealistic`, `artistic`, `cartoon`, `minimalist`, `vintage`, `futuristic`                                                    |
| `aiVisual.videoDuration` | string | No       | Video elements only, e.g. `"8s"`. Allowed values depend on the model. Defaults to the model's shortest duration                   |

```jsonc theme={null}
{
  "type": "video",
  "aiVisual": { "prompt": "abstract waves of light on a dark background", "model": "veo3.1", "videoDuration": "8s" },
  "top": "40%",
  "left": "55%"
}
```

<Note>
  AI generation consumes **AI Credits** according to the selected model's rate. See the per-model credit tables in the [AI Background Images](/guides/ai-generated-visuals/background-images#available-image-models) and [AI Background Videos](/guides/ai-generated-visuals/background-videos) guides, and [Understanding AI Credits](https://kb.pictory.ai/en/articles/12382079-understanding-ai-credits). If your account does not have enough credits, the element is skipped and the rest of the video still renders.
</Note>

## Positioning and Sizing

Media elements use the same positioning rules as shapes and text: a `position` anchor **or** explicit `top`/`left` offsets, never both.

| Field      | Type   | Description                                                                                                                                                         |
| ---------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `position` | string | Anchor region: `top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`, `bottom-left`, `bottom-center`, `bottom-right`. Defaults to `center` |
| `top`      | string | Vertical offset as a percentage from the top, e.g. `"40%"`. Cannot be combined with `position`                                                                      |
| `left`     | string | Horizontal offset as a percentage from the left, e.g. `"55%"`. Cannot be combined with `position`                                                                   |
| `width`    | string | Element width as a percentage of the canvas, e.g. `"30%"`. Height follows automatically from the media's own proportions                                            |

### Sizing: exact or automatic

* **If you provide `width`**, it is used exactly as given. A `width` of `"100%"` spans the full canvas.
* **If you omit `width`**, the element is sized automatically: it starts at 30% of the canvas and shrinks as needed so the whole element stays on screen, taking into account its position and the media's proportions. A portrait clip placed near the bottom of a landscape video, for example, is scaled down so it does not run off the bottom edge.

### Recipe: full-screen video element

To cover the entire frame with a video element, combine explicit offsets at the origin with a 100% width:

```jsonc theme={null}
{
  "type": "video",
  "visualUrl": "https://example.com/videos/loop.mp4",
  "top": "0%",
  "left": "0%",
  "width": "100%",
  "settings": { "loop": true, "mute": true }
}
```

The element covers the frame exactly when the media's aspect ratio matches the video's aspect ratio (for example, a 16:9 clip in a 16:9 video).

## Playback Settings (Video Elements)

Video elements accept an optional `settings` object:

| Field           | Type    | Default | Description                                                             |
| --------------- | ------- | ------- | ----------------------------------------------------------------------- |
| `settings.loop` | boolean | `true`  | Restart the clip when it ends, for as long as the scene lasts           |
| `settings.mute` | boolean | `true`  | Silence the clip's own audio so it does not compete with your narration |

Both default to `true`, which is what you want for most picture-in-picture overlays. Set `mute: false` only when the element's own audio should be heard.

```jsonc theme={null}
{ "type": "video", "visualUrl": "https://example.com/testimonial.mp4", "settings": { "loop": false, "mute": false } }
```

## Color Overlay on an Element

Any media element can carry its own `colorOverlay`, the same tint control available for [scene backgrounds](/guides/advanced-features/color-overlay), applied to just this element:

```jsonc theme={null}
{
  "type": "image",
  "searchFilter": { "query": "city skyline at dusk" },
  "position": "center-left",
  "width": "35%",
  "colorOverlay": { "color": "#1A1A2E", "opacity": 0.3 }
}
```

## 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 createVideoWithMediaElements() {
    const response = await axios.post(
      `${API_BASE_URL}/v2/video/storyboard`,
      {
        videoName: "video_with_media_elements",
        scenes: [
          {
            story: "Here is the dashboard our customers use every day.",
            elements: [
              // Your own screen recording, picture-in-picture
              {
                type: "video",
                visualUrl: "https://example.com/videos/dashboard-demo.mp4",
                position: "bottom-right",
                width: "35%",
                settings: { loop: true, mute: true },
              },
            ],
          },
          {
            story: "Teams that switch to our platform report faster delivery within weeks.",
            elements: [
              // A stock image found by search, tinted to match the brand
              {
                type: "image",
                searchFilter: { query: "team collaborating around a laptop" },
                top: "12%",
                left: "8%",
                width: "30%",
                colorOverlay: { color: "#1A1A2E", opacity: 0.25 },
              },
              // An AI-generated illustration
              {
                type: "image",
                aiVisual: {
                  prompt: "minimalist illustration of a rising bar chart with a rocket",
                  model: "seedream3.0",
                  mediaStyle: "minimalist",
                },
                position: "center-right",
                width: "25%",
              },
            ],
          },
        ],
      },
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: API_KEY,
        },
      }
    );

    console.log("✓ Storyboard creation started!");
    console.log("Job ID:", response.data.data.jobId);
  }

  createVideoWithMediaElements();
  ```

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

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

  def create_video_with_media_elements():
      response = requests.post(
          f"{API_BASE_URL}/v2/video/storyboard",
          headers={"Content-Type": "application/json", "Authorization": API_KEY},
          json={
              "videoName": "video_with_media_elements",
              "scenes": [
                  {
                      "story": "Here is the dashboard our customers use every day.",
                      "elements": [
                          {
                              "type": "video",
                              "visualUrl": "https://example.com/videos/dashboard-demo.mp4",
                              "position": "bottom-right",
                              "width": "35%",
                              "settings": {"loop": True, "mute": True},
                          },
                      ],
                  },
                  {
                      "story": "Teams that switch to our platform report faster delivery within weeks.",
                      "elements": [
                          {
                              "type": "image",
                              "searchFilter": {"query": "team collaborating around a laptop"},
                              "top": "12%",
                              "left": "8%",
                              "width": "30%",
                              "colorOverlay": {"color": "#1A1A2E", "opacity": 0.25},
                          },
                          {
                              "type": "image",
                              "aiVisual": {
                                  "prompt": "minimalist illustration of a rising bar chart with a rocket",
                                  "model": "seedream3.0",
                                  "mediaStyle": "minimalist",
                              },
                              "position": "center-right",
                              "width": "25%",
                          },
                      ],
                  },
              ],
          },
      )

      print("✓ Storyboard creation started!")
      print("Job ID:", response.json()["data"]["jobId"])

  create_video_with_media_elements()
  ```
</CodeGroup>

## When an Element Cannot Be Resolved

Media elements are designed to degrade gracefully. The element is left out of the video, without failing the request, when:

* a `searchFilter` query finds no suitable stock match,
* an `aiVisual` generation fails, or
* the account does not have enough AI Credits for the generation.

Everything else in the scene (background, narration, other elements) still renders. If an expected element is missing from the preview, refine the search query, check your AI Credits balance, or verify the `visualUrl` is publicly reachable.

## Next Steps

<CardGroup cols={2}>
  <Card title="Shapes and Text Elements" icon="shapes" href="/guides/advanced-features/scene-elements">
    Add badges, arrows, and decorative text alongside your media
  </Card>

  <Card title="Background Color Overlay" icon="droplet" href="/guides/advanced-features/color-overlay">
    Tint the whole scene background for readability and branding
  </Card>

  <Card title="AI-Generated Visuals" icon="wand-magic-sparkles" href="/guides/ai-generated-visuals/background-images">
    Use the same AI models for full scene backgrounds
  </Card>

  <Card title="Get Project by ID" icon="folder-open" href="/api-reference/projects/get-project-by-id">
    See how elements appear in the saved project structure
  </Card>
</CardGroup>
