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 a keystroke overlay to input.mp4 Presses: Cmd + C at 0.8-1.9s, Cmd + V at 2.2-3.3s, Shift + Cmd + Z at 3.6-4.9s
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 a keystroke overlay 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 |
|---|---|---|
PRESSES | Cmd + C at 0.8-1.9s, Cmd + V at 2.2-3.3s, Shift + Cmd + Z at 3.6-4.9s | Each shortcut's start and end second and the keys held together. |
Y | 0.82 | Where the row of keys is centered (0 = top, 1 = bottom). |
KEY_H | 0.13 | Key cap height as a share of the frame width. |
POP | 0.15 | Seconds a shortcut takes to pop in and to fade out. |
CAP, EDGE, INK | (255, 255, 255), (170, 170, 178), (25, 25, 30) | Key cap face, raised edge and label 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
Add a keystroke overlay to input.mp4 that shows three keyboard shortcuts as key caps: "Cmd" + "C" from 0.8 to 1.9 seconds, "Cmd" + "V" from 2.2 to 3.3 seconds and "Shift" + "Cmd" + "Z" from 3.6 to 4.9 seconds. Draw each key as a pure white (255, 255, 255) rounded key cap 13% of the frame width tall (at least as wide as it is tall) with its name in dark (25, 25, 30) bold text at 42% of the key height, a gray (170, 170, 178) edge peeking out 8% of the key height below it so it looks raised, and a white "+" between keys. Put each shortcut's keys on a dark rounded panel (15, 15, 20) at 75% opacity, centered across the frame and 82% of the way down. Each shortcut pops in over 0.15 seconds, growing from 85% to full size with a slight overshoot, and fades out over its last 0.15 seconds; nothing shows between shortcuts. Use Python with Pillow to draw the keys (don't rely on ffmpeg's drawtext filter) and ffmpeg to put them on 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 keystroke-overlay.py and run:
python3 keystroke-overlay.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Keystroke overlay: white key caps pop up near the bottom showing each shortcut as it is pressed.
Usage: python3 keystroke-overlay.py input.mp4 output.mp4
The shortcuts and their times go in PRESSES below, from your recorder's key log or written by hand.
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
PRESSES = [(0.8, 1.9, ["Cmd", "C"]), # (start second, end second, keys held together)
(2.2, 3.3, ["Cmd", "V"]),
(3.6, 4.9, ["Shift", "Cmd", "Z"])]
Y = 0.82 # where the row of keys is centered (0 = top, 1 = bottom)
KEY_H = 0.13 # key cap height as a share of the frame width
POP = 0.15 # seconds a combo takes to pop in (and to fade out)
CAP = (255, 255, 255) # key cap face
EDGE = (170, 170, 178) # key cap's lower edge, which makes it look raised
INK = (25, 25, 30) # key label color
PANEL = (15, 15, 20, 190) # dark rounded panel behind the keys
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 combo(keys):
"""One shortcut drawn as raised key caps joined by plus signs on a dark panel."""
kh = round(w * KEY_H)
font = ImageFont.truetype(FONT_FILE, round(kh * 0.42))
edge, gap, pad = round(kh * 0.08), round(kh * 0.45), round(kh * 0.28)
widths = [max(kh, round(font.getlength(k) + kh * 0.6)) for k in keys]
width = sum(widths) + gap * (len(keys) - 1) + 2 * pad
img = Image.new("RGBA", (width, kh + edge + 2 * pad))
draw = ImageDraw.Draw(img)
draw.rounded_rectangle([0, 0, img.width - 1, img.height - 1], radius=pad, fill=PANEL)
x = pad
for i, (key, kw) in enumerate(zip(keys, widths)):
r = round(kh * 0.18)
draw.rounded_rectangle([x, pad + edge, x + kw, pad + kh + edge], radius=r, fill=EDGE)
draw.rounded_rectangle([x, pad, x + kw, pad + kh], radius=r, fill=CAP)
draw.text((x + kw / 2, pad + kh / 2), key, font=font, anchor="mm", fill=INK)
x += kw
if i < len(keys) - 1:
draw.text((x + gap / 2, pad + kh / 2), "+", font=font, anchor="mm", fill="white")
x += gap
return img
def overlay(t):
img = Image.new("RGBA", (w, h))
for (start, end, _), card in zip(PRESSES, cards):
if start <= t < end:
scale = 0.85 + 0.15 * ease_out_back((t - start) / POP)
fade = min(1, (end - t) / POP)
c = card.resize((round(card.width * scale), round(card.height * scale)), Image.LANCZOS)
if fade < 1:
c.putalpha(c.getchannel("A").point(lambda a: round(a * fade)))
img.alpha_composite(c, (round(w / 2 - c.width / 2), round(h * Y - c.height / 2)))
return img
def ease_out_back(p):
"""0 -> 1 with a small overshoot past 1 before settling, which reads as a 'pop'."""
p = min(max(p, 0), 1)
return 1 + 2.70158 * (p - 1) ** 3 + 1.70158 * (p - 1) ** 2
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):
"""Draw overlay(t) on every frame of src and save to out, keeping the audio."""
ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
"-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
"-filter_complex", "[0:v][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)
cards = [combo(keys) for _, _, keys in PRESSES]
render(src, out)





