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
Add an incoming call screen to input.mp4 Caller: Your Skin Status: incoming call...
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.
Add an incoming call screen to @clip1.mp4, put a sale badge on @clip2.mp4, give @clip3.mp4 a VHS look, then join all three with crossfades.
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 |
|---|---|---|
CALLER | Your Skin | Name on the call screen. |
STATUS | incoming call... | Small line above the name. |
END | 2.5 | Second the call is answered and the screen starts to fade. |
FADE | 0.3 | Seconds the fade takes. |
DARKEN | 0.45 | How much the blurred clip behind the call is darkened (0-1). |
ACCEPT, DECLINE | (52, 199, 89), (255, 59, 48) | Button colors. |
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
Make input.mp4 open on a fake incoming phone call. From 0 to 2.5 seconds, show the clip blurred (box blur radius 18, 2 passes) and darkened to 55% brightness, with a call screen over it: "incoming call..." in light gray (235, 235, 240) bold text 4.5% of the frame width tall, centered at 14% down; the caller name "Your Skin" in white bold text 11% of the frame width tall, centered at 21% down; a gray (142, 142, 147) circle 30% of the frame width across centered at 36% down with a white "Y" in it; and two round buttons 20% of the frame width across centered at 82% down: a red (255, 59, 48) Decline button at 25% across and a green (52, 199, 89) Accept button at 75% across, each with a white phone handset icon and a white label ("Decline", "Accept", 4% of the frame width tall) just below. A thin green ring pulses out of the Accept button about once a second. At 2.5 seconds the call screen and the blur fade out over 0.3 seconds, and the clip plays on untouched. Use Python with Pillow to draw the call screen (don't rely on ffmpeg's drawtext filter) and ffmpeg for the blur and compositing, keep the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as incoming-call-screen.py and run:
python3 incoming-call-screen.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Incoming call screen: the clip opens as a phone call from "Your Skin", then the call screen fades away.
Usage: python3 incoming-call-screen.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import math
import subprocess
import sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
CALLER = "Your Skin" # name on the call screen
STATUS = "incoming call..." # small line under the name
END = 2.5 # second the call is answered and the screen starts to fade
FADE = 0.3 # seconds the fade takes
DARKEN = 0.45 # how much the blurred clip behind the call screen is darkened (0-1)
ACCEPT = (52, 199, 89) # answer button color
DECLINE = (255, 59, 48) # decline button color
FONTS = ["/System/Library/Fonts/Supplemental/Arial Bold.ttf", # macOS
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", # Linux
"C:/Windows/Fonts/arialbd.ttf"] # Windows
FONT_FILE = next((f for f in FONTS if Path(f).exists()), None) or sys.exit("No bold font found: add one to FONTS")
def font(share):
return ImageFont.truetype(FONT_FILE, round(w * share))
def handset(draw, cx, cy, r, angle):
"""A phone handset icon: a thick arc with an ear and a mouth piece, turned by `angle` degrees."""
cx -= r * 0.36 * math.cos(math.radians(270 + angle)) # nudge it so the icon, not the arc's circle, is centered
cy -= r * 0.36 * math.sin(math.radians(270 + angle))
draw.arc([cx - r, cy - r, cx + r, cy + r], 200 + angle, 340 + angle, fill="white", width=round(r * 0.38))
for a in (200 + angle, 340 + angle):
ex, ey = cx + r * 0.86 * math.cos(math.radians(a)), cy + r * 0.86 * math.sin(math.radians(a))
draw.ellipse([ex - r * 0.3, ey - r * 0.3, ex + r * 0.3, ey + r * 0.3], fill="white")
def call_screen():
img = Image.new("RGBA", (w, h))
draw = ImageDraw.Draw(img)
draw.text((w / 2, h * 0.14), STATUS, font=font(0.045), anchor="mm", fill=(235, 235, 240))
draw.text((w / 2, h * 0.21), CALLER, font=font(0.11), anchor="mm", fill="white")
r = w * 0.15
draw.ellipse([w / 2 - r, h * 0.36 - r, w / 2 + r, h * 0.36 + r], fill=(142, 142, 147))
draw.text((w / 2, h * 0.36), CALLER[0], font=font(0.15), anchor="mm", fill="white")
for x, color, label, angle in ((0.25, DECLINE, "Decline", 0), (0.75, ACCEPT, "Accept", -135)):
cx, cy, br = w * x, h * 0.82, w * 0.1
draw.ellipse([cx - br, cy - br, cx + br, cy + br], fill=color)
handset(draw, cx, cy, br * 0.5, angle)
draw.text((cx, cy + br + w * 0.05), label, font=font(0.04), anchor="mm", fill="white")
return img
def overlay(t):
"""The call screen, with a ring pulsing out of the answer button, faded out after END."""
img = screen.copy()
pulse = (t * 1.2) % 1
cx, cy, r = w * 0.75, h * 0.82, w * 0.1 * (1 + 0.45 * pulse)
ring = Image.new("RGBA", (w, h))
ImageDraw.Draw(ring).ellipse([cx - r, cy - r, cx + r, cy + r], outline=ACCEPT + (round(200 * (1 - pulse)),),
width=max(2, w // 120))
img = Image.alpha_composite(ring, img)
alpha = min(max(1 - (t - END) / FADE, 0), 1)
if alpha < 1:
img.putalpha(img.getchannel("A").point(lambda a: round(a * alpha)))
return img
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 render(src, out):
"""Blur and darken the clip behind the call screen until END, then fade both away, keeping the audio."""
behind = (f"[0:v]split[clip][b];[b]boxblur=18:2,colorchannelmixer=rr={1 - DARKEN}:gg={1 - DARKEN}:bb={1 - DARKEN},"
f"format=rgba,fade=t=out:st={END}:d={FADE}:alpha=1[blur];[clip][blur]overlay[bg];")
ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
"-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
"-filter_complex", behind + "[bg][1:v]overlay=shortest=1,format=yuv420p[v]",
"-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out],
stdin=subprocess.PIPE)
for i in range(math.ceil(duration * fps)):
ff.stdin.write(overlay(i / fps).tobytes())
ff.stdin.close()
if ff.wait():
sys.exit("ffmpeg failed")
src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
screen = call_screen()
render(src, out)





