> ## 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 you also need details relating to other Cloudinary products for your current use case, see the parent index at: https://cloudinary.com/documentation/llms.txt?referrer=docpage

# Video Canvas (Beta)


> **INFO**: Video Canvas is currently in **Beta**. There may be minor changes to parameter names or other implementation details before the general access release. We invite you to try it out. We would appreciate any feedback via our [support team](https://support.cloudinary.com/hc/en-us/requests/new).

Video Canvas is a visual, node-based editor for building video transformation pipelines in the Console. Instead of writing a transformation URL by hand, you drag **blocks** onto a canvas, connect them into a flow that goes from an input, through one or more transformation steps, to an output, and preview the result live as you work.

When you're happy with a flow, you save it as an **applet**: a reusable, named pipeline backed by a [named transformation](video_named_transformations). Anything that can reference a named transformation, such as a delivery URL or an SDK call, can then use your applet.

## How Video Canvas works

Video Canvas has three core concepts:

* **Canvas**: The workspace where you compose a pipeline. It's a directed flow of blocks connected by wires that carry media (and, for advanced flows, values) from one block to the next.
* **Block**: A single step in the pipeline. An input block seeds the flow with a video, action blocks transform it (resize, trim, add effects, adjust encoding, and more), and an output block determines what's delivered.
* **Applet**: A canvas saved as a reusable pipeline with a defined interface: the inputs a caller provides and the output they get back. A named transformation backs each saved applet, so you can apply it anywhere you use named transformations.

Because an applet is based on a named transformation, delivering through it uses the familiar `t_<name>` syntax. For example, once you save an applet that produces a square, watermarked clip, you can add its transformation to a URL and reuse it across as many assets as you like.

## Opening Video Canvas

Video Canvas is available from the **Video** section of the Console, listed as **Video Canvas** (Beta).

From the **Video Applets** landing page, you can start from a preset or blank canvas, open a saved applet, or import an exported applet.

![The Video Canvas landing page, showing starter templates and the saved applets list](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/v1784130117/docs/video_canvas/video_canvas_landing.png "thumb: w_700,dpr_2, width:700, with_code:false, with_url:false, popup:true")

## Building a transformation

To build a transformation, open a blank canvas and assemble a flow from left to right:

1. **Add an input block.** From the block palette, add a **Video input** block and set its **Public ID** to the video you want to transform. Enter a public ID or click **Browse** to select an asset from your product environment.
2. **Add action blocks.** Add one or more transformation blocks (for example, **Resize**, then **Format**) and configure each block's settings.
3. **Add an output block.** Add a **Video output** block to complete the flow.
4. **Connect the blocks.** Draw wires from each block's output port to the next block's input port so the media flows input to output. A valid derived flow is a single chain from a video input to one output.

As you build, Video Canvas continuously compiles the flow into a Cloudinary delivery URL and shows validation hints (for example, if the flow is missing an output block).

![A Video Canvas flow connecting a video input, a color adjustments block, a vignette effect, and a video output with a live preview](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/v1784130121/docs/video_canvas/video_canvas_editor.png "thumb: w_700,dpr_2, width:700, with_code:false, with_url:false, popup:true")

### The block palette

Open the palette from the canvas toolbar to browse the available blocks, grouped by purpose. You can also search across block names and their settings.

![The Video Canvas block palette, showing the Inputs and Optimization & Normalization groups](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/v1784130119/docs/video_canvas/video_canvas_palette.png "thumb: w_700,dpr_2, width:700, with_code:false, with_url:false, popup:true")

* **Inputs**: Video input, Text input. (Image input is coming soon.)
* **Optimization & Normalization**: Resize, Pad, Crop, Smart crop, Rotate, Format, Quality, DPR, Color space, HDR, Trim, Video summarization, Audio volume, Video codec, Audio codec, Bitrate, FPS, Audio frequency, and keyframe interval.
* **Effects & Enhancements**: Color adjustments, Blur effect, Visual noise effect, Vignette effect, Deshake, Fade in, Fade out, Loop, Accelerate, Boomerang effect, Reverse effect, Border, Round corners, Make transparent, Progress bar, and Text overlay.
* **Tools**: Metadata variable, Calculator, If / Else, End If, Custom transformation, and Preview.
* **Outputs**: Video output. (Image output is coming soon.)

> **TIP**:
>
> Use the **Custom transformation** block when you need a transformation that doesn't have a dedicated block yet. It accepts raw transformation syntax (for example, `e_sepia`) and adds it to the flow as-is.

The **Tools** blocks add dynamic behavior to a flow: **Metadata variable** and **Calculator** produce values from an asset's characteristics (such as duration or width), and **If / Else** with **End If** branch the pipeline on a condition. These build on Cloudinary's [user-defined variables and arithmetic transformations](video_user_defined_variables) and [conditional transformations](video_conditional_expressions).

## Previewing your flow

Add a **Preview** block anywhere in the chain to inspect the media at that point, or use the **Video output** block's preview to see the final result. The preview plays the compiled delivery so you can confirm the transformation before saving.

## Saving an applet

When your flow is ready, save it as an applet. Saving creates a named transformation from the flow (with an automatically generated name that begins with `vc_`), so the applet is immediately usable in delivery URLs and SDKs.

A few things to know about saving:

* A complete flow runs from an input block, through your transform blocks, to a single output block. While a flow is incomplete, the canvas shows an advisory hint (for example, *Graph must have exactly one input block*).
* You can rename an applet from the editable title at the top of the editor. This changes the display name only; the named transformation keeps its original `vc_` name, so existing delivery URLs and SDK calls keep working.
* The canvas is the source of truth: edit the flow and save again to update the applet.

## Defining your applet's interface

Switch to the **Applet** view to define the interface your applet exposes to callers. This is where a canvas becomes a reusable, parameterized pipeline:

![The Video Canvas Applet view, showing the applet description, parameters, result, and the API and SDK snippets](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/v1784130123/docs/video_canvas/video_canvas_applet.png "thumb: w_700,dpr_2, width:700, with_code:false, with_url:false, popup:true")

* **Description**: A short summary of what the applet does.
* **Parameters**: The inputs a caller provides, derived from the input blocks in your flow. Each parameter has a **kind** (its value type: video, image, or text). You must set the base video, which is the asset the transformation applies to. All other inputs are optional; if the caller doesn't provide a value, the applet uses the default value set in the canvas.
* **Result**: The output the applet returns, taken from its output block.

Use **Try it out** to fill the parameters with real values and run the applet to preview its output. Enter a public ID or click **Browse** to select one, then click **Run**: Video Canvas plays the result and returns its delivery URL. Running requires a saved applet with at least one input and an output block.

## Managing and reusing applets

From the landing page, the **Applets** list shows every applet saved in your product environment. You can search applets by name and description, reopen one to keep editing, and start new applets from templates. The same browser is available inside the editor from the menu in the top-left corner.

From the applets browser you can also **export** an applet to a JSON file (the download icon on each row), **delete** an applet (the trash icon), and **import** a previously exported `.json` applet (the import icon at the top of the panel). Deleting an applet removes only the applet itself; the named transformation it created stays in place, so any delivery URLs or SDK calls that already reference it keep working. To stop delivering the transformation, remove the named transformation separately (see [Named transformations](video_named_transformations)).

Within an open applet, use **Save as** to save the current flow as a new, separately named applet (you're prompted for the name).

## Delivering with an applet

Because a named transformation backs each saved applet, you deliver through it the same way you'd use any named transformation, by adding the applet's transformation to a delivery URL. A basic delivery URL has this structure:

```text
https://res.cloudinary.com/<cloud_name>/video/upload/t_vc_<applet>/<inputVideo>.mp4
```

For example, this applet, saved for the Cloudinary `demo` product environment, crops the source video to a square, keeping the main subject in view:

![Video Canvas applet that crops a video to a square](https://res.cloudinary.com/demo/video/upload/t_vc_docs_example_square_d78c4c/samples/sea-turtle.mp4)

```nodejs
cloudinary.video("samples/sea-turtle", {transformation: ["vc_docs_example_square_d78c4c"]})
```

```react
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4").namedTransformation(
  name("vc_docs_example_square_d78c4c")
);
```

```vue
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4").namedTransformation(
  name("vc_docs_example_square_d78c4c")
);
```

```angular
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4").namedTransformation(
  name("vc_docs_example_square_d78c4c")
);
```

```js
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4").namedTransformation(
  name("vc_docs_example_square_d78c4c")
);
```

```python
CloudinaryVideo("samples/sea-turtle").video(transformation=["vc_docs_example_square_d78c4c"])
```

```php
use Cloudinary\Transformation\NamedTransformation;

(new VideoTag('samples/sea-turtle.mp4'))
	->namedTransformation(NamedTransformation::name("vc_docs_example_square_d78c4c"));
```

```java
cloudinary.url().transformation(new Transformation().named("vc_docs_example_square_d78c4c")).videoTag("samples/sea-turtle");
```

```ruby
cl_video_tag("samples/sea-turtle", transformation: ["vc_docs_example_square_d78c4c"])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation().Named("vc_docs_example_square_d78c4c")).BuildVideoTag("samples/sea-turtle")
```

```dart
cloudinary.video('samples/sea-turtle.mp4').transformation(Transformation()
	.namedTransformation(NamedTransformation.name("vc_docs_example_square_d78c4c")));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation().setNamed("vc_docs_example_square_d78c4c")).generate("samples/sea-turtle.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation().named("vc_docs_example_square_d78c4c")).resourceType("video").generate("samples/sea-turtle.mp4");
```

```flutter
cloudinary.video('samples/sea-turtle.mp4').transformation(Transformation()
	.namedTransformation(NamedTransformation.name("vc_docs_example_square_d78c4c")));
```

```kotlin
cloudinary.video {
	publicId("samples/sea-turtle.mp4")
	 namedTransformation(NamedTransformation.name("vc_docs_example_square_d78c4c")) 
}.generate()
```

```jquery
$.cloudinary.video("samples/sea-turtle", {transformation: ["vc_docs_example_square_d78c4c"]})
```

```react_native
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4").namedTransformation(
  name("vc_docs_example_square_d78c4c")
);
```

Where an applet takes a caller-supplied input, that value appears in the URL. This applet overlays a line of text (here, `Cloudinary`), passed as a [user-defined variable](video_user_defined_variables) (`$inputText_!...!`) that the applet's transformation references:

![Video Canvas applet that overlays caller-supplied text](https://res.cloudinary.com/demo/video/upload/$inputText_!Cloudinary!/t_vc_docs_example_with_text_159f4f/samples/sea-turtle.mp4)

```nodejs
cloudinary.video("samples/sea-turtle", {transformation: [
  {variables: [["$inputText", "!Cloudinary!"]]},
  {transformation: ["vc_docs_example_with_text_159f4f"]}
  ]})
```

```react
import { set } from "@cloudinary/url-gen/actions/variable";
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4")
  .addVariable(set("inputText", "Cloudinary"))
  .namedTransformation(name("vc_docs_example_with_text_159f4f"));
```

```vue
import { set } from "@cloudinary/url-gen/actions/variable";
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4")
  .addVariable(set("inputText", "Cloudinary"))
  .namedTransformation(name("vc_docs_example_with_text_159f4f"));
```

```angular
import { set } from "@cloudinary/url-gen/actions/variable";
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4")
  .addVariable(set("inputText", "Cloudinary"))
  .namedTransformation(name("vc_docs_example_with_text_159f4f"));
```

```js
import { set } from "@cloudinary/url-gen/actions/variable";
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4")
  .addVariable(set("inputText", "Cloudinary"))
  .namedTransformation(name("vc_docs_example_with_text_159f4f"));
```

```python
CloudinaryVideo("samples/sea-turtle").video(transformation=[
  {'variables': [["$inputText", "!Cloudinary!"]]},
  {'transformation': ["vc_docs_example_with_text_159f4f"]}
  ])
```

```php
use Cloudinary\Transformation\Variable;
use Cloudinary\Transformation\NamedTransformation;

(new VideoTag('samples/sea-turtle.mp4'))
	->addVariable(Variable::set("inputText","Cloudinary"))
	->namedTransformation(NamedTransformation::name("vc_docs_example_with_text_159f4f"));
```

```java
cloudinary.url().transformation(new Transformation()
  .variables(variable("$inputText","!Cloudinary!")).chain()
  .named("vc_docs_example_with_text_159f4f")).videoTag("samples/sea-turtle");
```

```ruby
cl_video_tag("samples/sea-turtle", transformation: [
  {variables: [["$inputText", "!Cloudinary!"]]},
  {transformation: ["vc_docs_example_with_text_159f4f"]}
  ])
```

```csharp
cloudinary.Api.UrlVideoUp.Transform(new Transformation()
  .Variables("$inputText", !Cloudinary!).Chain()
  .Named("vc_docs_example_with_text_159f4f")).BuildVideoTag("samples/sea-turtle")
```

```dart
cloudinary.video('samples/sea-turtle.mp4').transformation(Transformation()
	.addVariable(Variable.set("inputText","Cloudinary"))
	.namedTransformation(NamedTransformation.name("vc_docs_example_with_text_159f4f")));
```

```swift
cloudinary.createUrl().setResourceType("video").setTransformation(CLDTransformation()
  .chain()
  .setNamed("vc_docs_example_with_text_159f4f")).generate("samples/sea-turtle.mp4")
```

```android
MediaManager.get().url().transformation(new Transformation()
  .variables(variable("$inputText","!Cloudinary!")).chain()
  .named("vc_docs_example_with_text_159f4f")).resourceType("video").generate("samples/sea-turtle.mp4");
```

```flutter
cloudinary.video('samples/sea-turtle.mp4').transformation(Transformation()
	.addVariable(Variable.set("inputText","Cloudinary"))
	.namedTransformation(NamedTransformation.name("vc_docs_example_with_text_159f4f")));
```

```kotlin
cloudinary.video {
	publicId("samples/sea-turtle.mp4")
	 addVariable(Variable.set("inputText","Cloudinary"))
	 namedTransformation(NamedTransformation.name("vc_docs_example_with_text_159f4f")) 
}.generate()
```

```jquery
$.cloudinary.video("samples/sea-turtle", {transformation: [
  {variables: [["$inputText", "!Cloudinary!"]]},
  {transformation: ["vc_docs_example_with_text_159f4f"]}
  ]})
```

```react_native
import { set } from "@cloudinary/url-gen/actions/variable";
import { name } from "@cloudinary/url-gen/actions/namedTransformation";

new CloudinaryVideo("samples/sea-turtle.mp4")
  .addVariable(set("inputText", "Cloudinary"))
  .namedTransformation(name("vc_docs_example_with_text_159f4f"));
```

The **Applet** view's **API & SDK** panel gives you ready-to-use integration options, with tabs for the delivery **URL**, server SDKs (**Node**, **Python**), the Cloudinary Video **Player**, and an **Embed** snippet, so you can copy the exact form you need for your code.

For more on how named transformations work in delivery URLs, see [Named transformations](video_named_transformations).

## Managing applets via the API

In addition to building applets in the Console, you can use the Video Canvas API to list, create, retrieve, replace, and delete applets programmatically. When you create or replace an applet, Cloudinary compiles its inputs, blocks, connections, and outputs into a named transformation.

The API uses Basic Authentication with your Cloudinary **API Key** and **API Secret**, and its base URL is `https://api.cloudinary.com/v2/video/{cloudName}`.

For the full list of endpoints, request and response payloads, and interactive examples, see the [Video Canvas API reference](video_canvas_reference).

> **READING**:
>
> Learn more about related video transformation features:

> * [Video Canvas API reference](video_canvas_reference)

> * [Named transformations](video_named_transformations)

> * [Video transformations](video_manipulation_and_delivery)

> * [User-defined variables and arithmetic transformations](video_user_defined_variables)

> * [Conditional transformations](video_conditional_expressions)
