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 rubber stamp text to input.mp4 Text: APPROVED
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 rubber stamp text 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 |
|---|---|---|
TEXT | APPROVED | The word on the stamp. |
LAND | 0.8 | Second the stamp hits the frame. |
DROP | 0.12 | Seconds it takes to come down onto the frame. |
START_SCALE | 2.5 | How big the stamp starts its drop, relative to its final size. |
WIDTH | 0.74 | Stamp width, border included, as a share of the frame width. |
X, Y | 0.5, 0.13 | Stamp center (0-1 across and down the frame). |
INK | (205, 30, 40) | Ink color. |
OPACITY | 0.92 | Ink opacity, so a little of the picture shows through. |
TILT | 12 | Rotation in degrees, counterclockwise. |
WEAR | 0.3 | Share of the ink knocked out by the worn, patchy print. |
JOLT | 10 | How far the frame jumps on impact, in pixels. |
JOLT_LEN | 0.3 | Seconds until the jolt settles. |
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 rubber stamp to input.mp4: the word "APPROVED" in a heavy font (Arial Black) in red ink (205, 30, 40) at 92% opacity, inside a rounded rectangle border, with a thick outer line (14% of the letter size) and a thin inner line (5% of the letter size). The stamp, border included, is 74% of the frame width wide and 1.7 times the letter size tall, with the word 80% of the stamp's width; tilt it 12 degrees counterclockwise and center it 50% across and 13% down. Make the ink look worn: mix soft blotchy noise with fine speckle (numpy is fine) and leave out the weakest 30% of the ink so the print is patchy. The stamp comes down over 0.12 seconds and hits the frame at 0.8 seconds: it starts at 250% size and fully transparent, shrinks to 100% while fading in, speeding up as it drops, then stays to the end. On impact, jolt the whole frame (stamp included) about 10 pixels in changing directions with an extra 4% zoom so no edge shows, both dying down to still over 0.3 seconds. Nothing changes before 0.68 seconds. Draw the stamp with Python and Pillow (don't rely on ffmpeg's drawtext filter), keep the size and audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as rubber-stamp-text.py and run:
python3 rubber-stamp-text.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Rubber stamp text: a worn red "APPROVED" stamp slams down onto the frame, tilted, and the picture jolts.
Usage: python3 rubber-stamp-text.py input.mp4 output.mp4
Needs ffmpeg, Pillow and numpy (pip install pillow numpy).
"""
import json
import math
import subprocess
import sys
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFont
TEXT = "APPROVED"
LAND = 0.8 # second the stamp hits the frame
DROP = 0.12 # seconds it takes to come down onto the frame
START_SCALE = 2.5 # how big the stamp starts its drop, relative to its final size
WIDTH = 0.74 # stamp width (border included) as a share of the frame width
X, Y = 0.5, 0.13 # stamp center (0-1 across and down the frame)
INK = (205, 30, 40) # ink color
OPACITY = 0.92 # ink opacity, so a little of the picture shows through
TILT = 12 # degrees, counterclockwise
WEAR = 0.3 # share of the ink knocked out by the worn, patchy print
JOLT = 10 # how far the frame jumps on impact, in pixels
JOLT_LEN = 0.3 # seconds until the jolt settles
FONTS = ["/System/Library/Fonts/Supplemental/Arial Black.ttf", # macOS
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", # Linux
"C:/Windows/Fonts/ariblk.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 stamp():
"""The stamp drawn once: the word inside a thick and a thin border, worn patchy, tilted."""
box_w = round(w * WIDTH)
probe_font = ImageFont.truetype(FONT_FILE, 100)
x0, y0, x1, y1 = probe_font.getbbox(TEXT)
font = ImageFont.truetype(FONT_FILE, round(100 * box_w * 0.8 / (x1 - x0)))
thick, thin = round(font.size * 0.14), round(font.size * 0.05)
box_h = round(font.size * 1.7)
ink = Image.new("L", (box_w, box_h))
draw = ImageDraw.Draw(ink)
draw.rounded_rectangle([0, 0, box_w - 1, box_h - 1], radius=thick * 2, outline=255, width=thick)
gap = thick + thin * 2
draw.rounded_rectangle([gap, gap, box_w - 1 - gap, box_h - 1 - gap], radius=thick, outline=255, width=thin)
draw.text((box_w / 2, box_h / 2), TEXT, font=font, anchor="mm", fill=255)
# wear: soft blotches plus fine speckle, and the weakest WEAR share of the ink doesn't print
rng = np.random.default_rng(7)
blotches = Image.fromarray((rng.random((box_h // 14 + 2, box_w // 14 + 2)) * 255).astype(np.uint8))
blotches = np.asarray(blotches.resize((box_w, box_h), Image.BICUBIC), np.float32) / 255
worn = 0.6 * blotches + 0.4 * rng.random((box_h, box_w))
printed = worn > np.quantile(worn[np.asarray(ink) > 0], WEAR)
alpha = (np.asarray(ink, np.float32) * printed * OPACITY).astype(np.uint8)
img = Image.new("RGBA", (box_w, box_h), INK)
img.putalpha(Image.fromarray(alpha))
return img.rotate(TILT, resample=Image.BICUBIC, expand=True)
def add_stamp(img, t):
if t < LAND - DROP:
return img
p = min((t - (LAND - DROP)) / DROP, 1)
scale = START_SCALE + (1 - START_SCALE) * p * p # speeds up as it comes down, like a hand pressing
s = full.resize((round(full.width * scale), round(full.height * scale)), Image.LANCZOS)
if p < 1:
s.putalpha(s.getchannel("A").point(lambda a: a * p))
img.alpha_composite(s, (round(w * X - s.width / 2), round(h * Y - s.height / 2)))
return img
def jolt(img, t):
"""Knock the whole frame sideways and down on impact, settling over JOLT_LEN (a slight zoom hides the edges)."""
s = t - LAND
if s < 0 or s >= JOLT_LEN:
return img
k = 1 - s / JOLT_LEN
zoom = 1 + 0.04 * k
dx, dy = JOLT * k * math.sin(t * 83), JOLT * k * math.cos(t * 61)
cw, ch = w / zoom, h / zoom
x0, y0 = (w - cw) / 2 + dx / zoom, (h - ch) / 2 + dy / zoom
return img.resize((w, h), Image.BICUBIC, box=(x0, y0, x0 + cw, y0 + ch))
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):
"""Read every frame of src, stamp and jolt it, and save to out with the audio."""
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
stdout=subprocess.PIPE)
writer = subprocess.Popen(["ffmpeg", "-y", "-v", "error",
"-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
"-i", src, "-map", "0:v", "-map", "1:a?", "-c:v", "libx264", "-crf", "18",
"-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out], stdin=subprocess.PIPE)
for i in range(math.ceil(duration * fps)):
raw = reader.stdout.read(w * h * 3)
if len(raw) < w * h * 3:
break
t = i / fps
img = add_stamp(Image.frombuffer("RGB", (w, h), raw).convert("RGBA"), t).convert("RGB")
writer.stdin.write(jolt(img, t).tobytes())
writer.stdin.close()
reader.wait()
if writer.wait():
sys.exit("ffmpeg failed")
src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
full = stamp()
render(src, out)





