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

# Create animated images


You can create a single animated image (GIF, PNG or WebP) or video (MP4 or WebM) from multiple image assets, where each asset is used as a single frame of the resulting animated image or video.
Animated images can be created from a maximum of 500 frames (individual images), except in the following cases where the maximum is 100 frames:

* If the processing is done in a synchronous mode (i.e., without the `async` parameter set to true).
* If there is an underlay or overlay added to the image (`l_` or `u_`).
* If any transformation parameters are added that are not on the following list: `background`, `flags`, `crop`, `width`, `height`, `x`, `y`, `gravity`, `quality`, `angle`, `page`, and `dpr`.
* If the `angle` parameter is added with a value that is anything but `exif` or `auto`.
* If the `crop` parameter is added with a value that is anything but `scale`, `limit`, `fit`, `fill`, `thumb` or `crop`.

If the limit is exceeded, only the first 500 (or 100) images will be included.

## Step 1: Upload and tag the images

Upload all the images to be included in the animated image or video. Make sure that you include:

* An appropriate public ID when uploading each of the files; when they are merged into a single animated image or video, they are sorted alphabetically by their public IDs.
* An identical tag for all images. The tag must be unique to these images only; the animated image creation process finds all images with the same tag and merges them into a single file.

## Step 2: Create the animated image

Use the [multi method of the upload API](image_upload_api_reference#multi_method) to create the animated image. If the images to be merged are not all the same size, you can add transformation parameters to the URL to crop them accordingly (using one of the crop modes plus `width` or `height`, etc). For example, to create an animated GIF from all images with the tag `arrow_animation`:

```multi

|ruby
Cloudinary::Uploader.multi('arrow_animation')

|php_2
$cloudinary->uploadApi()->multi('arrow_animation');

|python
cloudinary.uploader.multi("arrow_animation")

|nodejs
cloudinary.v2.uploader
.multi('arrow_animation')
.then(result=>console.log(result)); 

|java
cloudinary.uploader().multi('arrow_animation', ObjectUtils.emptyMap());

|go
resp, err := cld.Upload.Multi(ctx, uploader.MultiParams{Tag: "arrow_animation"})

|curl
curl https://api.cloudinary.com/v1_1/demo/image/multi -X POST --data 'tag=arrow_animation&timestamp=173719931&api_key=436464676&signature=a788d68f86a6f868af' 

|cli
cld uploader multi tag="arrow_animation"
```

## Step 3: Deliver the animated image

To deliver the animated image, use the Cloudinary image delivery URL with `type` set to `multi`. For example, to deliver an animated GIF created from all images with the tag `arrow_animation`:

![arrow_animation.gif created from all images with the arrow_animation tag](https://res.cloudinary.com/demo/image/multi/arrow_animation.gif)

```nodejs
cloudinary.image("arrow_animation.gif", {type: "multi"})
```

```react
new CloudinaryImage("arrow_animation.gif").setDeliveryType("multi");
```

```vue
new CloudinaryImage("arrow_animation.gif").setDeliveryType("multi");
```

```angular
new CloudinaryImage("arrow_animation.gif").setDeliveryType("multi");
```

```js
new CloudinaryImage("arrow_animation.gif").setDeliveryType("multi");
```

```python
CloudinaryImage("arrow_animation.gif").image(type="multi")
```

```php
(new ImageTag('arrow_animation.gif'))
	->deliveryType("multi");
```

```java
cloudinary.url().transformation(new Transformation().type("multi").imageTag("arrow_animation.gif");
```

```ruby
cl_image_tag("arrow_animation.gif", type: "multi")
```

```csharp
cloudinary.Api.UrlImgUp.Action("multi").BuildImageTag("arrow_animation.gif")
```

```dart
cloudinary.image('arrow_animation.gif').transformation(Transformation()
	.setDeliveryType("multi"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setType( "multi").generate("arrow_animation.gif")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().type("multi").generate("arrow_animation.gif");
```

```flutter
cloudinary.image('arrow_animation.gif').transformation(Transformation()
	.setDeliveryType("multi"));
```

```kotlin
cloudinary.image {
	publicId("arrow_animation.gif")
	 deliveryType("multi") 
}.generate()
```

```jquery
$.cloudinary.image("arrow_animation.gif", {type: "multi"})
```

```react_native
new CloudinaryImage("arrow_animation.gif").setDeliveryType("multi");
```

## Example script

The following example showcases a method to create a very simple animated GIF of revolving text consisting of 20 individual frames. A script is executed to upload the individual images to Cloudinary, where each individual image (frame) is constructed from:

* A previously uploaded blank image used as a base image.
* A text string overlaid over the base image.

Each frame is a combination of the base image together with an overlay of a slightly modified version of the text string. The text is modified for each frame with the `distort` effect parameter to change its perspective.

```ruby
coordinates = {}
(0..10).each do |frame|
  x_offset = frame * 10
  y_back   = 10*(frame < 5 ? -frame : frame - 10)
  y_front  = y_back*2

  front    = [ x_offset, y_front, 
               100 - x_offset, -y_back,
               100 - x_offset, 100+y_back,
               x_offset, 100 - y_front ]
            .map { |i| "#{i}p" }.join(":")

  back     = [ x_offset, -y_back, 
               100 - x_offset, y_back*2,
               100 - x_offset, 100 - y_back*2,
               x_offset, 100 + y_back ]
            .map { |i| "#{i}p" }.join(":")

  coordinates[frame]      = front
  coordinates[20 - frame] = back
end

(0..19).each do |frame|
  x_offset = frame < 10 ? frame*10 : 200 - frame*10
  myurl    = Cloudinary::Utils.cloudinary_url(
    "base.png",
    transformation: [
      { width: 510, height: 300, crop: "scale",
        background: "white" },
      { overlay: "text:roboto_150_bold:Spinning text", 
        color: "#0071BA", width: 500, height: 100 },
      { effect: "distort:#{coordinates[frame]}" },
      { crop: "crop", gravity: "center", 
        width: ((500*(100-2*x_offset)/100.0).abs.to_i), 
        height: 300 },
      { flags: "layer_apply" }])

  Cloudinary::Uploader.upload(
     myurl,
     public_id: "spinning_text_#{'%02d' % frame}",
     tags: "spinning_text"
  ) if x_offset != 50
end

Cloudinary::Uploader.multi("spinning_text", delay: 100)
```

After the script is run and the images are uploaded, the following URL delivers the animated GIF:
![animated_logo.gif created from all images with the spinning_text tag](https://res.cloudinary.com/demo/image/multi/dl_100/spinning_text.gif)

```nodejs
cloudinary.image("spinning_text.gif", {delay: "100", type: "multi"})
```

```react
new CloudinaryImage("spinning_text.gif")
  .animated(edit().delay(100))
  .setDeliveryType("multi");
```

```vue
new CloudinaryImage("spinning_text.gif")
  .animated(edit().delay(100))
  .setDeliveryType("multi");
```

```angular
new CloudinaryImage("spinning_text.gif")
  .animated(edit().delay(100))
  .setDeliveryType("multi");
```

```js
new CloudinaryImage("spinning_text.gif")
  .animated(edit().delay(100))
  .setDeliveryType("multi");
```

```python
CloudinaryImage("spinning_text.gif").image(delay="100", type="multi")
```

```php
(new ImageTag('spinning_text.gif'))
	->animated(Animated::edit()->delay(100))
	->deliveryType("multi");
```

```java
cloudinary.url().transformation(new Transformation().delay("100")).type("multi").imageTag("spinning_text.gif");
```

```ruby
cl_image_tag("spinning_text.gif", delay: "100", type: "multi")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Delay("100")).Action("multi").BuildImageTag("spinning_text.gif")
```

```dart
cloudinary.image('spinning_text.gif').transformation(Transformation()
	.addTransformation("dl_100")
	.setDeliveryType("multi"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setType( "multi").setTransformation(CLDTransformation().setDelay("100")).generate("spinning_text.gif")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().delay("100")).type("multi").generate("spinning_text.gif");
```

```flutter
cloudinary.image('spinning_text.gif').transformation(Transformation()
	.addTransformation("dl_100")
	.setDeliveryType("multi"));
```

```kotlin
cloudinary.image {
	publicId("spinning_text.gif")
	 animated(Animated.edit() { delay(100) })
	 deliveryType("multi") 
}.generate()
```

```jquery
$.cloudinary.image("spinning_text.gif", {delay: "100", type: "multi"})
```

```react_native
new CloudinaryImage("spinning_text.gif")
  .animated(edit().delay(100))
  .setDeliveryType("multi");
```

