How to add a pinwheel wipe with Claude

Four curved blades edged in white spin clockwise out from the center, each one widening until clip B fills the frame.

Get this effect

  1. Switch to Code in your Claude Desktop App

  2. Install the skill (once)

    Show the prompt
    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 pinwheel wipe
    Length of the transition in seconds: 1.0
    Number of blades on the pinwheel: 4

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 pinwheel wipe.

Settings you can change

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

SettingDefaultWhat it does
D1.0Length of the transition in seconds.
BLADES4Number of blades on the pinwheel.
TWIST70How far each blade's edge bends between the center and the frame's corners, in degrees.
LINE4Width of the white line along each blade's edge in pixels; 0 for none.

Without the skill

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

Show the prompt
Join clipA.mp4 and clipB.mp4 with a 1 second pinwheel wipe starting 1 second before A ends (output 11 seconds for two 6 second clips). Split the frame into 4 equal blades around its center, the first starting at twelve o'clock. Each blade's edge curves: its angle shifts clockwise by 70 degrees from the center out to the frame's corners. Over the second, every blade of B grows clockwise from its leading edge until it covers its whole quarter, eased in and out (smoothstep), so all four sweep at once. Draw a 4 pixel pure white (255,255,255) line along every blade edge while the wipe runs, and smooth the edges over about a pixel. Fit B to A's size at 30fps. Build the mask per frame with numpy and composite with Python, piping frames to ffmpeg. Crossfade the audio over the same 1 second and keep it. Save it as output.mp4 (H.264, yuv420p).

Run it without Claude

This is the exact script we tested. Save it as pinwheel-wipe.py and run:

python3 pinwheel-wipe.py clipA.mp4 clipB.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Pinwheel wipe: four curved blades of clip B sweep clockwise around the center of clip A until B fills the frame.

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

import numpy as np

D = 1.0          # transition length in seconds
BLADES = 4       # blades on the pinwheel
TWIST = 70       # how far each blade's edge bends between the center and the frame's corners, in degrees
LINE = 4         # width of the white line along each blade's edge, in pixels (0 for none)
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 an 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 True:
        chunk = ff.stdout.read(w * h * 3)
        if not chunk:
            break
        yield np.frombuffer(chunk, np.uint8).reshape(h, w, 3)


def blade_layout():
    """For every pixel: where it sits within its blade's slice (0-1, clockwise from the blade's leading edge,
    bent by TWIST further out), and how many pixels one whole slice spans at its distance from the center."""
    y, x = np.mgrid[0:h, 0:w].astype(np.float32)
    dx, dy = x + 0.5 - w / 2, y + 0.5 - h / 2
    r = np.hypot(dx, dy)
    angle = np.arctan2(dx, -dy) - math.radians(TWIST) * r / r.max()    # 0 at twelve o'clock, clockwise
    slice_angle = 2 * math.pi / BLADES
    return (angle / slice_angle) % 1, np.maximum(r, 1) * slice_angle


def pinwheel_wipe(a, b, p):
    """Mix of A and B at progress p (0-1): each blade of B covers the first p of its slice (easing in and out),
    with a white line along its edges, all smoothed over about a pixel."""
    eased = p * p * (3 - 2 * p)
    inside = place < eased
    edge = np.where(inside, np.minimum(place, eased - place), -np.minimum(place - eased, 1 - place)) * span
    mask = np.clip(0.5 + edge, 0, 1)[..., None]
    line = np.clip(LINE / 2 + 0.5 - np.abs(edge), 0, 1)[..., None]
    mixed = a * (1 - mask) + b * mask
    return (mixed * (1 - line) + 255 * line).astype(np.uint8)


a_path, b_path, out = sys.argv[1:4]
w, h, a_len = probe(a_path)
place, span = blade_layout()
start = round((a_len - D) * FPS)    # first frame of the transition
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 = pinwheel_wipe(a, next(b_frames), (i - start + 1) / (round(D * FPS) + 1))
    ff.stdin.write(a.tobytes())
for b in b_frames:
    ff.stdin.write(b.tobytes())
ff.stdin.close()
if ff.wait():
    sys.exit("ffmpeg failed")

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