How to add a stretch transition with Claude

Clip A slides off to the left as its right edge smears into long horizontal streaks that fill the frame, then clip B pulls in out of the streaks from its own left edge and lands sharp.

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 stretch transition
    Length of the transition in seconds: half smearing A away, half snapping B in: 0.6
    Share of the frame width at the edge that gets stretched into streaks: 0.05

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 stretch transition.

Settings you can change

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

SettingDefaultWhat it does
D0.6Length of the transition in seconds: half smearing A away, half snapping B in.
EDGE0.05Share of the frame width at the edge that gets stretched into streaks.
FPS30Output frame rate.

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 0.6 second stretch transition that starts 0.6 seconds before A ends (the result is 11.4 seconds). Use progress p = frame index / 18 over the 18 transition frames. For p < 0.5, take A's frame, slide it left by s = 540*(2p)^2 pixels, and fill the gap on the right by stretching A's rightmost 27 pixel columns (5% of the width) horizontally across the gap plus their own width, so the right edge smears into flat horizontal streaks. At p = 0.5 the whole frame is streaks. For p >= 0.5, do the mirror image with B's frame: B sits shifted right by s = 540*(2-2p)^2 pixels and its leftmost 27 columns are stretched across the gap on the left, so B slides in from the right out of streaks and slows as it lands. No fade, blur or black. Before the transition A plays normally and B continues full frame afterwards. Crossfade the audio over the same 0.6 seconds. Fit B to A's size first. Output 540x960, 30fps, H.264 yuv420p. Use ffmpeg with Python and numpy for the per-frame column remapping, keep the audio and save it as output.mp4.

Run it without Claude

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

python3 stretch-transition.py clipA.mp4 clipB.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Stretch transition: clip A slides off to the left while its right edge smears into long streaks, then clip B snaps in out of streaks from its own left edge.

Usage: python3 stretch-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
from itertools import islice

import numpy as np

D = 0.6       # transition length in seconds: half smearing A away, half snapping B in
EDGE = 0.05   # share of the frame width at the edge that gets stretched into streaks
FPS = 30      # output frame rate


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):
    """Decode a clip as w x h RGB frames at FPS, cropping it to fill the frame if its shape differs."""
    ff = subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf",
                           f"scale={w}:{h}:force_original_aspect_ratio=increase,crop={w}:{h},fps={FPS}",
                           "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], stdout=subprocess.PIPE)
    while True:
        data = ff.stdout.read(w * h * 3)
        if len(data) < w * h * 3:
            return
        yield np.frombuffer(data, np.uint8).reshape(h, w, 3)


def smear(img, shift):
    """Slide the picture `shift` pixels left and stretch its rightmost EDGE strip across the gap it leaves."""
    edge = round(w * EDGE)
    start = w - shift - edge                 # output column where the stretched strip begins
    x = np.arange(w)
    src = np.where(x < start, x + shift, (w - edge) + (x - start) * edge / (shift + edge))
    return img[:, np.minimum(src.astype(int), w - 1)]


def stretch(a, b, p):
    """One transition frame at progress p (0-1): A smears away until the frame is all streaks, then B snaps back."""
    if p < 0.5:
        return smear(a, round(w * (2 * p) ** 2))                     # speeds up as it leaves
    return smear(b[:, ::-1], round(w * (2 - 2 * p) ** 2))[:, ::-1]   # mirrored: B's left edge, slowing as it lands


A, B, out = sys.argv[1:4]
w, h, length = probe(A)
cut_in = round((length - D) * FPS)   # first frame of the transition
n = round(D * FPS)                   # frames in the transition

enc = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                        "-r", str(FPS), "-i", "-", "-i", A, "-i", B,
                        "-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)
clip_a, clip_b = frames(A), frames(B)
for a in islice(clip_a, cut_in):
    enc.stdin.write(a.tobytes())
for i, (a, b) in enumerate(zip(islice(clip_a, n), islice(clip_b, n))):
    enc.stdin.write(np.ascontiguousarray(stretch(a, b, i / n)).tobytes())
for b in clip_b:
    enc.stdin.write(b.tobytes())
enc.stdin.close()
if enc.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