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 hexagon transition Length of the transition in seconds: 1.2 Hexagon size from center to corner, in pixels: 60
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 hexagon 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.2 | Length of the transition in seconds. |
R | 60 | Hexagon size from center to corner, in pixels. |
GROW | 0.5 | Share of the transition each cell takes to open; the rest staggers the cells outward from the center. |
RIM | 0.08 | Thickness of the white rim, as a share of a cell's center-to-edge distance (about 4 pixels). |
RIM_COLOR | (255, 255, 255) | Color of the rim on each growing hexagon. |
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 (both 540x960, 30fps) with a 1.2 second hexagon transition starting 1.2 seconds before A ends. Lay a honeycomb of pointy-top hexagons over the frame, 60 pixels from center to corner (104 pixels across the flat sides), with one cell centered exactly on the frame's center (270, 480). Each cell opens onto clip B as a hexagon growing from the cell's center, eased in and out (smoothstep) over 0.6 seconds, growing to 1.05 times the cell's size so neighbors close with no seams. The middle cell starts at once and the others start later in proportion to their centers' distance from the frame's center, the furthest cell starting 0.6 seconds in, so the honeycomb spreads outward and the whole transition ends at 1.2 seconds. While a cell is growing, draw a 4 pixel pure white (255, 255, 255) rim just inside its hexagon's edge; no rims before or after the transition. Crossfade the audio over the same 1.2 seconds. Match B's size and frame rate to A. Use Python with numpy (and Pillow for any drawing, not ffmpeg's drawtext) and ffmpeg, keep the audio and save it as output.mp4 (H.264, yuv420p, 30fps, 10.8 seconds long).Run it without Claude
This is the exact script we tested. Save it as hexagon-transition.py and run:
python3 hexagon-transition.py clipA.mp4 clipB.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Hexagon transition: honeycomb cells grow open from the center outward, each with a white rim, swapping clip A for clip B over 1.2s.
Usage: python3 hexagon-transition.py clipA.mp4 clipB.mp4 output.mp4 (both clips need an audio track)
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import subprocess
import sys
from itertools import islice
import numpy as np
D = 1.2 # transition length in seconds
FPS = 30 # output frame rate
R = 60 # hexagon size: center to corner, in pixels
GROW = 0.5 # share of the transition each cell takes to open
RIM = 0.08 # rim thickness, as a share of the cell's center-to-edge distance (about 4 px)
RIM_COLOR = (255, 255, 255)
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 honeycomb():
"""For every pixel: how far it sits from its cell's center (0 at the center, 1 on the cell's edge),
and when its cell starts to open (0 for the middle cell, 1 - GROW for the cells furthest out)."""
y, x = np.mgrid[0:h, 0:w].astype(np.float32)
x, y = x - w / 2, y - h / 2 # a cell sits right on the frame's center
# pointy-top hex grid: pixel -> axial (q, r), rounded to the nearest cell in cube coordinates
q, r = (np.sqrt(3) / 3 * x - y / 3) / R, (2 / 3 * y) / R
s = -q - r
rq, rr, rs = np.round(q), np.round(r), np.round(s)
dq, dr, ds = abs(rq - q), abs(rr - r), abs(rs - s)
rq = np.where((dq > dr) & (dq > ds), -rr - rs, rq)
rr = np.where((dr >= dq) & (dr > ds), -rq - rs, rr)
cx, cy = R * np.sqrt(3) * (rq + rr / 2), R * 1.5 * rr
# distance to the cell's center measured against its three pairs of edges
ox, oy = np.abs(x - cx), np.abs(y - cy)
edge = np.maximum(ox, ox / 2 + oy * np.sqrt(3) / 2) / (R * np.sqrt(3) / 2)
reach = np.hypot(cx, cy)
return edge, reach / reach.max() * (1 - GROW)
def hexagons(a, b, p):
"""One transition frame at progress p (0-1): each cell shows B inside a hexagon growing from its center."""
local = np.clip((p - start) / GROW, 0, 1)
size = local * local * (3 - 2 * local) * 1.05 # smoothstep, a touch past 1 so cells close with no seams
img = np.where((edge < size)[..., None], b, a)
img[(edge < size) & (edge >= size - RIM) & (local < 1)] = RIM_COLOR
return img
A, B, out = sys.argv[1:4]
w, h, length = probe(A)
edge, start = honeycomb()
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(hexagons(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")





