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 money rain to input.mp4 Second the first notes drop in at the top edge: 0.3 Banknotes in the shower: 40
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 money rain 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 |
|---|---|---|
START | 0.3 | Second the first notes drop in at the top edge. |
COUNT | 40 | Banknotes in the shower. |
SIZE | (0.20, 0.30) | Note length range, as a share of the frame width. |
SPEED | (0.30, 0.46) | Fall speed range, in frame heights per second. |
SPIN | 90 | Fastest spin, in degrees per second. |
SWAY | 0.04 | Side-to-side drift as a share of the frame width. |
GREEN, PAPER, INK | (70, 150, 80), (246, 248, 236), (25, 80, 40) | Note face, border and dollar sign 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 money rain to input.mp4. Draw a banknote with Pillow: a 300x130 pixel rounded rectangle in off-white (246, 248, 236), a green (70, 150, 80) face inset 9 pixels, a thin dark green (25, 80, 40) line 15 pixels in, a pale green (150, 205, 140) oval 88x80 pixels in the middle with a dark green "$" in bold type, and "100" in off-white in each corner. Rain 40 of these notes down the frame starting at 0.3 seconds: each is scaled to 20% to 30% of the frame width long (bigger notes fall faster, 0.30 to 0.46 frame heights per second, and are drawn in front), placed at random from 5% to 95% across, and queued evenly from 8% to 138% of the frame height above the top edge so they enter one after another. Spin each note at up to 90 degrees per second either way from a random starting angle, squash its height with |cos| over a 1.2 to 2.4 second cycle (never below 12%) so it looks like it flips over, and sway it side to side by 4% of the frame width over 1.8 to 3.2 seconds. A note that falls 15% past the bottom comes back in at the top, so the shower keeps going to the end. Nothing shows before 0.3 seconds and the notes take until about 2 seconds to reach the bottom half. Use Python with numpy and Pillow to draw it and ffmpeg to put it on the video (don't rely on ffmpeg's drawtext filter), keep the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as money-rain.py and run:
python3 money-rain.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Money rain: green banknotes shower down from the top of the frame, spinning and flipping as they fall.
Usage: python3 money-rain.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFont
START = 0.3 # second the first notes drop in at the top edge
COUNT = 40 # banknotes in the shower
SIZE = (0.20, 0.30) # note length range, as a share of the frame width
SPEED = (0.30, 0.46) # fall speed range, in frame heights per second (bigger notes fall faster)
SPIN = 90 # fastest spin, in degrees per second either way
SWAY = 0.04 # side-to-side drift as a share of the frame width
GREEN = (70, 150, 80) # note color
PAPER = (246, 248, 236) # border and numbers
INK = (25, 80, 40) # dollar sign and inner line
SEED = 11 # change for a different shower
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 banknote(w=300, h=130):
"""A flat note: paper border, green face, a pale oval with a dollar sign and 100 in the corners."""
img = Image.new("RGBA", (w, h))
draw = ImageDraw.Draw(img)
draw.rounded_rectangle([0, 0, w - 1, h - 1], radius=6, fill=PAPER)
draw.rectangle([9, 9, w - 10, h - 10], fill=GREEN)
draw.rectangle([15, 15, w - 16, h - 16], outline=INK, width=2)
draw.ellipse([w / 2 - 44, h / 2 - 40, w / 2 + 44, h / 2 + 40], fill=(150, 205, 140), outline=INK, width=3)
draw.text((w / 2, h / 2 + 2), "$", font=ImageFont.truetype(FONT_FILE, 64), anchor="mm", fill=INK)
small = ImageFont.truetype(FONT_FILE, 24)
for x, y, anchor in [(24, 22, "lt"), (w - 24, 22, "rt"), (24, h - 22, "lb"), (w - 24, h - 22, "rb")]:
draw.text((x, y), "100", font=small, anchor=anchor, fill=PAPER)
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"])
src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
note = banknote()
rng = np.random.default_rng(SEED)
notes = []
for k in range(COUNT): # notes queue up evenly above the frame
depth = rng.uniform(0, 1) # 0 = far (small, slow), 1 = near (big, fast)
length = (SIZE[0] + depth * (SIZE[1] - SIZE[0])) * w
notes.append({"sprite": note.resize((round(length), round(length * note.height / note.width)), Image.LANCZOS),
"x": rng.uniform(0.05, 0.95) * w, "y": -(0.08 + 1.3 * k / COUNT) * h,
"speed": (SPEED[0] + depth * (SPEED[1] - SPEED[0])) * h, "spin": rng.uniform(-SPIN, SPIN),
"angle": rng.uniform(0, 360), "flip": rng.uniform(1.2, 2.4), "sway": rng.uniform(1.8, 3.2),
"phase": rng.uniform(0, 2 * math.pi), "depth": depth})
notes.sort(key=lambda n: n["depth"]) # draw far notes first so near ones cover them
def draw_notes(img, t):
"""Paste every note at its place at time t onto the frame."""
age = t - START
span = h * 1.3 # a note that falls off the bottom comes back in at the top
for n in notes:
y = n["y"] + n["speed"] * age
if y > h * 1.15:
y = (y + 0.15 * h) % span - 0.15 * h
x = n["x"] + SWAY * w * math.sin(2 * math.pi * age / n["sway"] + n["phase"])
turn = abs(math.cos(2 * math.pi * age / n["flip"] + n["phase"])) # flipping over: squashed when edge-on
sprite = n["sprite"].resize((n["sprite"].width, max(2, round(n["sprite"].height * max(turn, 0.12)))),
Image.LANCZOS)
sprite = sprite.rotate(n["angle"] + n["spin"] * age, expand=True, resample=Image.BICUBIC)
img.paste(sprite, (round(x - sprite.width / 2), round(y - sprite.height / 2)), sprite)
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)
i = 0
while raw := reader.stdout.read(w * h * 3):
t = i / fps
if t >= START:
img = Image.frombytes("RGB", (w, h), raw)
draw_notes(img, t)
raw = img.tobytes()
writer.stdin.write(raw)
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





