---
title: "How to add a cloud transition with Claude (tested prompt and script)"
description: "The first shot sinks and zooms as if the camera were climbing, white clouds stream down from the top until the frame is all cloud, then they part from the middle outward to reveal the second shot. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/cloud-transition
---

# How to add a cloud transition with Claude

The first shot sinks and zooms as if the camera were climbing, white clouds stream down from the top until the frame is all cloud, then they part from the middle outward to reveal the second shot.

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

   ```
   Join clipA.mp4 and clipB.mp4 with a cloud transition
   Length of the transition in seconds; the cut to B happens unseen at the midpoint: 2.0
   Share of the transition the frame stays fully covered around the cut: 0.1
   ```

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

Put a sale badge on **@clip1.mp4**, give **@clip2.mp4** a VHS look, then join them with a cloud transition.

## 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                                                                      |
| -------- | ----------------- | --------------------------------------------------------------------------------- |
| `D`      | `2.0`             | Length of the transition in seconds; the cut to B happens unseen at the midpoint. |
| `HOLD`   | `0.1`             | Share of the transition the frame stays fully covered around the cut.             |
| `LIGHT`  | `(248, 249, 251)` | Sunlit cloud color.                                                               |
| `SHADOW` | `(206, 212, 222)` | Cloud color in the shadowed folds.                                                |
| `RISE`   | `900`             | How fast the clouds stream down past the camera, in pixels per second.            |
| `CLIMB`  | `0.18`            | How far clip A sinks as the camera rises, as a share of the frame height.         |
| `PUSH`   | `1.25`            | Zoom on A as it disappears and on B as it first shows; B settles back to 1.       |
| `BLOB`   | `150`             | Rough size of the cloud puffs, in pixels.                                         |
| `SOFT`   | `0.14`            | Width of the feathered cloud edge (0 = hard edge).                                |

## Without the skill

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

```
Join clipA.mp4 and clipB.mp4 (both 540x960, 6 seconds) with a 2 second cloud transition, using Python with numpy and ffmpeg to read and write the frames. Make the clouds from soft cloudy noise (random noise low-pass filtered so the puffs are about 150 pixels across) that streams downward at 900 pixels per second and swells to 1.6 times its size over the transition, so it feels like flying up through it; shade it from sunlit (248, 249, 251) to a cool gray (206, 212, 222) in the folds, with a feathered edge. The transition runs from 4.0 to 6.0 seconds of the output. From 4.0 to 4.9 seconds the cloud bank comes down from the top edge and fills the frame top first while clip A zooms in around its center from 1.0 to 1.25 and slides down by 18% of the frame height (smoothstep easing), as if the camera were rising. The frame is fully covered from 4.9 to 5.1 seconds and the cut from A to B happens hidden behind it at 5.0 seconds. From 5.1 to 6.0 seconds a hole opens in the middle of the frame and widens out to the corners, while clip B settles from 1.25 zoom back to 1.0 (smoothstep), so at 5.45 seconds the center of B is clear and the top and bottom edges are still cloud. B then plays to its end, for a 10 second video at 30fps. Crossfade the audio over the same 2 seconds, don't use ffmpeg's drawtext filter, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 cloud-transition.py clipA.mp4 clipB.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Cloud transition: the camera climbs out of clip A into a white cloud bank, then the clouds part from the middle to reveal clip B.

Usage: python3 cloud-transition.py clipA.mp4 clipB.mp4 output.mp4  (both clips need an audio track)
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys

import numpy as np

D = 2.0                     # transition length in seconds; the cut to B happens unseen at the midpoint
HOLD = 0.1                  # share of D the frame stays fully white around the cut
LIGHT = (248, 249, 251)     # sunlit cloud color
SHADOW = (206, 212, 222)    # cloud color in the shadowed folds, a cool gray
RISE = 900                  # how fast the clouds stream down past the camera, in pixels per second
CLIMB = 0.18                # how far clip A sinks as the camera rises, as a share of the frame height
PUSH = 1.25                 # zoom at the moment A disappears, and B's zoom when it first shows (settles to 1)
BLOB = 150                  # rough size of the cloud puffs, in pixels
SOFT = 0.14                 # width of the feathered cloud edge (0 = hard edge)
SEED = 7                    # change for a different cloud pattern
FPS = 30

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

def frames(path):
    """Every frame of a clip, fitted to A's size at 30fps, as a float RGB array."""
    fit = f"scale={w}:{h}:force_original_aspect_ratio=increase,crop={w}:{h},fps={FPS}"
    ff = subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf", fit, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
                          stdout=subprocess.PIPE)
    while chunk := ff.stdout.read(w * h * 3):
        yield np.frombuffer(chunk, np.uint8).reshape(h, w, 3).astype(np.float32)

def clouds(rng, height, width, size):
    """Soft cloudy noise (0-1) that wraps at the edges: random noise with its fine detail filtered out."""
    fy = np.fft.fftfreq(height)[:, None]
    fx = np.fft.fftfreq(width)[None, :]
    f = np.hypot(fx, fy) * size
    field = np.fft.ifft2(np.fft.fft2(rng.standard_normal((height, width))) * np.exp(-f ** 2) / np.maximum(f, 0.3)).real
    return ((field - field.min()) / (field.max() - field.min())).astype(np.float32)

def camera(img, zoom, drop):
    """The frame scaled up by zoom around its center and moved down by drop pixels (the camera rising past it)."""
    rows = np.clip(((ys - h / 2 - drop) / zoom + h / 2).astype(int), 0, h - 1)
    cols = np.clip(((xs - w / 2) / zoom + w / 2).astype(int), 0, w - 1)
    return img[rows][:, cols]

def ease(p):
    return p * p * (3 - 2 * p)

def cloud_layer(p):
    """Cloud density (0-1) and shading at progress p: the puffs stream downward and swell as the camera flies in."""
    swell = 1 + 0.6 * p
    rows = ((ys - h / 2) / swell + h / 2 - RISE * p * D).astype(int) % puffs.shape[0]
    cols = ((xs - w / 2) / swell + w / 2).astype(int) % puffs.shape[1]
    return puffs[rows][:, cols], folds[rows][:, cols]

def frame_at(a, b, p):
    """Frame at progress p (0-1): clouds roll in from the top over A, hold, then part from the center over B."""
    density, shade = cloud_layer(p)
    if p < 0.5:
        amount = min(p / (0.5 - HOLD / 2), 1)
        k = ease(amount)
        base = camera(a, 1 + (PUSH - 1) * k, CLIMB * h * k)
        threshold = 0.5 * density + 0.5 * y                          # the cloud bank comes down from above
    else:
        amount = min((1 - p) / (0.5 - HOLD / 2), 1)
        base = camera(b, 1 + (PUSH - 1) * ease(amount), 0)
        threshold = 0.5 * density + 0.5 * (1 - radius)                # a hole opens in the middle and widens
    alpha = np.clip((amount * (1 + SOFT) - threshold) / SOFT, 0, 1)[..., None]
    color = light + (shadow - light) * shade[..., None]
    return base * (1 - alpha) + color * alpha

a_path, b_path, out = sys.argv[1:4]
w, h, a_len = probe(a_path)
rng = np.random.default_rng(SEED)
puffs = clouds(rng, 2 * h, w, BLOB)
folds = clouds(rng, 2 * h, w, BLOB / 2) ** 2
light, shadow = np.array(LIGHT, np.float32), np.array(SHADOW, np.float32)
ys, xs = np.arange(h, dtype=np.float32), np.arange(w, dtype=np.float32)
y = np.linspace(0, 1, h, dtype=np.float32)[:, None] * np.ones((1, w), np.float32)
radius = np.hypot((xs[None, :] - w / 2) / (w / 2), (ys[:, None] - h / 2) / (h / 2)) / np.sqrt(2)
start = round((a_len - D) * FPS)    # first frame of the transition
steps = round(D * FPS)
ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                       "-r", str(FPS), "-i", "-", "-i", a_path, "-i", b_path,
                       "-filter_complex", f"[1:a][2:a]acrossfade=d={D}[au]", "-map", "0:v", "-map", "[au]",
                       "-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p", "-c:a", "aac", out], stdin=subprocess.PIPE)
b_frames = frames(b_path)
for i, a in enumerate(frames(a_path)):
    if i >= start:
        a = frame_at(a, next(b_frames), (i - start + 0.5) / steps)
    ff.stdin.write(np.clip(a, 0, 255).astype(np.uint8).tobytes())
for b in b_frames:
    ff.stdin.write(b.astype(np.uint8).tobytes())
ff.stdin.close()
if ff.wait():
    sys.exit("ffmpeg failed")
```

## More transitions

- [Barn door transition **Barn door transition**](https://www.recharm.com/effects/barn-door)
- [Blinds transition **Blinds transition**](https://www.recharm.com/effects/blinds-transition)
- [Block dissolve **Block dissolve**](https://www.recharm.com/effects/block-dissolve)
- [Blur transition **Blur transition**](https://www.recharm.com/effects/blur-transition)
- [Bounce transition **Bounce transition**](https://www.recharm.com/effects/bounce-transition)

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