Get this effect
Switch to Code in your Claude Desktop App
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/skillsAsk for the effect
Join clipA.mp4 and clipB.mp4 with a lens flare transition Ghosts: 1.5, 2.0, 2.6 along the line
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 lens flare 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 | 1.0 | Length of the transition in seconds; the clips overlap by this much. |
PATH | (-0.25, 0.30), (1.25, 0.55) | Where the flare enters and leaves, 0-1 across and down the frame. |
CORE | 0.06 | Radius of the white-hot center, as a share of the frame width. |
HALO | 0.30 | Reach of the golden glow, as a share of the frame width. |
RAYS | 14 | Number of light rays around the center. |
CORE_COLOR, HALO_COLOR | (255, 250, 235), (255, 150, 30) | Colors of the center and streak, and of the glow, rays and wash. |
GHOSTS | 1.5, 2.0, 2.6 along the line | Lens reflections: distance along the line through the frame center, radius and color of each. |
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.0 second lens flare transition. Fit B to A's size (540x960) and frame rate (30fps), overlap the last 1.0 seconds of A with the first 1.0 seconds of B so the output is 11.0 seconds long, and crossfade the audio over the same 1.0 seconds. Outside the overlap, show A and B untouched. In the overlap, with p going from 0 to 1: the picture is A until p = 0.4, changes to B with a smoothstep between p = 0.4 and 0.6, and is B after that. On top, add a flare whose center moves in a straight line from (-25% across, 30% down) to (125% across, 55% down) as p goes from 0 to 1, built from: a warm white (255, 250, 235) core, exp(-(r/(0.06*W))^2) with r the distance in pixels from the center and W the frame width, plus a horizontal streak of the same color through the center, exp(-(dy/5)^2) * exp(-|dx|/(0.7*W)); and in gold (255, 150, 30) a glow exp(-r/(0.30*W)), 14 rays 0.6 * (0.5 + 0.5*cos(14*angle + 3p))^12 * exp(-r/(0.45*W)), and a 0.4 gold wash over the whole frame. Add three lens reflections as soft-edged discs at 0.3 strength on the line from the flare center through the frame center, at 1.5, 2.0 and 2.6 times that distance from the flare, with radii 5%, 9% and 4% of the width and colors (120, 255, 170), (255, 140, 40) and (110, 190, 255). Clip the light to 0-1, multiply it by sin(pi*p) so it brightens and dies away, and combine it with the picture by a screen blend (1 - (1 - frame) * (1 - light), values 0-1). Use Python with numpy for the flare and ffmpeg to read and write the video, keep the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as lens-flare-transition.py and run:
python3 lens-flare-transition.py clipA.mp4 clipB.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Lens flare transition: a golden starburst flare sweeps across clip A and clip B is on screen once it has passed.
Usage: python3 lens-flare-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 = 1.0 # transition length in seconds (the clips overlap by this much)
PATH = (-0.25, 0.30), (1.25, 0.55) # where the flare enters and leaves (0-1 across and down the frame)
CORE = 0.06 # radius of the white-hot center, as a share of the frame width
HALO = 0.30 # reach of the golden glow around it, as a share of the frame width
RAYS = 14 # number of light rays around the center
CORE_COLOR = (255, 250, 235) # the hot center and the horizontal streak: warm white
HALO_COLOR = (255, 150, 30) # the glow, the rays and the wash over the frame: gold
# lens reflections: (distance along the line through the frame center, radius as a share of the width, color)
GHOSTS = [(1.5, 0.05, (120, 255, 170)), (2.0, 0.09, (255, 140, 40)), (2.6, 0.04, (110, 190, 255))]
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 flare(p):
"""The flare as an RGB light layer (0-1) at progress p (0-1) through the transition."""
(x0, y0), (x1, y1) = PATH
cx, cy = (x0 + (x1 - x0) * p) * w, (y0 + (y1 - y0) * p) * h
dx, dy = x - cx, y - cy
r = np.hypot(dx, dy)
core = np.exp(-(r / (CORE * w)) ** 2)
streak = np.exp(-(dy / 5) ** 2) * np.exp(-np.abs(dx) / (0.7 * w))
halo = np.exp(-r / (HALO * w))
rays = 0.6 * (0.5 + 0.5 * np.cos(RAYS * np.arctan2(dy, dx) + 3 * p)) ** 12 * np.exp(-r / (0.45 * w))
layer = np.minimum(core + streak, 1)[..., None] * core_rgb + (halo + rays + 0.4)[..., None] * halo_rgb
for k, radius, color in GHOSTS: # reflections sit on the line from the flare through the frame center
gx, gy = cx + k * (w / 2 - cx), cy + k * (h / 2 - cy)
disc = np.clip((radius * w - np.hypot(x - gx, y - gy)) / 4, 0, 1) * 0.3
layer += disc[..., None] * np.array(color) / 255
return np.minimum(layer, 1)
def reader(path, filters):
return subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf", filters, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
stdout=subprocess.PIPE)
a_path, b_path, out = sys.argv[1:4]
w, h, rate, fps, a_len = probe(a_path)
y, x = np.mgrid[0:h, 0:w].astype(np.float32)
core_rgb, halo_rgb = np.array(CORE_COLOR) / 255, np.array(HALO_COLOR) / 255
start = round((a_len - D) * fps) # first frame of the overlap
frames = round(D * fps)
# B is fitted to A's size and frame rate, and the sound crossfades over the same D seconds
a = reader(a_path, f"fps={rate}")
b = reader(b_path, f"scale={w}:{h}:force_original_aspect_ratio=increase,crop={w}:{h},fps={rate}")
writer = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
"-r", rate, "-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)
size = w * h * 3
for _ in range(start):
writer.stdin.write(a.stdout.read(size))
for i in range(frames):
p = (i + 0.5) / frames
fa = np.frombuffer(a.stdout.read(size), np.uint8).reshape(h, w, 3) / 255.0
fb = np.frombuffer(b.stdout.read(size), np.uint8).reshape(h, w, 3) / 255.0
swap = np.clip((p - 0.4) / 0.2, 0, 1) # A turns into B while the flare is brightest
base = fa + (fb - fa) * swap * swap * (3 - 2 * swap)
light = flare(p) * np.sin(np.pi * p) # the flare brightens, then dies away
writer.stdin.write(((1 - (1 - base) * (1 - light)) * 255).round().astype(np.uint8).tobytes()) # screen blend
while raw := b.stdout.read(size):
writer.stdin.write(raw)
writer.stdin.close()
if writer.wait() or a.wait() or b.wait():
sys.exit("ffmpeg failed")





