> ## Documentation Index
> This page is part of the Image and Video APIs product. Fetch the complete documentation index for Image and Video APIs at: https://cloudinary.com/documentation/llms-image-and-video-apis.txt?referrer=docpage and then use it to discover all relevant pages before exploring further.
> If your task extends beyond this product, fetch the top-level index covering all Cloudinary products and topics at: https://cloudinary.com/documentation/llms.txt?referrer=docpage

# Placing layers on videos

[underlays]: video_layer_watermarking#image_underlays
[special_layer_applications]: video_layer_watermarking#special_layer_applications
[layer_placement]: video_layer_placement
[layer_transformations]: video_layer_transformations
[styling_parameters]: video_text_layers#styling_parameters
[text_layer_options]: video_text_layers#text_layer_options
[embedding_in_SDKs]: video_transformations#embedding_images_in_web_pages_using_sdks

[embedding_in_SDKs]: video_manipulation_and_delivery#embedding_videos_in_web_pages_using_sdks


Cloudinary allows you to dynamically add layers to specific locations within your videos, where the new layers are added over the base video as overlays, and can also be easily transformed to suit your needs. There are multiple options for adding a new layer to a base video, either an asset uploaded to Cloudinary, a remote asset, or a text string. 

Video layers can also be added as [underlays][underlays] instead, and there are [special layer applications][special_layer_applications] for using layers in combination with other Cloudinary transformations.

Here are examples of some popular use cases that you can accomplish using layers (combined with other transformations):

  
    
    
    
  
Add video overlays<code>l_video:

  
    
    
    
  
Add image overlays<code>l_

  
    
    
    
  
Add text overlays<code>l_text:

This page covers the layer syntax and the overlay types you can apply. To learn how to position layers, see [Layer placement](video_layer_placement). For text options, see [Text layer options](video_text_layers). For transformations and relative sizing, see [Transforming layers](video_layer_transformations). For underlays, watermarking, and special effects, see [Underlays, watermarking, and special effects](video_layer_watermarking).

## Layer transormation syntax

In its most simple form, adding a layer over the base video takes following URL syntax:

```
l_<public id of overlay>/fl_layer_apply
```

The `layer` parameter is in its own URL component and starts the overlay definition (similar to an open bracket). The `layer_apply` flag is in a separate component that closes the definition (similar to a closing bracket) and instructs Cloudinary to place it.

> **NOTE**: Replace any forward slashes in the public ID of the overlay with colons.

You can enhance your layer both by controlling where and how it's [placed][layer_placement] on the base video using **gravity**, **offset** and other placement qualifiers, and by applying [transformations][layer_transformations] to the layered asset, using the following general URL syntax.

```
l_<public_id of layer>/<optional layer transformations>/fl_layer_apply,<optional placement qualifiers>
```

### Authenticated or private layers

You can add asset overlays that are set to `authenticated` or `private` by modifying the syntax:

**Image overlays**:

* For private image layers: `l_private:<public_id of layer>`
* For authenticated image layers: `l_authenticated:<public_id of layer>`

**Video overlays**:

* For private video layers: `l_video:private:<public_id of layer>`
* For authenticated video layers: `l_video:authenticated:<public_id of layer>`

**Audio overlays**:

* For private audio layers: `l_audio:private:<public_id of layer>`
* For authenticated audio layers: `l_audio:authenticated:<public_id of layer>`

> **INFO**: You can only add overlays that are set to `authenticated` or `private` if you also sign the whole URL (no separate signature is required for the overlay part). See the [Media access control](control_access_to_media) documentation for more details on delivering private and authenticated assets.

## Image overlays

The default overlay type is an image. For example, adding an overlay of a logo to a base video (`l_docs:logo-semi-opaque/fl_layer_apply`):

![Image with overlay](https://res.cloudinary.com/demo/image/upload/l_docs:logo-semi-opaque/fl_layer_apply/docs/suit_man_forest "thumb: w_500")

```nodejs
cloudinary.image("docs/suit_man_forest", {transformation: [
  {overlay: "docs:logo-semi-opaque"},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/suit_man_forest").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/suit_man_forest").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/suit_man_forest").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/suit_man_forest").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```python
CloudinaryImage("docs/suit_man_forest").image(transformation=[
  {'overlay': "docs:logo-semi-opaque"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new ImageTag('docs/suit_man_forest'))
	->overlay(Overlay::source(
	Source::image("docs/logo-semi-opaque")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new Layer().publicId("docs:logo-semi-opaque")).chain()
  .flags("layer_apply")).imageTag("docs/suit_man_forest");
```

```ruby
cl_image_tag("docs/suit_man_forest", transformation: [
  {overlay: "docs:logo-semi-opaque"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Overlay(new Layer().PublicId("docs:logo-semi-opaque")).Chain()
  .Flags("layer_apply")).BuildImageTag("docs/suit_man_forest")
```

```dart
cloudinary.image('docs/suit_man_forest').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setOverlay("docs:logo-semi-opaque").chain()
  .setFlags("layer_apply")).generate("docs/suit_man_forest")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new Layer().publicId("docs:logo-semi-opaque")).chain()
  .flags("layer_apply")).generate("docs/suit_man_forest");
```

```flutter
cloudinary.image('docs/suit_man_forest').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))));
```

```kotlin
cloudinary.image {
	publicId("docs/suit_man_forest")
	 overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))) 
}.generate()
```

```jquery
$.cloudinary.image("docs/suit_man_forest", {transformation: [
  {overlay: new cloudinary.Layer().publicId("docs:logo-semi-opaque")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/suit_man_forest").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

![Video with image overlay](https://res.cloudinary.com/demo/video/upload/l_docs:logo-semi-opaque/fl_layer_apply/glide-over-coastal-beach.mp4 "thumb: w_600")

```nodejs
cloudinary.video("glide-over-coastal-beach", {transformation: [
  {overlay: "docs:logo-semi-opaque"},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("glide-over-coastal-beach.mp4").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("glide-over-coastal-beach.mp4").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("glide-over-coastal-beach.mp4").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("glide-over-coastal-beach.mp4").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

```python
CloudinaryVideo("glide-over-coastal-beach").video(transformation=[
  {'overlay': "docs:logo-semi-opaque"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new VideoTag('glide-over-coastal-beach.mp4'))
	->overlay(Overlay::source(
	Source::image("docs/logo-semi-opaque")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new Layer().publicId("docs:logo-semi-opaque")).chain()
  .flags("layer_apply")).videoTag("glide-over-coastal-beach");
```

```ruby
cl_video_tag("glide-over-coastal-beach", transformation: [
  {overlay: "docs:logo-semi-opaque"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new Layer().PublicId("docs:logo-semi-opaque")).Chain()
  .Flags("layer_apply")).BuildVideoTag("glide-over-coastal-beach")
```

```dart
cloudinary.video('glide-over-coastal-beach.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("docs:logo-semi-opaque").chain()
  .setFlags("layer_apply")).generate("glide-over-coastal-beach.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new Layer().publicId("docs:logo-semi-opaque")).chain()
  .flags("layer_apply")).resourceType("video").generate("glide-over-coastal-beach.mp4");
```

```flutter
cloudinary.video('glide-over-coastal-beach.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))));
```

```kotlin
cloudinary.video {
	publicId("glide-over-coastal-beach.mp4")
	 overlay(Overlay.source(
	Source.image("docs/logo-semi-opaque"))) 
}.generate()
```

```jquery
$.cloudinary.video("glide-over-coastal-beach", {transformation: [
  {overlay: new cloudinary.Layer().publicId("docs:logo-semi-opaque")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { image } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("glide-over-coastal-beach.mp4").overlay(
  source(image("docs/logo-semi-opaque"))
);
```

> **INFO**: If the public ID of an image includes slashes (e.g., the public ID of an image is `animals/dog`), replace the slashes with colons when using the image as an overlay (e.g., the public ID of the overlay image becomes `animals:dog` when used as an overlay).

**See full syntax**: [l_\<image id\>](transformation_reference#l_image_id) in the _Transformation Reference_.

**See also**: [Fade overlays in and out](video_playback_effects#fade_in_out). 

## Remote image overlays

Use a remote image (an image not stored in your Cloudinary product environment) as an overlay by adding the `fetch` (or `url` for some SDKs) property of the `layer` parameter ( `l_fetch:` in URLs) and the base64 encoded URL of the remote image.

> **NOTE**:
>
> You can only fetch remote **images** for overlays. Fetching remote videos or audio files for use as overlays isn't supported.

The general URL syntax for adding a remote image as an overlay takes the following form:

```
l_fetch:<URL of overlay>/<optional transformations>/fl_layer_apply,<optional placement qualifiers>
```

For example, adding an overlay of the remote image `https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png` to the base video.

![Image with remote overlay](https://res.cloudinary.com/demo/image/upload/l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply/docs/office_desk "thumb: w_500")

```nodejs
cloudinary.image("docs/office_desk", {transformation: [
  {overlay: {url: "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/office_desk").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/office_desk").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/office_desk").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/office_desk").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```python
CloudinaryImage("docs/office_desk").image(transformation=[
  {'overlay': {'url': "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new ImageTag('docs/office_desk'))
	->overlay(Overlay::source(
	Source::fetch("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).chain()
  .flags("layer_apply")).imageTag("docs/office_desk");
```

```ruby
cl_image_tag("docs/office_desk", transformation: [
  {overlay: {url: "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Overlay(new FetchLayer("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).Chain()
  .Flags("layer_apply")).BuildImageTag("docs/office_desk")
```

```dart
cloudinary.image('docs/office_desk').transformation(Transformation()
	.addTransformation("l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setOverlay("fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw").chain()
  .setFlags("layer_apply")).generate("docs/office_desk")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).chain()
  .flags("layer_apply")).generate("docs/office_desk");
```

```flutter
cloudinary.image('docs/office_desk').transformation(Transformation()
	.addTransformation("l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply"));
```

```kotlin
cloudinary.image {
	publicId("docs/office_desk")
	 overlay(Overlay.source(
	Source.fetch("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"))) 
}.generate()
```

```jquery
$.cloudinary.image("docs/office_desk", {transformation: [
  {overlay: new cloudinary.FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryImage("docs/office_desk").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

![Video with remote overlay](https://res.cloudinary.com/demo/video/upload/l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply/blue_sports_car.mp4 "thumb: w_500")

```nodejs
cloudinary.video("blue_sports_car", {transformation: [
  {overlay: {url: "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("blue_sports_car.mp4").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("blue_sports_car.mp4").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("blue_sports_car.mp4").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("blue_sports_car.mp4").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

```python
CloudinaryVideo("blue_sports_car").video(transformation=[
  {'overlay': {'url': "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new VideoTag('blue_sports_car.mp4'))
	->overlay(Overlay::source(
	Source::fetch("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).chain()
  .flags("layer_apply")).videoTag("blue_sports_car");
```

```ruby
cl_video_tag("blue_sports_car", transformation: [
  {overlay: {url: "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new FetchLayer("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).Chain()
  .Flags("layer_apply")).BuildVideoTag("blue_sports_car")
```

```dart
cloudinary.video('blue_sports_car.mp4').transformation(Transformation()
	.addTransformation("l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply"));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw").chain()
  .setFlags("layer_apply")).generate("blue_sports_car.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")).chain()
  .flags("layer_apply")).resourceType("video").generate("blue_sports_car.mp4");
```

```flutter
cloudinary.video('blue_sports_car.mp4').transformation(Transformation()
	.addTransformation("l_fetch:aHR0cHM6Ly9yZXMuY2xvdWRpbmFyeS5jb20vZGVtby9pbWFnZS91cGxvYWQvbG9nb3MvY2xvdWRpbmFyeV9pY29uX3doaXRlLnBuZw/fl_layer_apply"));
```

```kotlin
cloudinary.video {
	publicId("blue_sports_car.mp4")
	 overlay(Overlay.source(
	Source.fetch("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"))) 
}.generate()
```

```jquery
$.cloudinary.video("blue_sports_car", {transformation: [
  {overlay: new cloudinary.FetchLayer().url("https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { fetch } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("blue_sports_car.mp4").overlay(
  source(
    fetch(
      "https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png"
    )
  )
);
```

> **NOTE**: The Cloudinary SDKs automatically generate a base64 encoded URL for a given HTTP/S URL, but if you generate the delivery URL in your own code, then you'll need to supply the fetch URL in base64 encoding with padding.

**See full syntax**: [l_fetch](transformation_reference#l_fetch) in the _Transformation Reference_.
## Video overlays

Add another video as an overlay over the base video by using the overlay video parameter (`l_video:` in URLs) and the public ID of a previously uploaded video (e.g. `l_video:dog` for a video with the public ID of `dog`). 

You can enhance your layer both by controlling when the video overlay is displayed by using any combination of the 3 offset parameters (see [Trimming videos](video_trimming#trimming_videos) for more information on the parameters and their possible values). You can also decide where and how it is [placed](video_layer_placement) on the base video using gravity, offset and other placement qualifiers, and by applying [transformations](video_layer_transformations) to the layered asset, with the following general syntax.

```
l_video:<public_id of layer>/<optional layer transformations>/fl_layer_apply,<optional placement qualifiers>
```

For example, adding an overlay of a video that fades in after 2.5 seconds and fades out 7.5 seconds later. The overlay is also positioned with a gravity of 'south east' and scaled to a width of 500 pixels. 

![man_on_phone.mp4 with overlay from 2.5s - 8s](https://res.cloudinary.com/demo/video/upload/l_video:man_talking/du_7.5/c_scale,w_500/e_fade:1000/e_fade:-1000/fl_layer_apply,g_south_east,so_2.5/man_on_phone.mp4 "thumb: h_500")

```nodejs
cloudinary.video("man_on_phone", {transformation: [
  {overlay: "video:man_talking"},
  {duration: "7.5"},
  {width: 500, crop: "scale"},
  {effect: "fade:1000"},
  {effect: "fade:-1000"},
  {gravity: "south_east", start_offset: "2.5", flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { trim } from "@cloudinary/url-gen/actions/videoEdit";
import { scale } from "@cloudinary/url-gen/actions/resize";
import { fadeIn, fadeOut } from "@cloudinary/url-gen/actions/effect";
import { video } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("man_on_phone.mp4").overlay(
  source(
    video("man_talking").transformation(
      new Transformation()
        .videoEdit(trim().duration(7.5))
        .resize(scale().width(500))
        .effect(fadeIn().duration(1000))
        .effect(fadeOut().duration(1000))
    )
  )
    .position(new Position().gravity(compass("south_east")))
    .timeline(position().startOffset(2.5))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { trim } from "@cloudinary/url-gen/actions/videoEdit";
import { scale } from "@cloudinary/url-gen/actions/resize";
import { fadeIn, fadeOut } from "@cloudinary/url-gen/actions/effect";
import { video } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("man_on_phone.mp4").overlay(
  source(
    video("man_talking").transformation(
      new Transformation()
        .videoEdit(trim().duration(7.5))
        .resize(scale().width(500))
        .effect(fadeIn().duration(1000))
        .effect(fadeOut().duration(1000))
    )
  )
    .position(new Position().gravity(compass("south_east")))
    .timeline(position().startOffset(2.5))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { trim } from "@cloudinary/url-gen/actions/videoEdit";
import { scale } from "@cloudinary/url-gen/actions/resize";
import { fadeIn, fadeOut } from "@cloudinary/url-gen/actions/effect";
import { video } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("man_on_phone.mp4").overlay(
  source(
    video("man_talking").transformation(
      new Transformation()
        .videoEdit(trim().duration(7.5))
        .resize(scale().width(500))
        .effect(fadeIn().duration(1000))
        .effect(fadeOut().duration(1000))
    )
  )
    .position(new Position().gravity(compass("south_east")))
    .timeline(position().startOffset(2.5))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { trim } from "@cloudinary/url-gen/actions/videoEdit";
import { scale } from "@cloudinary/url-gen/actions/resize";
import { fadeIn, fadeOut } from "@cloudinary/url-gen/actions/effect";
import { video } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("man_on_phone.mp4").overlay(
  source(
    video("man_talking").transformation(
      new Transformation()
        .videoEdit(trim().duration(7.5))
        .resize(scale().width(500))
        .effect(fadeIn().duration(1000))
        .effect(fadeOut().duration(1000))
    )
  )
    .position(new Position().gravity(compass("south_east")))
    .timeline(position().startOffset(2.5))
);
```

```python
CloudinaryVideo("man_on_phone").video(transformation=[
  {'overlay': "video:man_talking"},
  {'duration': "7.5"},
  {'width': 500, 'crop': "scale"},
  {'effect': "fade:1000"},
  {'effect': "fade:-1000"},
  {'gravity': "south_east", 'start_offset': "2.5", 'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\Resize;
use Cloudinary\Transformation\Effect;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\Position;
use Cloudinary\Transformation\Gravity;
use Cloudinary\Transformation\Compass;

(new VideoTag('man_on_phone.mp4'))
	->overlay(Overlay::source(
	Source::video("man_talking")
	->transformation((new Transformation())
	->videoEdit(VideoEdit::trim()->duration(7.5))
	->resize(Resize::scale()->width(500))
	->effect(Effect::fadeIn()->duration(1000))
	->effect(Effect::fadeOut()->duration(1000)))
	)
	->position((new Position())
	->gravity(
	Gravity::compass(
	Compass::southEast()))
	)
	->timeline(
	Timeline::position()->startOffset(2.5))
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new Layer().publicId("video:man_talking")).chain()
  .duration("7.5").chain()
  .width(500).crop("scale").chain()
  .effect("fade:1000").chain()
  .effect("fade:-1000").chain()
  .gravity("south_east").startOffset("2.5").flags("layer_apply")).videoTag("man_on_phone");
```

```ruby
cl_video_tag("man_on_phone", transformation: [
  {overlay: "video:man_talking"},
  {duration: "7.5"},
  {width: 500, crop: "scale"},
  {effect: "fade:1000"},
  {effect: "fade:-1000"},
  {gravity: "south_east", start_offset: "2.5", flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new Layer().PublicId("video:man_talking")).Chain()
  .Duration("7.5").Chain()
  .Width(500).Crop("scale").Chain()
  .Effect("fade:1000").Chain()
  .Effect("fade:-1000").Chain()
  .Gravity("south_east").StartOffset("2.5").Flags("layer_apply")).BuildVideoTag("man_on_phone")
```

```dart
cloudinary.video('man_on_phone.mp4').transformation(Transformation()
	.addTransformation("l_video:man_talking/du_7.5/c_scale,w_500/e_fade:1000/e_fade:-1000/fl_layer_apply,g_south_east,so_2.5"));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("video:man_talking").chain()
  .setDuration("7.5").chain()
  .setWidth(500).setCrop("scale").chain()
  .setEffect("fade:1000").chain()
  .setEffect("fade:-1000").chain()
  .setGravity("south_east").setStartOffset("2.5").setFlags("layer_apply")).generate("man_on_phone.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new Layer().publicId("video:man_talking")).chain()
  .duration("7.5").chain()
  .width(500).crop("scale").chain()
  .effect("fade:1000").chain()
  .effect("fade:-1000").chain()
  .gravity("south_east").startOffset("2.5").flags("layer_apply")).resourceType("video").generate("man_on_phone.mp4");
```

```flutter
cloudinary.video('man_on_phone.mp4').transformation(Transformation()
	.addTransformation("l_video:man_talking/du_7.5/c_scale,w_500/e_fade:1000/e_fade:-1000/fl_layer_apply,g_south_east,so_2.5"));
```

```kotlin
cloudinary.video {
	publicId("man_on_phone.mp4")
	 overlay(Overlay.source(
	Source.video("man_talking") {
	 transformation(Transformation {
	 videoEdit(VideoEdit.trim() { duration(7.5F) })
	 resize(Resize.scale() { width(500) })
	 effect(Effect.fadeIn() { duration(1000) })
	 effect(Effect.fadeOut() { duration(1000) }) })
	 }) {
	 position(Position() {
	 gravity(
	Gravity.compass(
	Compass.southEast()))
	 })
	 timeline(
	Timeline.position() { startOffset(2.5F) })
	 }) 
}.generate()
```

```jquery
$.cloudinary.video("man_on_phone", {transformation: [
  {overlay: new cloudinary.Layer().publicId("video:man_talking")},
  {duration: "7.5"},
  {width: 500, crop: "scale"},
  {effect: "fade:1000"},
  {effect: "fade:-1000"},
  {gravity: "south_east", start_offset: "2.5", flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { trim } from "@cloudinary/url-gen/actions/videoEdit";
import { scale } from "@cloudinary/url-gen/actions/resize";
import { fadeIn, fadeOut } from "@cloudinary/url-gen/actions/effect";
import { video } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("man_on_phone.mp4").overlay(
  source(
    video("man_talking").transformation(
      new Transformation()
        .videoEdit(trim().duration(7.5))
        .resize(scale().width(500))
        .effect(fadeIn().duration(1000))
        .effect(fadeOut().duration(1000))
    )
  )
    .position(new Position().gravity(compass("south_east")))
    .timeline(position().startOffset(2.5))
);
```

> **INFO**:
>
> * If the public ID of a video includes slashes (e.g., the public ID of a video is `animals/cat`), replace the slashes with colons when using the video as an overlay (e.g., the public ID of the video becomes `animals:cat` when used as an overlay).

> * When delivering public videos, you can only add other public videos as overlays. See the [Media access control](control_access_to_media) documentation for more details on delivering private and authenticated assets.

**See full syntax**: [l_video](transformation_reference#l_video) in the _Transformation Reference_.

## Audio overlays

Add an additional audio track over the existing video using the audio overlay parameter (`l_audio` in URLs) and the public ID of a video or audio file stored in your product environment.

If you supply a video as the overlay, only the audio track will be overlaid. The visual part is discarded.

You can add audio tracks alongside the existing audio, such as adding a voiceover or music. You can also control which parts of the layered audio file to play using the 3 timing offset parameters (see [Trimming videos](video_trimming#trimming_videos)). You can also control the volume using the [volume effect](audio_transformations#adjust_the_audio_volume).

Here's an example of adding an MP3 music file with the public ID, `electronic` to the `hourglass_timer` base video:

![hourglass_timer.mp4 with music overlay](https://res.cloudinary.com/demo/video/upload/l_audio:electronic/fl_layer_apply/hourglass_timer.mp4 "thumb: w_500, muted: false")

```nodejs
cloudinary.video("hourglass_timer", {transformation: [
  {overlay: "audio:electronic"},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("hourglass_timer.mp4").overlay(source(audio("electronic")));
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("hourglass_timer.mp4").overlay(source(audio("electronic")));
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("hourglass_timer.mp4").overlay(source(audio("electronic")));
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("hourglass_timer.mp4").overlay(source(audio("electronic")));
```

```python
CloudinaryVideo("hourglass_timer").video(transformation=[
  {'overlay': "audio:electronic"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new VideoTag('hourglass_timer.mp4'))
	->overlay(Overlay::source(
	Source::audio("electronic")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new Layer().publicId("audio:electronic")).chain()
  .flags("layer_apply")).videoTag("hourglass_timer");
```

```ruby
cl_video_tag("hourglass_timer", transformation: [
  {overlay: "audio:electronic"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new Layer().PublicId("audio:electronic")).Chain()
  .Flags("layer_apply")).BuildVideoTag("hourglass_timer")
```

```dart
cloudinary.video('hourglass_timer.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.audio("electronic"))));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("audio:electronic").chain()
  .setFlags("layer_apply")).generate("hourglass_timer.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new Layer().publicId("audio:electronic")).chain()
  .flags("layer_apply")).resourceType("video").generate("hourglass_timer.mp4");
```

```flutter
cloudinary.video('hourglass_timer.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.audio("electronic"))));
```

```kotlin
cloudinary.video {
	publicId("hourglass_timer.mp4")
	 overlay(Overlay.source(
	Source.audio("electronic"))) 
}.generate()
```

```jquery
$.cloudinary.video("hourglass_timer", {transformation: [
  {overlay: new cloudinary.Layer().publicId("audio:electronic")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("hourglass_timer.mp4").overlay(source(audio("electronic")));
```

It's also possible to remove or replace the existing audio track from the base video. To remove the existing audio track, set the audio codec to `none` (`ac_none` in URLs). You can then replace this with your own audio by adding it as an audio overlay as shown above.

Here's an example of replacing the original audio of the video with the `electronic` MP3 file and reducing the volume by 50%:

![video with replaced music](https://res.cloudinary.com/demo/video/upload/ac_none/l_audio:electronic/e_volume:-50/fl_layer_apply/docs/drive-colorful-trees-waterside.mp4 "thumb: w_500, muted: false")

```nodejs
cloudinary.video("docs/drive-colorful-trees-waterside", {transformation: [
  {audio_codec: "none"},
  {overlay: "audio:electronic"},
  {effect: "volume:-50"},
  {flags: "layer_apply"}
  ]})
```

```react
import { audioCodec } from "@cloudinary/url-gen/actions/transcode";
import { source } from "@cloudinary/url-gen/actions/overlay";
import { volume } from "@cloudinary/url-gen/actions/videoEdit";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4")
  .transcode(audioCodec("none"))
  .overlay(
    source(
      audio("electronic").transformation(
        new Transformation().videoEdit(volume(-50))
      )
    )
  );
```

```vue
import { audioCodec } from "@cloudinary/url-gen/actions/transcode";
import { source } from "@cloudinary/url-gen/actions/overlay";
import { volume } from "@cloudinary/url-gen/actions/videoEdit";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4")
  .transcode(audioCodec("none"))
  .overlay(
    source(
      audio("electronic").transformation(
        new Transformation().videoEdit(volume(-50))
      )
    )
  );
```

```angular
import { audioCodec } from "@cloudinary/url-gen/actions/transcode";
import { source } from "@cloudinary/url-gen/actions/overlay";
import { volume } from "@cloudinary/url-gen/actions/videoEdit";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4")
  .transcode(audioCodec("none"))
  .overlay(
    source(
      audio("electronic").transformation(
        new Transformation().videoEdit(volume(-50))
      )
    )
  );
```

```js
import { audioCodec } from "@cloudinary/url-gen/actions/transcode";
import { source } from "@cloudinary/url-gen/actions/overlay";
import { volume } from "@cloudinary/url-gen/actions/videoEdit";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4")
  .transcode(audioCodec("none"))
  .overlay(
    source(
      audio("electronic").transformation(
        new Transformation().videoEdit(volume(-50))
      )
    )
  );
```

```python
CloudinaryVideo("docs/drive-colorful-trees-waterside").video(transformation=[
  {'audio_codec': "none"},
  {'overlay': "audio:electronic"},
  {'effect': "volume:-50"},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Transcode;
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\VideoEdit;
use Cloudinary\Transformation\AudioCodec;
use Cloudinary\Transformation\Source;

(new VideoTag('docs/drive-colorful-trees-waterside.mp4'))
	->transcode(Transcode::audioCodec(
	AudioCodec::none()))
	->overlay(Overlay::source(
	Source::audio("electronic")
	->transformation((new Transformation())
	->videoEdit(VideoEdit::volume(-50)))
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .audioCodec("none").chain()
  .overlay(new Layer().publicId("audio:electronic")).chain()
  .effect("volume:-50").chain()
  .flags("layer_apply")).videoTag("docs/drive-colorful-trees-waterside");
```

```ruby
cl_video_tag("docs/drive-colorful-trees-waterside", transformation: [
  {audio_codec: "none"},
  {overlay: "audio:electronic"},
  {effect: "volume:-50"},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .AudioCodec("none").Chain()
  .Overlay(new Layer().PublicId("audio:electronic")).Chain()
  .Effect("volume:-50").Chain()
  .Flags("layer_apply")).BuildVideoTag("docs/drive-colorful-trees-waterside")
```

```dart
cloudinary.video('docs/drive-colorful-trees-waterside.mp4').transformation(Transformation()
	.transcode(Transcode.audioCodec(
	AudioCodec.none()))
	.overlay(Overlay.source(
	Source.audio("electronic")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.volume(-50)))
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setAudioCodec("none").chain()
  .setOverlay("audio:electronic").chain()
  .setEffect("volume:-50").chain()
  .setFlags("layer_apply")).generate("docs/drive-colorful-trees-waterside.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .audioCodec("none").chain()
  .overlay(new Layer().publicId("audio:electronic")).chain()
  .effect("volume:-50").chain()
  .flags("layer_apply")).resourceType("video").generate("docs/drive-colorful-trees-waterside.mp4");
```

```flutter
cloudinary.video('docs/drive-colorful-trees-waterside.mp4').transformation(Transformation()
	.transcode(Transcode.audioCodec(
	AudioCodec.none()))
	.overlay(Overlay.source(
	Source.audio("electronic")
	.transformation(new Transformation()
	.videoEdit(VideoEdit.volume(-50)))
	)));
```

```kotlin
cloudinary.video {
	publicId("docs/drive-colorful-trees-waterside.mp4")
	 transcode(Transcode.audioCodec(
	AudioCodec.none()))
	 overlay(Overlay.source(
	Source.audio("electronic") {
	 transformation(Transformation {
	 videoEdit(VideoEdit.volume(-50)) })
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("docs/drive-colorful-trees-waterside", {transformation: [
  {audio_codec: "none"},
  {overlay: new cloudinary.Layer().publicId("audio:electronic")},
  {effect: "volume:-50"},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { audioCodec } from "@cloudinary/url-gen/actions/transcode";
import { source } from "@cloudinary/url-gen/actions/overlay";
import { volume } from "@cloudinary/url-gen/actions/videoEdit";
import { audio } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4")
  .transcode(audioCodec("none"))
  .overlay(
    source(
      audio("electronic").transformation(
        new Transformation().videoEdit(volume(-50))
      )
    )
  );
```

> **TIP**: For videos delivered with [automatic streaming profile selection](adaptive_bitrate_streaming#automatic_streaming_profile_selection), you can also define multiple [alternate audio tracks](adaptive_bitrate_streaming#defining_alternate_audio_tracks) that will be included in the resulting manifest. This allows for switching of audio tracks when used with a video player.

**See full syntax**: [l_audio](transformation_reference#l_audio) in the _Transformation Reference_.

**See also**: [Mixing audio tracks](audio_transformations#mixing_audio_tracks), [Defining alternate audio tracks](adaptive_bitrate_streaming#defining_alternate_audio_tracks).

## Text overlays

Add a text overlay over the base video with the `text` property of the `layer` parameter ( `l_text:` in URLs). The parameter also requires specifying font family and size (separated with an underscore and followed by a colon), and the text string to display. The general URL syntax for adding a text layer takes the following form:

```
l_text:<text styling options>/<optional transformations>/fl_layer_apply,<optional placement qualifiers>
```

In addition to the required font and size styling values, you can optionally specify a variety of other CSS-like [styling parameters][styling_parameters] and to further customize your text layers by specifying text color, adding line breaks, emojis and other special characters, and other [text layer options][text_layer_options].

Cloudinary first generates an image from the text definition and then overlays it like any other image overlay, and thus supports all the same [transformations][layer_transformations] that any image overlay supports.

For example, to overlay the text string "Coffee" in the Arial font with a size of 80 pixels (`l_text:Arial_80:Coffee/fl_layer_apply`):

![Adding dynamic text to image](https://res.cloudinary.com/demo/image/upload/l_text:Arial_80:Coffee/fl_layer_apply/docs/cappuccino.jpg "thumb: w_500")

```nodejs
cloudinary.image("docs/cappuccino.jpg", {transformation: [
  {overlay: {font_family: "Arial", font_size: 80, text: "Coffee"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryImage("docs/cappuccino.jpg").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryImage("docs/cappuccino.jpg").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryImage("docs/cappuccino.jpg").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryImage("docs/cappuccino.jpg").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```python
CloudinaryImage("docs/cappuccino.jpg").image(transformation=[
  {'overlay': {'font_family': "Arial", 'font_size': 80, 'text': "Coffee"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\TextStyle;

(new ImageTag('docs/cappuccino.jpg'))
	->overlay(Overlay::source(
	Source::text("Coffee",(new TextStyle("Arial",80)))));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")).chain()
  .flags("layer_apply")).imageTag("docs/cappuccino.jpg");
```

```ruby
cl_image_tag("docs/cappuccino.jpg", transformation: [
  {overlay: {font_family: "Arial", font_size: 80, text: "Coffee"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Overlay(new TextLayer().FontFamily("Arial").FontSize(80).Text("Coffee")).Chain()
  .Flags("layer_apply")).BuildImageTag("docs/cappuccino.jpg")
```

```dart
cloudinary.image('docs/cappuccino.jpg').transformation(Transformation()
	.addTransformation("l_text:Arial_80:Coffee/fl_layer_apply"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setOverlay("text:Arial_80:Coffee").chain()
  .setFlags("layer_apply")).generate("docs/cappuccino.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")).chain()
  .flags("layer_apply")).generate("docs/cappuccino.jpg");
```

```flutter
cloudinary.image('docs/cappuccino.jpg').transformation(Transformation()
	.addTransformation("l_text:Arial_80:Coffee/fl_layer_apply"));
```

```kotlin
cloudinary.image {
	publicId("docs/cappuccino.jpg")
	 overlay(Overlay.source(
	Source.text("Coffee",TextStyle("Arial",80)))) 
}.generate()
```

```jquery
$.cloudinary.image("docs/cappuccino.jpg", {transformation: [
  {overlay: new cloudinary.TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryImage("docs/cappuccino.jpg").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

![Adding dynamic text to video](https://res.cloudinary.com/demo/video/upload/l_text:Arial_80:Coffee/fl_layer_apply/coffee_drinking.mp4 "thumb: w_500")

```nodejs
cloudinary.video("coffee_drinking", {transformation: [
  {overlay: {font_family: "Arial", font_size: 80, text: "Coffee"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("coffee_drinking.mp4").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("coffee_drinking.mp4").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("coffee_drinking.mp4").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("coffee_drinking.mp4").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

```python
CloudinaryVideo("coffee_drinking").video(transformation=[
  {'overlay': {'font_family': "Arial", 'font_size': 80, 'text': "Coffee"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\TextStyle;

(new VideoTag('coffee_drinking.mp4'))
	->overlay(Overlay::source(
	Source::text("Coffee",(new TextStyle("Arial",80)))));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")).chain()
  .flags("layer_apply")).videoTag("coffee_drinking");
```

```ruby
cl_video_tag("coffee_drinking", transformation: [
  {overlay: {font_family: "Arial", font_size: 80, text: "Coffee"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new TextLayer().FontFamily("Arial").FontSize(80).Text("Coffee")).Chain()
  .Flags("layer_apply")).BuildVideoTag("coffee_drinking")
```

```dart
cloudinary.video('coffee_drinking.mp4').transformation(Transformation()
	.addTransformation("l_text:Arial_80:Coffee/fl_layer_apply"));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("text:Arial_80:Coffee").chain()
  .setFlags("layer_apply")).generate("coffee_drinking.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")).chain()
  .flags("layer_apply")).resourceType("video").generate("coffee_drinking.mp4");
```

```flutter
cloudinary.video('coffee_drinking.mp4').transformation(Transformation()
	.addTransformation("l_text:Arial_80:Coffee/fl_layer_apply"));
```

```kotlin
cloudinary.video {
	publicId("coffee_drinking.mp4")
	 overlay(Overlay.source(
	Source.text("Coffee",TextStyle("Arial",80)))) 
}.generate()
```

```jquery
$.cloudinary.video("coffee_drinking", {transformation: [
  {overlay: new cloudinary.TextLayer().fontFamily("Arial").fontSize(80).text("Coffee")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { text } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("coffee_drinking.mp4").overlay(
  source(text("Coffee", new TextStyle("Arial", 80)))
);
```

**See full syntax**: [l_text](transformation_reference#l_text) in the _Transformation Reference_.

### Timed text overlays

You can control when a text overlay appears and/or disappears by adding timing qualifiers to the text layer. Use one or more of the following as part of the `layer_apply` component:

* "so_ (start offset)
* eo_ (end offset)
* du_ (duration)."

Example: Four back-to-back labels, each visible for 4 seconds:

```
l_text:Arial_34_bold:First%20text/fl_layer_apply,g_west,x_10,eo_3/
l_text:Arial_34_bold:Second%20text/fl_layer_apply,g_west,x_10,so_3,eo_6/
l_text:Arial_34_bold:Third%20text/fl_layer_apply,g_west,x_10,so_6,eo_9/
l_text:Arial_34_bold:Fourth%20text/fl_layer_apply,g_west,x_10,so_9,eo_12
```

You can also use `du_<time>` instead of specifying both `so_` and `eo_`. For full syntax, see `so` [start offset](transformation_reference#so_start_offset), `eo` [end offset](transformation_reference#eo_end_offset), and `du` [duration](transformation_reference#du_duration).

> **TIP**: :title=Tip for long URLs

If you have many timed text snippets, constructing them all with `l_text` can make URLs long. Instead, upload an SRT or WebVTT file as a raw asset and overlay it with `l_subtitles:<public_id.ext>`. This keeps the delivery URL short and centralizes your captions.
See [Uploading non-media files as raw files](upload_parameters#uploading_non_media_files_as_raw_files)

## Subtitles

You can add subtitles to videos as an embedded layer based on [SRT](https://en.wikipedia.org/wiki/SubRip) or [WebVTT](https://en.wikipedia.org/wiki/WebVTT) files that were previously uploaded to Cloudinary as [raw files](upload_parameters#uploading_non_media_files_as_raw_files). To do this, use the `subtitles:` property of the `overlay` parameter ( `l_subtitles:` in URLs), followed by the public ID (including the **.srt** or **.vtt** extension). 

For example, to overlay the subtitles in the `outdoors.vtt` file:
 
![video with subtitles from outdoors.vtt](https://res.cloudinary.com/demo/video/upload/l_subtitles:outdoors.vtt/fl_layer_apply/docs/rocky-mountains.mp4 "thumb: w_500")

```nodejs
cloudinary.video("docs/rocky-mountains", {transformation: [
  {overlay: {resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(subtitles("outdoors.vtt"))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(subtitles("outdoors.vtt"))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(subtitles("outdoors.vtt"))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(subtitles("outdoors.vtt"))
);
```

```python
CloudinaryVideo("docs/rocky-mountains").video(transformation=[
  {'overlay': {'resource_type': "subtitles", 'public_id': "outdoors.vtt"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;

(new VideoTag('docs/rocky-mountains.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("outdoors.vtt")));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).videoTag("docs/rocky-mountains");
```

```ruby
cl_video_tag("docs/rocky-mountains", transformation: [
  {overlay: {resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new SubtitlesLayer().PublicId("outdoors.vtt")).Chain()
  .Flags("layer_apply")).BuildVideoTag("docs/rocky-mountains")
```

```dart
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt"))));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("subtitles:outdoors.vtt").chain()
  .setFlags("layer_apply")).generate("docs/rocky-mountains.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).resourceType("video").generate("docs/rocky-mountains.mp4");
```

```flutter
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt"))));
```

```kotlin
cloudinary.video {
	publicId("docs/rocky-mountains.mp4")
	 overlay(Overlay.source(
	Source.subtitles("outdoors.vtt"))) 
}.generate()
```

```jquery
$.cloudinary.video("docs/rocky-mountains", {transformation: [
  {overlay: new cloudinary.SubtitlesLayer().publicId("outdoors.vtt")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(subtitles("outdoors.vtt"))
);
```

You can optionally include font and size settings (separated with an underscore) before the subtitles file name. For example, to overlay the subtitles in the `outdoors.vtt` file using the Arial font with a size of 20 pixels:
 
![video with subtitles from sample_sub_en.srt in 20px ariel](https://res.cloudinary.com/demo/video/upload/l_subtitles:arial_20:outdoors.vtt/fl_layer_apply/docs/driving-through-norway-hills.mp4 "thumb: w_500")

```nodejs
cloudinary.video("docs/driving-through-norway-hills", {transformation: [
  {overlay: {font_family: "arial", font_size: 20, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/driving-through-norway-hills.mp4").overlay(
  source(subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20)))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/driving-through-norway-hills.mp4").overlay(
  source(subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20)))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/driving-through-norway-hills.mp4").overlay(
  source(subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20)))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/driving-through-norway-hills.mp4").overlay(
  source(subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20)))
);
```

```python
CloudinaryVideo("docs/driving-through-norway-hills").video(transformation=[
  {'overlay': {'font_family': "arial", 'font_size': 20, 'resource_type': "subtitles", 'public_id': "outdoors.vtt"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\TextStyle;

(new VideoTag('docs/driving-through-norway-hills.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("outdoors.vtt")
	->textStyle((new TextStyle("arial",20)))
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).videoTag("docs/driving-through-norway-hills");
```

```ruby
cl_video_tag("docs/driving-through-norway-hills", transformation: [
  {overlay: {font_family: "arial", font_size: 20, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new SubtitlesLayer().FontFamily("arial").FontSize(20).PublicId("outdoors.vtt")).Chain()
  .Flags("layer_apply")).BuildVideoTag("docs/driving-through-norway-hills")
```

```dart
cloudinary.video('docs/driving-through-norway-hills.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",20))
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("subtitles:arial_20:outdoors.vtt").chain()
  .setFlags("layer_apply")).generate("docs/driving-through-norway-hills.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).resourceType("video").generate("docs/driving-through-norway-hills.mp4");
```

```flutter
cloudinary.video('docs/driving-through-norway-hills.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",20))
	)));
```

```kotlin
cloudinary.video {
	publicId("docs/driving-through-norway-hills.mp4")
	 overlay(Overlay.source(
	Source.subtitles("outdoors.vtt") {
	 textStyle(TextStyle("arial",20))
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("docs/driving-through-norway-hills", {transformation: [
  {overlay: new cloudinary.SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/driving-through-norway-hills.mp4").overlay(
  source(subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20)))
);
```

You can also control the color of the text by adding the `color` property (`co` in URLs) to change the fill color of the text, and adding the `background` property (`b` in URLs) to change the color of the text background. Colors can be set as an RGB hex triplet (e.g. `b_rgb:3e2222`), a 3 character RGB hex (e.g. `co_rgb:777`), a named color (e.g. `b_green`), or an RGB hex quadruplet, where the 4th value represents the opacity (e.g. `b_rgb:3e222266`). 

For example, to overlay the subtitles in the `outdoors.vtt` file using the Arial font with a size of 40 pixels, in yellow text (FFFF00) with a black background:
![video with subtitles in yellow text with a black background](https://res.cloudinary.com/demo/video/upload/b_black,co_rgb:ffff00,l_subtitles:arial_40:outdoors.vtt/fl_layer_apply/docs/drive-colorful-trees-waterside.mp4 "thumb: w_500")

```nodejs
cloudinary.video("docs/drive-colorful-trees-waterside", {transformation: [
  {background: "black", color: "#ffff00", overlay: {font_family: "arial", font_size: 40, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("arial", 40))
      .textColor("#ffff00")
      .backgroundColor("black")
  )
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("arial", 40))
      .textColor("#ffff00")
      .backgroundColor("black")
  )
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("arial", 40))
      .textColor("#ffff00")
      .backgroundColor("black")
  )
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("arial", 40))
      .textColor("#ffff00")
      .backgroundColor("black")
  )
);
```

```python
CloudinaryVideo("docs/drive-colorful-trees-waterside").video(transformation=[
  {'background': "black", 'color': "#ffff00", 'overlay': {'font_family': "arial", 'font_size': 40, 'resource_type': "subtitles", 'public_id': "outdoors.vtt"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\TextStyle;
use Cloudinary\Transformation\Color;

(new VideoTag('docs/drive-colorful-trees-waterside.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("outdoors.vtt")
	->textStyle((new TextStyle("arial",40)))
	->textColor(Color::rgb("ffff00"))
	->backgroundColor(Color::BLACK)
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .background("black").color("#ffff00").overlay(new SubtitlesLayer().fontFamily("arial").fontSize(40).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).videoTag("docs/drive-colorful-trees-waterside");
```

```ruby
cl_video_tag("docs/drive-colorful-trees-waterside", transformation: [
  {background: "black", color: "#ffff00", overlay: {font_family: "arial", font_size: 40, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Background("black").Color("#ffff00").Overlay(new SubtitlesLayer().FontFamily("arial").FontSize(40).PublicId("outdoors.vtt")).Chain()
  .Flags("layer_apply")).BuildVideoTag("docs/drive-colorful-trees-waterside")
```

```dart
cloudinary.video('docs/drive-colorful-trees-waterside.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",40))
	.textColor(Color.rgb("ffff00"))
	.backgroundColor(Color.BLACK)
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setBackground("black").setColor("#ffff00").setOverlay("subtitles:arial_40:outdoors.vtt").chain()
  .setFlags("layer_apply")).generate("docs/drive-colorful-trees-waterside.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .background("black").color("#ffff00").overlay(new SubtitlesLayer().fontFamily("arial").fontSize(40).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).resourceType("video").generate("docs/drive-colorful-trees-waterside.mp4");
```

```flutter
cloudinary.video('docs/drive-colorful-trees-waterside.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",40))
	.textColor(Color.rgb("ffff00"))
	.backgroundColor(Color.BLACK)
	)));
```

```kotlin
cloudinary.video {
	publicId("docs/drive-colorful-trees-waterside.mp4")
	 overlay(Overlay.source(
	Source.subtitles("outdoors.vtt") {
	 textStyle(TextStyle("arial",40))
	 textColor(Color.rgb("ffff00"))
	 backgroundColor(Color.BLACK)
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("docs/drive-colorful-trees-waterside", {transformation: [
  {background: "black", color: "#ffff00", overlay: new cloudinary.SubtitlesLayer().fontFamily("arial").fontSize(40).publicId("outdoors.vtt")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/drive-colorful-trees-waterside.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("arial", 40))
      .textColor("#ffff00")
      .backgroundColor("black")
  )
);
```

#### Custom fonts for subtitles

In place of a [universally available font](video_text_layer_fonts#supported_fonts) name, you can specify the public ID of a [custom font](video_text_layer_fonts) that you've uploaded to your product environment as a [raw](upload_parameters#uploading_non_media_files_as_raw_files), [authenticated](upload_parameters#authenticated_assets) file. Include the file extension (`.ttf`, `.otf` or `.woff2`) in the public ID.

> **NOTE**: For `l_subtitles`, the public ID of the uploaded font file must exactly match the font family name, for example `Alex Brush.ttf`. If the names don't match, the subtitles may render with a fallback font.

URL-encode spaces and other special characters in the font public ID.

For example, to overlay the subtitles in the `outdoors.vtt` file using the custom `Alex Brush.ttf` font at size 40, in white text on a black background:

![video with subtitles using a custom font](https://res.cloudinary.com/demo/video/upload/b_black,co_white,l_subtitles:Alex%20Brush.ttf_40:outdoors.vtt/fl_layer_apply/docs/rocky-mountains.mp4 "thumb: w_500")

```nodejs
cloudinary.video("docs/rocky-mountains", {transformation: [
  {background: "black", color: "white", overlay: {font_family: "ttf", font_size: 40, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("Alex Brush.ttf", 40))
      .textColor("white")
      .backgroundColor("black")
  )
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("Alex Brush.ttf", 40))
      .textColor("white")
      .backgroundColor("black")
  )
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("Alex Brush.ttf", 40))
      .textColor("white")
      .backgroundColor("black")
  )
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("Alex Brush.ttf", 40))
      .textColor("white")
      .backgroundColor("black")
  )
);
```

```python
CloudinaryVideo("docs/rocky-mountains").video(transformation=[
  {'background': "black", 'color': "white", 'overlay': {'font_family': "ttf", 'font_size': 40, 'resource_type': "subtitles", 'public_id': "outdoors.vtt"}},
  {'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\TextStyle;

(new VideoTag('docs/rocky-mountains.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("outdoors.vtt")
	->textStyle((new TextStyle("Alex Brush.ttf",40)))
	->textColor(Color::WHITE)
	->backgroundColor(Color::BLACK)
	));
```

```java
cloudinary.url().transformation(new Transformation()
  .background("black").color("white").overlay(new SubtitlesLayer().fontFamily("ttf").fontSize(40).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).videoTag("docs/rocky-mountains");
```

```ruby
cl_video_tag("docs/rocky-mountains", transformation: [
  {background: "black", color: "white", overlay: {font_family: "ttf", font_size: 40, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Background("black").Color("white").Overlay(new SubtitlesLayer().FontFamily("ttf").FontSize(40).PublicId("outdoors.vtt")).Chain()
  .Flags("layer_apply")).BuildVideoTag("docs/rocky-mountains")
```

```dart
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("Alex Brush.ttf",40))
	.textColor(Color.WHITE)
	.backgroundColor(Color.BLACK)
	)));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setBackground("black").setColor("white").setOverlay("subtitles:Alex%20Brush.ttf_40:outdoors.vtt").chain()
  .setFlags("layer_apply")).generate("docs/rocky-mountains.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .background("black").color("white").overlay(new SubtitlesLayer().fontFamily("ttf").fontSize(40).publicId("outdoors.vtt")).chain()
  .flags("layer_apply")).resourceType("video").generate("docs/rocky-mountains.mp4");
```

```flutter
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("Alex Brush.ttf",40))
	.textColor(Color.WHITE)
	.backgroundColor(Color.BLACK)
	)));
```

```kotlin
cloudinary.video {
	publicId("docs/rocky-mountains.mp4")
	 overlay(Overlay.source(
	Source.subtitles("outdoors.vtt") {
	 textStyle(TextStyle("Alex Brush.ttf",40))
	 textColor(Color.WHITE)
	 backgroundColor(Color.BLACK)
	 })) 
}.generate()
```

```jquery
$.cloudinary.video("docs/rocky-mountains", {transformation: [
  {background: "black", color: "white", overlay: new cloudinary.SubtitlesLayer().fontFamily("ttf").fontSize(40).publicId("outdoors.vtt")},
  {flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt")
      .textStyle(new TextStyle("Alex Brush.ttf", 40))
      .textColor("white")
      .backgroundColor("black")
  )
);
```

For font upload requirements and other guidelines, see [Custom fonts](video_text_layer_fonts).

#### Subtitle positioning

You can use compass gravity positions (e.g., `g_west`, `g_south_east`, `g_north`) to control where subtitles appear on the video. Unlike most other layers, the default gravity for subtitles is `south` (bottom of the video) rather than `center`.

For example, to position subtitles at the top of the video:

![video with subtitles positioned at the top](https://res.cloudinary.com/demo/video/upload/l_subtitles:arial_20:outdoors.vtt/g_north,fl_layer_apply/docs/rocky-mountains.mp4 "thumb: w_500")

```nodejs
cloudinary.video("docs/rocky-mountains", {transformation: [
  {overlay: {font_family: "arial", font_size: 20, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {gravity: "north", flags: "layer_apply"}
  ]})
```

```react
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20))
  ).position(new Position().gravity(compass("north")))
);
```

```vue
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20))
  ).position(new Position().gravity(compass("north")))
);
```

```angular
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20))
  ).position(new Position().gravity(compass("north")))
);
```

```js
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20))
  ).position(new Position().gravity(compass("north")))
);
```

```python
CloudinaryVideo("docs/rocky-mountains").video(transformation=[
  {'overlay': {'font_family': "arial", 'font_size': 20, 'resource_type': "subtitles", 'public_id': "outdoors.vtt"}},
  {'gravity': "north", 'flags': "layer_apply"}
  ])
```

```php
use Cloudinary\Transformation\Overlay;
use Cloudinary\Transformation\Source;
use Cloudinary\Transformation\Position;
use Cloudinary\Transformation\TextStyle;
use Cloudinary\Transformation\Gravity;
use Cloudinary\Transformation\Compass;

(new VideoTag('docs/rocky-mountains.mp4'))
	->overlay(Overlay::source(
	Source::subtitles("outdoors.vtt")
	->textStyle((new TextStyle("arial",20)))
	)
	->position((new Position())
	->gravity(
	Gravity::compass(
	Compass::north()))
	)
	);
```

```java
cloudinary.url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")).chain()
  .gravity("north").flags("layer_apply")).videoTag("docs/rocky-mountains");
```

```ruby
cl_video_tag("docs/rocky-mountains", transformation: [
  {overlay: {font_family: "arial", font_size: 20, resource_type: "subtitles", public_id: "outdoors.vtt"}},
  {gravity: "north", flags: "layer_apply"}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Overlay(new SubtitlesLayer().FontFamily("arial").FontSize(20).PublicId("outdoors.vtt")).Chain()
  .Gravity("north").Flags("layer_apply")).BuildVideoTag("docs/rocky-mountains")
```

```dart
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",20))
	)
	.position(Position()
	.gravity(
	Gravity.compass(
	Compass.north()))
	)
	));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .setOverlay("subtitles:arial_20:outdoors.vtt").chain()
  .setGravity("north").setFlags("layer_apply")).generate("docs/rocky-mountains.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .overlay(new SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")).chain()
  .gravity("north").flags("layer_apply")).resourceType("video").generate("docs/rocky-mountains.mp4");
```

```flutter
cloudinary.video('docs/rocky-mountains.mp4').transformation(Transformation()
	.overlay(Overlay.source(
	Source.subtitles("outdoors.vtt")
	.textStyle(TextStyle("arial",20))
	)
	.position(Position()
	.gravity(
	Gravity.compass(
	Compass.north()))
	)
	));
```

```kotlin
cloudinary.video {
	publicId("docs/rocky-mountains.mp4")
	 overlay(Overlay.source(
	Source.subtitles("outdoors.vtt") {
	 textStyle(TextStyle("arial",20))
	 }) {
	 position(Position() {
	 gravity(
	Gravity.compass(
	Compass.north()))
	 })
	 }) 
}.generate()
```

```jquery
$.cloudinary.video("docs/rocky-mountains", {transformation: [
  {overlay: new cloudinary.SubtitlesLayer().fontFamily("arial").fontSize(20).publicId("outdoors.vtt")},
  {gravity: "north", flags: "layer_apply"}
  ]})
```

```react_native
import { source } from "@cloudinary/url-gen/actions/overlay";
import { subtitles } from "@cloudinary/url-gen/qualifiers/source";
import { Position } from "@cloudinary/url-gen/qualifiers/position";
import { TextStyle } from "@cloudinary/url-gen/qualifiers/textStyle";
import { compass } from "@cloudinary/url-gen/qualifiers/gravity";

new CloudinaryVideo("docs/rocky-mountains.mp4").overlay(
  source(
    subtitles("outdoors.vtt").textStyle(new TextStyle("arial", 20))
  ).position(new Position().gravity(compass("north")))
);
```

> **NOTE**:
>
> The `x` and `y` offset parameters are **not supported** for subtitle layers. You can only use compass gravity values (`g_north`, `g_south`, `g_east`, `g_west`, `g_north_east`, `g_north_west`, `g_south_east`, `g_south_west`, `g_center`) to control subtitle placement. If you need more precise positioning, consider using a [text overlay](#text_overlays) instead.

**See full syntax**: [l_subtitles](transformation_reference#l_subtitles) in the _Transformation Reference_.

> **READING**:
>
> * [Layer placement](video_layer_placement): Position layers using gravity and offsets.

> * [Text layer options](video_text_layers): Text styling, color, multi-line text, and special characters.

> * [Transforming layers](video_layer_transformations): Apply transformations, multiple overlays, and relative sizing to layers.

> * [Underlays, watermarking, and special effects](video_layer_watermarking): Image underlays, watermarking, and special layer applications.
