---
title: "How to add a carousel swipe with Claude (tested prompt and script)"
description: "Four product photos on rounded cards swipe left one at a time over a blurred backdrop, with a row of page dots lighting up underneath like a phone photo carousel. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/carousel-swipe
---

# How to add a carousel swipe with Claude

Four product photos on rounded cards swipe left one at a time over a blurred backdrop, with a row of page dots lighting up underneath like a phone photo carousel.

Made by the script on this page, from our own test clips.

## Get this effect

1. ### Switch to Code in your [Claude Desktop App](https://claude.com/download)

2. ### Install the skill (once)

   ```
   Install the Recharm video effects skill: download https://recharm-growth-staging.s3.us-east-2.amazonaws.com/website/downloads/recharm-video-effects.zip and unzip it into ~/.claude/skills
   ```

3. ### Ask for the effect

   ```
   Make a carousel swipe from clipA.mp4 and clipB.mp4
   Which clip and second each photo is taken from, in carousel order: [(1, 0.5), (1, 5.0), (2, 2.5), (2, 5.5)]
   Second each swipe to the next photo starts: [1.0, 2.3, 3.6]
   ```

## Several videos, several effects, one ask

Put your clips in the folder, mention them with @, and ask for everything at once. Claude runs one tested script after another on each clip, then joins them.

Add a carousel swipe to **@clip1.mp4**, put a sale badge on **@clip2.mp4**, give **@clip3.mp4** a VHS look, then join all three with crossfades.

## Settings you can change

Ask Claude to change any of these, or edit them at the top of the script.

| Setting          | Default                                    | What it does                                                       |
| ---------------- | ------------------------------------------ | ------------------------------------------------------------------ |
| `PHOTOS`         | `[(1, 0.5), (1, 5.0), (2, 2.5), (2, 5.5)]` | Which clip and second each photo is taken from, in carousel order. |
| `SWIPES`         | `[1.0, 2.3, 3.6]`                          | Second each swipe to the next photo starts.                        |
| `SWIPE`          | `0.6`                                      | Seconds a swipe takes.                                             |
| `CARD_W, CARD_H` | `0.80, 0.60`                               | Card size as a share of the frame width and height.                |
| `GAP`            | `0.045`                                    | Space between cards, as a share of the frame width.                |
| `BLUR, DARKEN`   | `24, 0.40`                                 | Background blur and how much darker the background gets.           |

## Without the skill

Don’t want to install the skill? Paste this prompt into Claude Code and Claude writes the script itself.

```
Make a photo carousel from clipA.mp4 and clipB.mp4, keeping clipA's size (540x960), 30fps, 6 seconds and its audio. Background: clipA with a Gaussian blur (sigma 24) and every color channel multiplied by 0.6. Photos: four stills, taken from clipA at 0.5s and 5.0s and from clipB at 2.5s and 5.5s, each cropped to the card's box from the full frame (x 54 to 486, y 163 to 739, so 432x576, 80% of the width and 60% of the height, centered horizontally with its center 47% down). Draw each card with 27-pixel rounded corners and a soft black shadow (alpha 140, Gaussian blur 14, 8 pixels lower). Cards sit in a row 24 pixels apart, so the neighbors peek in at the edges. Start on the first photo, then swipe the row left by one card (456 pixels) at 1.0s, 2.3s and 3.6s, each swipe taking 0.6 seconds with a smoothstep ease in and out, ending on the fourth photo. Below the card, centered at y 777, draw four page dots of radius 7.5 pixels, 27 pixels apart center to center, centered horizontally: the dot of the photo on screen solid white, the others white at 40% opacity, cross-fading as the cards swipe. Draw the cards and dots with Python and Pillow (not ffmpeg's drawtext) and composite them over the background with ffmpeg. Save it as output.mp4 (H.264, yuv420p).
```

## Run it without Claude

This is the exact script we tested. Save it as `carousel-swipe.py` and run:

```
python3 carousel-swipe.py clipA.mp4 clipB.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Carousel swipe: four product photos on rounded cards swipe left one at a time, like a phone photo carousel.

Usage: python3 carousel-swipe.py clipA.mp4 clipB.mp4 output.mp4
The photos are stills taken from the two clips; clipA also plays blurred and darkened behind the cards, with its sound.
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import math
import subprocess
import sys

from PIL import Image, ImageDraw, ImageFilter

PHOTOS = [(1, 0.5), (1, 5.0), (2, 2.5), (2, 5.5)]  # (clip 1 or 2, second) of each photo, in carousel order
SWIPES = [1.0, 2.3, 3.6]       # second each swipe to the next photo starts
SWIPE = 0.6                    # seconds a swipe takes
CARD_W, CARD_H = 0.80, 0.60    # card size as a share of the frame width and height
Y = 0.47                       # card center (0 = top, 1 = bottom)
GAP = 0.045                    # space between cards, as a share of the frame width
RADIUS = 0.05                  # corner rounding, as a share of the frame width
DOT = 0.014                    # page dot radius, as a share of the frame width
BLUR, DARKEN = 24, 0.40        # background blur (Gaussian sigma, pixels) and how much darker it gets (0-1)

def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height,r_frame_rate:format=duration", "-of", "json", path]))
    s = info["streams"][0]
    num, den = s["r_frame_rate"].split("/")
    return s["width"], s["height"], s["r_frame_rate"], float(num) / float(den), float(info["format"]["duration"])

def still(path, t):
    """The frame of path at t seconds, fitted to the output size, cropped to the card's box and rounded off."""
    raw = subprocess.check_output(["ffmpeg", "-v", "error", "-ss", str(t), "-i", path, "-frames:v", "1",
                                   "-vf", f"scale={w}:{h}:force_original_aspect_ratio=increase,crop={w}:{h}",
                                   "-f", "rawvideo", "-pix_fmt", "rgb24", "-"])
    frame = Image.frombytes("RGB", (w, h), raw)
    card = frame.crop((card_x, card_y, card_x + card_w, card_y + card_h)).convert("RGBA")
    mask = Image.new("L", card.size)
    ImageDraw.Draw(mask).rounded_rectangle([0, 0, card_w - 1, card_h - 1], round(w * RADIUS), fill=255)
    card.putalpha(mask)
    return card

def shadow():
    """A soft dark shape the size of a card, drawn under each one."""
    pad = 30
    img = Image.new("RGBA", (card_w + 2 * pad, card_h + 2 * pad))
    ImageDraw.Draw(img).rounded_rectangle([pad, pad + 8, pad + card_w, pad + card_h + 8], round(w * RADIUS),
                                          fill=(0, 0, 0, 140))
    return img.filter(ImageFilter.GaussianBlur(14)), pad

def position(t):
    """How many photos along the carousel is at t: 0 on the first photo, 1.5 halfway from the second to the third."""
    return sum(ease((t - start) / SWIPE) for start in SWIPES)

def ease(p):
    p = min(max(p, 0), 1)
    return 3 * p * p - 2 * p * p * p

def overlay(t):
    img = Image.new("RGBA", (w, h))
    pos = position(t)
    step = card_w + round(w * GAP)
    for i, card in enumerate(cards):
        x = card_x + round((i - pos) * step)
        if -card_w < x < w:
            img.alpha_composite(drop, (x - pad, card_y - pad))
            img.alpha_composite(card, (x, card_y))
    draw = ImageDraw.Draw(img)
    r, spacing = w * DOT, w * DOT * 3.6
    first = w / 2 - spacing * (len(cards) - 1) / 2
    dot_y = card_y + card_h + w * 0.07
    for i in range(len(cards)):
        lit = max(0.0, 1 - abs(i - pos))  # the photo on screen gets the solid white dot
        x = first + i * spacing
        draw.ellipse([x - r, dot_y - r, x + r, dot_y + r], fill=(255, 255, 255, round(255 * (0.4 + 0.6 * lit))))
    return img

def render(src, out):
    """Blur and darken src, draw overlay(t) on every frame and save to out, keeping the audio."""
    ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
                           "-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
                           "-filter_complex", f"[0:v]gblur=sigma={BLUR},colorchannelmixer=rr={1 - DARKEN}:gg={1 - DARKEN}:bb={1 - DARKEN}[bg];"
                                              "[bg][1:v]overlay=shortest=1,format=yuv420p[v]",
                           "-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out],
                          stdin=subprocess.PIPE)
    for i in range(math.ceil(duration * fps)):
        ff.stdin.write(overlay(i / fps).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")

clips, out = sys.argv[1:3], sys.argv[3]
w, h, rate, fps, duration = probe(clips[0])
card_w, card_h = round(w * CARD_W), round(h * CARD_H)
card_x, card_y = (w - card_w) // 2, round(h * Y - card_h / 2)
cards = [still(clips[n - 1], t) for n, t in PHOTOS]
drop, pad = shadow()
render(clips[0], out)
```

## More product shots

- [Animated feature callouts **Animated feature callouts**](https://www.recharm.com/effects/animated-feature-callouts)
- [Before/after slider **Before/after slider**](https://www.recharm.com/effects/before-after-slider)
- [Best seller badge **Best seller badge**](https://www.recharm.com/effects/best-seller-badge)
- [Callout arrows and circles **Callout arrows and circles**](https://www.recharm.com/effects/callout-arrows-and-circles)
- [Comparison table **Comparison table**](https://www.recharm.com/effects/comparison-table)

## Find 3x _winning ads_

Recharm cuts your ad footage into tagged, searchable clips: hooks, testimonials, product shots. Find the shots that win, add the effect with Claude, and ship 3x more ad edits.

[Start 14-day free trial](https://www.recharm.com/get-started)
