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

# Video Player customization


[//]: # (PageTitle: Video player customization)

You can customize your video player in a number of ways using Cloudinary's customization options, or alternatively, add more advanced customizations using the [VideoJS API](#low_level_customization_with_video_js).
> **TIP**: You can also easily customize your video player using the [Video Player Studio](video_player_studio), which provides a visual interface for configuring player settings. Save settings directly to video assets or define reusable [player profiles](video_player_how_to_embed#player_profiles).

## Video player visuals

You can change the look and feel of your video player by specifying a global [skin theme](#skin_theme) and/or by controlling specific elements of the [color schemes](#color_scheme).
> **NOTE**: All transformations use the JavaScript (legacy) syntax. When looking at transformation examples in the rest of the documentation, you can change the JS tab to display the legacy (cloudinary-core) code examples.
### Skin theme

By default, the video player has a semi-transparent dark theme. The theme you select controls the color of the video player controls, controls background color, title style, and the color of the right-click context menu commands and central play button. You can change the theme skin by adding the class: `cld-video-player-skin-<value>` to the video tag. The possible values are `light` and `dark`. For example, to specify the light theme, include this in your `<video>` tag:

```js
  class="cld-video-player cld-video-player-skin-light" 
```

### Color scheme

If you would like more control over the color scheme for the player, you can customize this using the `colors` constructor parameter.

To set the color scheme for the player, you need to specify three colors (as hex values) to use for the `base`, `accent` and `text`:

* The `base` color is for the video player controls bar and information bar. It's also for the central play button and right-click context menu.
* The `accent` color is for the seek bar, volume control and for highlighting interactions with the UI, e.g. when hovering over a menu item.
* The `text` color is for all the text and icons that are present within the video player UI.
> **INFO**: To ensure compliance with the Web Content Accessibility Guidelines (WCAG) 2.1, select colors with appropriate contrast levels. See our dedicated [accessibility](video_player_accessibility) page to learn more about other Video Player accessibility features.

You can configure the color scheme as part of the `<video>` tag:

```
data-cld-colors='{ "base": "#0071ba", "accent": "#db8226", "text": "#fff" }'
```

Or using the JavaScript constructor parameter:

```js
colors: { "base": "#0071ba", "accent": "#db8226", "text": "#fff" }
```

Here's an example of a customized player using a Cloudinary color scheme and custom logo: 

> **NOTE**: The [skin theme](#skin_themes) you set affects how your color scheme displays. You should test your color scheme with both skin themes to ensure you get the desired output.

## Configuration options

You can set most configuration options either in your video tag or using JavaScript. 

This section covers:

* [Common configuration examples](#common_configuration_examples)
* [Default configuration behaviors](#default_configuration_behaviors) 
* [Configuration precedence](#configuration_precedence)

### Common configuration examples

The examples below highlight some of the available options you might want to use when configuring your video player instances

#### Define video transformations

Defining video transformations for your video player instance applies these same transformations to all video sources. The example below demonstrates a simple crop:

* In the `<video>` tag: 

 ```html
  data-cld-transformation='{ "crop": "limit", "width": 500}'>
 ```

* In the `videoPlayer`: 
	
 ```js
	 const cld = cloudinary.videoPlayer('demo-player',{
     transformation: {crop: 'limit', width: 200}
   })
 ```

You can also apply transformations to a single video by including the transformation when setting `videoPlayer.source`:

```js
cld.source('oceans', {transformation: {angle: 10}})
```

For information on available video transformations, see:

* [Video transformations guide](video_manipulation_and_delivery)
* [Transformation URL API Reference](transformation_reference)

#### Customize seek bar functionality

The standard video player seek bar has two additional configuration options available to enhance the experience for your users:

* **Seek thumbnails** show a preview of upcoming content to the user while they're seeking the video. This functionality is on by default. To disable it, set the `seekThumbnails` constructor parameter to `false`.  
* **AI-based highlights graph** shows a visual representation of the highlights of the video based on how our AI preview algorithm determines the level of interest for each part of the video. To enable this functionality, set the `aiHighlightsGraph` constructor parameter to `true`.

Here is a simple example that demonstrates both features. Hover over the seek bar to view both thumbnails and graph.

> **INFO**: Both features use additional Cloudinary functionality behind the scenes which consume additional transformations when enabled. For more information, see the transformation counts documentation for [Seek thumbnails](transformation_counts#video_player_seek_thumbnails) and [AI highlights graph](transformation_counts#video_player_ai_highlights_graph).

#### Additional common configurations

Some additional common configurations include:

* Setting the **autoplay** mode. For example, use `on-scroll` to cause the video to start playing when more than half the player is visible on screen.
* Setting a specific image as the video **poster**, including applying [image transformations](image_transformations) on the specified image.
* Adding a **floating player** when less than half the player becomes visible on screen. You can set this to appear in the bottom `left` or `right` and includes a button to close.
* Defining the preferred set of **video source types**. By default, the players uses [automatic format selection](video_optimization#automatic_format_selection_f_auto) to select the optimal file type based on the user's device and browser.
* Customizing the video player **font**. (The font gets applied to titles, descriptions, recommendations, time counter, etc.)

##### In the video tag: 

 ```html
   ...	
   loop
   controls
   data-cld-public-id="book"
   data-cld-font-face="Yatra One"
   data-cld-autoplay-mode="on-scroll"
   data-cld-floating-when-not-visible="left"
   data-cld-transformation='{ "width": 400, "crop": "limit",  
     streaming_profile": "hd" }'
   data-cld-poster-options= '{ "publicId": "mypic" "transformation": 
    {"effect": ["sepia"] } }'
   data-cld-source-types='["hls", "webm/vp9", "mp4/h265"]'
   ... 
 ```

##### As a videoPlayer method construction parameter: 

 ```js
 const cld = cloudinary.videoPlayer('demo-player',{
   publicId: 'book',
   loop: true,
   controls: true,
   autoplayMode: 'on-scroll',
   floatingWhenNotVisible: 'left',
   fontFace: 'Yatra One',
   transformation: { width: 400, crop: 'limit' },
   posterOptions: {publicId: 'mypic', transformation: { effect: ['sepia']}},
   sourceTypes: ["hls", "webm/vp9", "mp4/h265"]
 })
 ```

The above are just a few common options. For details on all available options, see the [Video Player configuration options](video_player_api_reference#configuration_options) section in the _Video Player API Reference_.

Additionally, see the [Cloudinary Video Player samples](https://cloudinary.github.io/cloudinary-video-player/) which demonstrate many of these configuration settings.

### Default configuration behaviors

Most standard HTML5 video attributes (`autoplay`, `loop`, `preload`, `muted`, etc) retain their standard default behavior (`auto` mode for `preload` and false for all others).

By default, the video player automatically uses the middle image of your video as a poster image (the equivalent of `<cloud_name>/video/<type>/<videoID>.jpg`). You can specify a different public ID and/or image transformations using the `posterOptions` (`data-cld-poster-options`) setting.

Additionally, by default, when the player requests your video, it automatically requests the best format from the default source types (`.webm`, `.ogg`, and `.mp4`). If the transformation defined already exists for the requested source type, it's delivered. Otherwise, the relevant transformation is transcoded and streamed in real time. You can override the default format options using the `sourceTypes` (`data-cld-source-types`) setting.

### Configuration precedence

You can set transformations and a number of other configurations either in the `<video>` tag or in the `videoPlayer` instance. In both cases, they define the defaults that apply to all video sources. You shouldn't include the same configuration parameter in both the tag and the JavaScript element, but if you do, the JavaScript setting takes precedence.

In general, if you set the same configuration parameter for a specific video source, those values override the values of the parallel setting for the player. 

However, if you define transformations for both the source and the player, the transformation definitions get merged. For example, you could define width, height, and text overlay transformations at the player level, and then apply some special effect transformations for a particular video source. The resulting video includes all of the above transformations.

When using [asset-saved settings](video_player_how_to_embed#asset_saved_settings) or [player profiles](video_player_how_to_embed#player_profiles), the player applies settings in the following order of precedence (highest to lowest):

1. Instance-level configurations (defined in your code when instantiating the player)
2. Asset-saved settings or profile-based configurations (these are mutually exclusive—if a profile is specified, asset-saved settings are ignored)

The cloudinary `autoPlayMode` (`data-cld-autoplay-mode`) is similar to the standard HTML5 `autoplay` parameter, but includes additional possible values. You shouldn't include both settings in your player, but if you do, the Cloudinary autoplay mode setting takes precedence. 

## Resize and crop

You can change the aspect ratio and resize behavior of your video by setting the `aspectRatio` and `cropMode` parameters. When you set an aspect ratio, the player applies a transformation to deliver the video in the specified proportions.

Available aspect ratios: `"16:9"` (landscape), `"9:16"` (portrait), `"1:1"` (square).

Available resize modes:

* `"smart"` - Keeps the most important content in view. This is the default.
* `"fill"` - Covers the frame, cropping as needed.
* `"pad"` - Fits the video within the frame and adds padding. Use `cropPadColor` to set the padding color.

```js
// Smart crop to portrait format
const player = cloudinary.videoPlayer('player', {
  aspectRatio: '9:16',
  cropMode: 'smart'
});

// Pad to square with white background
const paddedPlayer = cloudinary.videoPlayer('player', {
  aspectRatio: '1:1',
  cropMode: 'pad',
  cropPadColor: '#FFFFFF'
});
```

> **NOTE**: Resize and crop settings apply only to progressive delivery. They aren't available with adaptive streaming (HLS/DASH) or when [Auto HDR](video_player_api_reference#auto_hdr) is enabled.

You can also configure these settings visually in the [Video Player Studio](video_player_studio#resize_and_crop).

For details on all resize and crop parameters, see [aspectRatio](video_player_api_reference#aspectRatio), [cropMode](video_player_api_reference#cropMode), and [cropPadColor](video_player_api_reference#cropPadColor) in the _Video Player API Reference_.

### Responsive breakpoints

Enable responsive breakpoints to automatically select the optimal video resolution based on the player's container width and the viewer's device pixel ratio (DPR). This prevents delivering unnecessarily large video files to smaller screens, improving load times and reducing bandwidth.

When enabled, the player:

1. Measures the container width of the player element.
2. Multiplies by the effective DPR to determine the required width.
3. Rounds up to the nearest standard rendition width: 640, 848, 1280, 1920, 2560, or 3840 pixels.
4. Applies a [`c_limit`](transformation_reference#c_limit) transformation at that width.

To enable responsive breakpoints, set the `breakpoints` parameter to `true`:

```js
const player = cloudinary.videoPlayer('player', {
  cloudName: 'demo',
  breakpoints: true
});
player.source('sea_turtle');
```

Here's a live example with breakpoints enabled. The resolved URL below the player shows the `c_limit,w_XXXX` transformation automatically applied based on the container width and device DPR. Try resizing your browser window and refreshing the page to see the width value change:

Loading...

#### maxDpr

Use `maxDpr` to cap the device pixel ratio used for resolution calculations. The effective DPR is the minimum of `maxDpr`, the device's actual DPR, and 2.0 (the default cap).

```js
const player = cloudinary.videoPlayer('player', {
  cloudName: 'demo',
  breakpoints: true,
  maxDpr: 1.5
});
```

For example, on a device with a DPR of 2.0, if the player container is 640px wide:

- Required width = 640 &times; 2.0 = 1280
- Nearest rendition = 1280
- The video URL includes `c_limit,w_1280`

On the same device with `maxDpr: 1.0`:

- Required width = 640 &times; 1.0 = 640
- Nearest rendition = 640
- The video URL includes `c_limit,w_640`

You can also enable breakpoints for a specific video source:

```js
player.source('sea_turtle', { breakpoints: true, maxDpr: 1.5 });
```

For full details on all breakpoint parameters, see [breakpoints](video_player_api_reference#breakpoints_config) and [maxDpr](video_player_api_reference#maxDpr) in the _Video Player API Reference_.

> **NOTES**:
>
> * Responsive breakpoints require Video Player version **3.8.0** or later.

> * Breakpoints apply to progressive delivery only. They are not used with adaptive streaming formats (HLS/DASH).

> * Breakpoints can be set at both the [player](video_player_api_reference#breakpoints_config) level and the [source](video_player_api_reference#options) level. Source-level settings override player-level settings.

> * Raw (non-Cloudinary) URLs are not affected by breakpoints.

> * The available rendition widths are: **640**, **848**, **1280**, **1920**, **2560**, and **3840** pixels.

> **TIP**:
>
> You can also configure breakpoints in the [Video Player Studio](video_player_studio) under the **Optimize & Resize** section when using progressive delivery.

## HDR video delivery

The Cloudinary Video Player can automatically deliver HDR (High Dynamic Range) video to viewers whose devices and browsers support it, while falling back gracefully to a tone-mapped SDR (Standard Dynamic Range) version on devices that don't.

When HDR delivery is enabled, the player checks whether the viewer's display supports high dynamic range using the browser's `(dynamic-range: high)` media query. If the device supports HDR, the player applies `dr_high`, `vc_h265`, and `f_mp4` transformations to the video source URL, delivering H.265 HDR content. If the device doesn't support HDR, the video is delivered as a tone-mapped SDR version, preventing the washed-out colors that can occur when HDR content is displayed on a non-HDR screen.

### Enabling HDR delivery

To enable HDR delivery, set the [`hdr`](video_player_api_reference#hdr_config) parameter to `true` in the player constructor (or use the `data-cld-hdr="true"` attribute in the `<video>` tag):

```js
const player = cloudinary.videoPlayer('player', {
  cloudName: 'demo',
  hdr: true
});
player.source('my_hdr_video');
```

You can also enable HDR for a specific source:

```js
player.source('my_hdr_video', { hdr: true });
```

> **NOTES**:
>
> * HDR delivery applies to progressive delivery only. It isn't available when using adaptive streaming (HLS/DASH).

> * The source video must be HDR content (HDR10 or HLG compatible). SDR source videos are delivered as SDR regardless of this setting.

> * For details on HDR video transformations and supported formats, see [HDR video transformations](video_manipulation_and_delivery#hdr_video_transformations).

You can also enable HDR delivery in the [Video Player Studio](video_player_studio) under the **Optimize** section.

## Video titles and descriptions

Video titles and descriptions can help give your audience quick information about your video. When set, the video player displays this information as follows:

* The title and description get displayed at the top of the video player whenever the controls are visible.
* The title gets displayed on the [playlist thumbnails](video_player_playlists_recommendations#present_the_upcoming_video) in the playlist widget or 'Next up' preview.
* The title and description get displayed for the primary video recommendation when [recommendations](video_player_playlists_recommendations#showing_video_recommendations) are on. The title gets displayed for the remaining recommendations.
* If a title isn't defined for playlist or recommendations, the video's public ID gets used instead. If a description isn't defined, that element isn't displayed.

The recommended approach for setting video titles and descriptions is using the `title` and `description` parameters at the source level. These parameters provide flexibility for both custom content and [AI-generated](#ai_generated_titles_and_descriptions) metadata.

Both parameters accept either:

- **Boolean value**: Set to `true` to display values from context metadata (`Video Title` and `Video Description`), either AI-generated or manually set, or `false` to hide them (default)
- **String value**: Provide a custom hardcoded title or description

Here are examples of using values from context metadata:

```js
player.source({
  publicId: 'snow_horses',
  title: true,
  description: true
});
```

And here's an example with custom hardcoded values:

```js 
player.source({
  publicId: 'snow_horses',
  title: 'Hard coded title',
  description: 'Hard-coded description for snow horses video'
});
```

> **NOTE**: For Video Player versions prior to `3.1.0`, use the `info` parameter at the source level to define title, subtitle, and description values.

### AI-generated titles and descriptions

Cloudinary can analyze your video content using AI to create meaningful, contextual titles and descriptions that accurately represent your video. When you set `title: true` and/or `description: true`, the player triggers generation of titles and descriptions using this AI analysis. Cloudinary stores these values as context metadata that the player can display, if any values exist already, the player displays these instead of the AI-generated values. This feature works on videos with audio that includes dialogue.

```js
player.source({
  publicId: 'my_video',
  title: true,
  description: true
});
```

Alternatively, you can generate titles and descriptions via API by including the `auto_video_details` parameter when uploading videos or using `explicit` for existing videos, or via [MediaFlows](mediaflows_block_reference#generate_video_details). This pre-generates the metadata and stores it as context values that the player can then display. This method also generates tags.

> **NOTE**: If you're using our [Asia Pacific data center](admin_api#alternative_data_centers_and_endpoints_premium_feature), you currently can't request auto video details.

## Multiple video sources

You can provide multiple versions of the same video content using the `videoSources` parameter. This is useful when you have different versions of a video, such as dubbed versions in different languages or alternative cuts. When you provide multiple sources, a source switcher appears in the player controls allowing users to select between the available versions.

Set the `videoSources` parameter at the player level with an array of source objects. Each source object can include the same properties as a standard video source (`publicId`, `transformation`, `textTracks`, etc.) plus a `label` parameter that's specific to multiple video sources and defines what text appears in the source switcher UI.

Here's an example showing different cuts of the same video:

```js
const player = cloudinary.videoPlayer('player', {
  cloudName: 'demo',
  videoSources: [
    {
      publicId: 'product_demo',
      label: 'Main Version'
    },
    {
      publicId: 'product_demo_alt', 
      label: 'Alternative Version'
    }
  ]
});
```

## Subtitles and captions

You can add subtitles and captions in `vtt`, `srt` or `transcript` format to your videos as a separate text track for the video player. This allows the user to toggle them on or off while the video is playing. 

If you're looking to automatically generate transcripts to use as subtitles and captions, you can trigger automatic generation via the [Video Player Studio](https://console.cloudinary.com/app/video/player-studio), [directly from the video player](#ai_generation), on [upload](video_transcription#requesting_transcription), on existing assets using the [explicit method](image_upload_api_reference#explicit), or via addons using the [Google AI Video Transcription addon](google_ai_video_transcription_addon) or [Microsoft Azure Video Indexer addon](microsoft_azure_video_indexer_addon).

To add text tracks, set the `textTracks` parameter at the `source` level.

The `textTracks` parameter is an object containing the information about all the text tracks you want to add to the video source. You can configure tracks for both `subtitles` or `captions` and also define [customizations](#visual_customization).

For each text track set the following parameters:

* `default` - Boolean. Whether the subtitles or captions should get displayed by default when the player loads the video.
* `label` - The label that appears in the menu when toggling captions or subtitles, e.g. "English".
* `language` - Optional - The language and country code of the text track, e.g. "en-US". Use this only to add [translated](video_transcription#requesting_translation) `transcript` files.
* `url` - Optional. The link to the subtitles or captions file to use for the captions. You can upload your `vtt` or `srt` files to Cloudinary as [raw files](upload_parameters#uploading_non_media_files_as_raw_files). If you specify no URL, the player attempts to use the auto generated `.transcript` file with the same public ID as the video, for example `my-video.transcript`. 

Here's an example of setting subtitles and captions using auto generated and translated `.transcript` files without needing to specify the URL:

```js
player.source(
  'docs/marketing-video',
  {
    textTracks: {
      captions: {
        label: 'English (Original)',
        default: true,
      },
      options: {
        theme: 'videojs-default',
      },
      subtitles: [
        {
          label: 'French',
          language: 'fr-FR',
        },
        {
          label: 'Spanish',
          language: 'es',
        },
        {
          label: 'German',
          language: 'de',
        }
      ]
    }
  });
```

And here is an example of explicitly setting the URL for a vtt file:

```js
player.source('outdoors', {
  textTracks: { 
    captions: { 
      label: 'English (Captions)',
      default: true,
      url: 'https://res.cloudinary.com/demo/raw/upload/outdoors.vtt'
      } 
    } 
  })
```	

> **TIP**:
>
> You can download a VTT or SRT version of your transcript by clicking the relevant **Download** button in the Video Player Studio Transcript Editor.

### Paced subtitles

Control the pace of your subtitles and captions by defining the maximum number of words to appear on each subtitle frame and optionally add [word highlighting](#word_highlight).

> **INFO**: Paced subtitles are only supported for auto generated Cloudinary transcript files and only for the original transcription, translated transcripts aren't supported. Use [Cloudinary auto transcription](video_transcription) to generate your files.

Add paced subtitles by specifying the `maxWords` parameter in your text track configuration.

For example, to restrict the number of words to 2:

```js
player.source(
  'outdoors',
  {
    textTracks: {
      subtitles: {
        label: 'English',
        default: true,
        maxWords: 2,
      },
    }
  });
```

#### Word highlight

In conjunction with paced subtitles, optionally enable word highlighting to show exactly when each individual word gets spoken in the video, just like karaoke. You can also configure the styling for the highlighted words as part of the [visual customization](#visual_customization).

To enable word highlighting, set the `wordHighlight` parameter to `true` in your subtitles configuration. it's recommended to also define a `maxWords` value to limit the number of words displayed. Here's an example that has a `wordHighlightStyle` defined:

```js
player.source(
  'outdoors',
  {
    textTracks: {
      options: {
        fontFace: "Palatino",
        fontSize: "90%",
        gravity: "bottom",
        wordHighlightStyle: {
          "color": "royalblue"
          }
      },
      subtitles: {
        label: 'English',
        default: true,
        maxWords: 3,
        wordHighlight: true
      },
    }
  });
```

See it in action on one of our [Dev Hints](https://www.youtube.com/playlist?list=PL8dVGjLA2oMpaTbvoKCaRNBMQzBUIv7N8) Youtube Shorts below.

 
> **TIP**: Have a social media use case? Check out the [Social Media Videos guide](social_media_videos).

> **NOTES**:
>
> * Setting `maxWords` triggers the player to automatically attempt to use a `.transcript` file with the same public ID as the video (`<public-id>.transcript`). This is the default naming for auto generated transcript files.

> * Use the `timeOffset` parameter in your subtitles configuration to adjust when the subtitles appear to match the sound. For example, `timeOffset: -0.2` to display the subtitles 0.2 seconds earlier.

> * Paced subtitles aren't supported for translated transcripts.

### Visual customization

To configure the visual appearance of your captions and subtitles, add an `options` object to the `textTracks` configuration. You can define:

* `box` to define the size and location of the text box.
* `gravity` to determine where to place the text tracks on the video.
* `fontFace` to match your branding.
* `fontSize` to control sizing.
* `style` to apply custom CSS styling to the text.
* `theme` such as `player-colors` to reflect the [color scheme](#color_scheme) you have defined.
* `wordHighlightStyle` to apply custom CSS styling to the highlighted text when using [word highlighting](#word_highlight) for paced subtitles.
> **INFO**: To ensure compliance with the Web Content Accessibility Guidelines (WCAG) 2.1, select colors with appropriate contrast levels. See our dedicated [accessibility](video_player_accessibility) page to learn more about other Video Player accessibility features.
See full details of the options in the [Video Player reference](video_player_api_reference#text_tracks_options).

Here's an example showing a range of options applied:

```js
player.source(
  'outdoors',
  {
    textTracks: {
      options: {
        theme: 'yellow-outlined',
        gravity: 'center',
        fontFace: 'Kanit',
        fontSize: '2rem',
        style: {'opacity': '0.6'}
      },
      subtitles: {
        label: 'English',
        default: true,
        url: 'https://res.cloudinary.com/demo/raw/upload/outdoors.vtt'
      }
    }
  });
```

> **NOTE**: Due to the way the Safari browser handles text tracks, some custom styling may not work as expected. it's important to verify the appearance of your custom styling on Safari and adjust accordingly.

## Video chapters

Video chapters provide information about different sections of a video and allow for quick navigation to those sections. The video player gets updated to show chapter markers on the seek bar and the chapter titles appear in the control bar. 

You can define these chapters either through a VTT file or manually via the `chapters` configuration object. You can automatically generate the chapters VTT file by using the `auto_chaptering` parameter on [upload](image_upload_api_reference#auto_chaptering), [directly from the video player](#ai_generation), or via [MediaFlows](mediaflows_block_reference#video_chaptering).

> **NOTE**: If you're using our [Asia Pacific data center](admin_api#alternative_data_centers_and_endpoints_premium_feature), you currently can't request auto chaptering.

> **TIP**: :title=Module imports

To use chapters when [importing the player as a module](cloudinary_video_player#option_1_install_the_video_player_and_import_as_a_module), you also need to import the chapters module. For example:

  ```js
  import 'cloudinary-video-player/chapters';
  ```

Define `chapters`  when setting the video source or alternatively as a constructor parameter when creating a player instance. You can define the chapters in two ways:

* **Using a VTT file**: Use the VTT naming convention shown below or provide the URL of a VTT file that contains the chapter information. You can create your file and [upload it to Cloudinary as a raw file](upload_parameters#uploading_non_media_files_as_raw_files) or use the [chapters editor](#chapters_editor) to generate the file for you.
* **Using chapters object**: Define the chapters directly in the chapters object by specifying the start time in seconds as the key and the chapter title as the corresponding value.

To use a VTT file, set the `chapters` parameter to `true`, this looks for a file using the following naming convention:

`<video public id>-chapters.vtt`

For example, for the video with a public ID of `old_camera`, the VTT file `old_camera-chapters.vtt` gets referenced. If no chapters file exists and you have enabled **Auto chaptering** in your account's unsigned actions settings, the player will attempt to generate chapters using AI.

```js
const player = cloudinary.videoPlayer("player", { cloudName: "demo" });

player.source("old_camera", {
  chapters: true
});
```

Alternatively, you can set the `chapters` parameter to an object with the `url` parameter set to the URL of your VTT file:

```js
chapters: {
  url: "https://res.cloudinary.com/demo/raw/upload/docs/chapters_example.vtt"
}
```

Your VTT file should follow the standard WebVTT format. Here's a sample VTT file:

```WEBVTT
WEBVTT

00:00:00.000 --> 00:00:05.000
Chapter 1

00:00:06.000 --> 00:00:11.000
Chapter 2

00:00:12.000 --> 00:00:15.000
Chapter 3

```

To manually set chapters, you can define them in the chapters object. The keys represent the start time in seconds, and the values represent the chapter titles. For example:

```js
chapters: {
  1.5: "Chapter 1",
  6.3: "Chapter 2",
  12.2: "Chapter 3"
}
```

Optionally, you can show a button that displays a list of available chapters by adding the setting the `chaptersButton` constructor parameter to true.

Here's a full example that demonstrates adding chapters from a VTT file and displaying the chapters button:

```js
const player = cloudinary.videoPlayer("player", { 
  cloudName: "demo",
  chaptersButton: true
});

player.source("elephants", {
  chapters: {
    url:
      "https://res.cloudinary.com/demo/raw/upload/docs/chapters_example.vtt"
  }
});
```

> **TIP**: You can see video chapters in action in the [video review sample project](video_review_sample_project).

### Chapters editor

Define, edit, or upload your chapters using the chapters editor in the [Video Player Studio](https://console.cloudinary.com/app/video/player-studio). From the **Chapters** section of the studio, you have the following options:

* **Upload a VTT file** - Use this to upload your chapters VTT file and have it applied to your selected video.
* **Update chapters manually** - Define your chapter names and timestamps manually for the selected video. By default, the chapter gets added using the current time of the seekbar in the preview window. The chapters get added to a new VTT file that's uploaded alongside your video, and you can see how this gets referenced in the code window.

![Chapters editor](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/docs/player_studio_chapters.png "thumb: w_500,dpr_2, width:500, popup: true")

## AI generation

The video player can automatically trigger generation of AI-powered content on demand when you set the relevant options in your player configuration. This enables lazy generation, rather than generating them on upload or via the UI.

* **Titles and descriptions** - generate meaningful [titles and descriptions](#video_titles_and_descriptions) for your videos by setting `title: true` and/or `description: true`.
* **Transcripts** - generate transcripts for [subtitles and captions](#subtitles_and_captions) by configuring text tracks without specifying a URL
* **Chapters** - generate [chapter](#video_chapters) markers and titles for video navigation by setting `chapters: true`

The player only attempts AI generation when the requested content doesn't already exist. When AI generation starts, the player initially returns a `202` response to indicate that it's generating content.

> **INFO**:
>
> You can prevent the Video Player from attempting AI generation by disabling the relevant options (enabled by default) in your product environment [security settings](https://console.cloudinary.com/app/settings/security) in the **Unsigned actions allowed** section.
> For transcript translation, when you request multiple languages, you need to enable **Google Translate** in the **Unsigned add-on transformations allowed** section of the security settings.
> Unsigned actions for generating video details, transcripts, and chapters {valeExclude}are enabled{/valeExclude} by default. If you continue to allow unsigned actions, keep in mind that users can potentially perform actions that consume your quota or modify your assets.

## Low-level customization with video.js

The Cloudinary video player builds over the VideoJS player, v8. You can access all underlying capabilities of the VideoJS API, using the `videoPlayer.videojs.default` property.

> **NOTES**:
>
> * In previous versions of the Cloudinary Video Player, you accessed the VideoJS API with `videoPlayer.videojs`. Due to changes in the VideoJS API, you must now update these references to `videoPlayer.videojs.default`.

> * Adding customizations with video.js could cause conflicts when future versions of the Cloudinary video player get released. Before upgrading your version of the video player, it's important to verify that your customizations are still working correctly.

For full details on all of the available functionality, see the [VideoJS Player API documentation](https://docs.videojs.com/player.html).

> **What's Next?**:
>
> * [Register to events to further customize your player or for analytics tracking](video_player_events_analytics)

> * [Define playlists and set up video recommendations](video_player_playlists_recommendations)

> * [Check out all the video player options in the Video Player API Reference](video_player_api_reference)

> * [Learn more about the video effects and transformations you can apply to your videos](video_manipulation_and_delivery)
