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 rain to input.mp4 Second the first drops enter at the top: 0.5 Number of streaks once the rain is going: 220
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 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.5 | Second the first drops enter at the top. |
COUNT | 220 | Number of streaks once the rain is going. |
ANGLE | 15 | Slant in degrees from vertical. |
SPEED | (900, 1600) | Fall speed of the farthest and nearest drops, in pixels per second. |
COLOR | (240, 246, 255) | Streak color. |
DIM | 0.72 | The picture's brightness under the rain (1 = unchanged). |
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 rain to input.mp4. From 0.5 seconds, dim the picture to 72% brightness and draw 220 thin streaks of rain in blue-white (240, 246, 255), slanted 15 degrees from vertical so they fall down to the right. Give each drop a random depth: far drops are 30 pixels long, 1 pixel wide, 35% opaque and fall at 900 pixels per second; near drops are 90 pixels long, 2 pixels wide, 95% opaque and fall at 1600 pixels per second, with most drops far away. The first drops enter at the top edge at 0.5 seconds, so the rain pours in rather than appearing all at once, and each drop starts again above the frame when it falls off the bottom. Soften the streaks with a 0.5 pixel blur. Before 0.5 seconds the video is unchanged. Use Python with numpy and Pillow to draw the streaks and ffmpeg to read and write 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 rain.py and run:
python3 rain.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Rain: thin blue-white streaks of rain start falling at 0.5 seconds, slanting down to the right across the dimmed picture.
Usage: python3 rain.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys
import numpy as np
from PIL import Image, ImageDraw, ImageFilter
START = 0.5 # second the first drops enter at the top
COUNT = 220 # number of streaks on screen once the rain is going
ANGLE = 15 # slant in degrees from vertical, falling down to the right
LENGTH = (30, 90) # streak length of the farthest and nearest drops, in pixels
SPEED = (900, 1600) # fall speed of the farthest and nearest drops, in pixels per second
OPACITY = (0.35, 0.95) # opacity of the farthest and nearest drops
COLOR = (240, 246, 255) # blue-white
DIM = 0.72 # the picture's brightness under the rain (1 = unchanged)
SEED = 7 # change for a different arrangement
def probe(path):
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=width,height,r_frame_rate", "-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)
def rain(t):
"""The rain layer's opacity at time t (0-1), or None before it starts."""
if t < START:
return None
layer = Image.new("L", (w, h))
draw = ImageDraw.Draw(layer)
for d in drops:
fallen = d["speed"] * (t - START) - d["delay"]
if fallen < 0:
continue # this drop hasn't reached the top of the frame yet
y = fallen % cycle - d["length"] # top end of the streak; wraps back above the frame
x = (d["x"] + y * slant) % (w + margin) - margin / 2
dx, dy = d["length"] * slant, d["length"]
draw.line([(x, y), (x + dx, y + dy)], fill=round(255 * d["opacity"]), width=d["width"])
return np.asarray(layer.filter(ImageFilter.GaussianBlur(0.5)), np.float32)[..., None] / 255
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
slant = math.tan(math.radians(ANGLE))
cycle = h + LENGTH[1] * 2 # distance a drop falls before it starts again at the top
margin = h * slant + LENGTH[1] # drops start left of the frame so the slant still covers it
rng = np.random.default_rng(SEED)
drops = []
for _ in range(COUNT):
depth = rng.uniform() ** 1.5 # 0 = far, 1 = near; most drops are far away
drops.append({"x": rng.uniform(0, w + margin), "delay": rng.uniform(0, cycle),
"speed": SPEED[0] + (SPEED[1] - SPEED[0]) * depth,
"length": LENGTH[0] + (LENGTH[1] - LENGTH[0]) * depth,
"opacity": OPACITY[0] + (OPACITY[1] - OPACITY[0]) * depth,
"width": 2 if depth > 0.6 else 1})
color = np.array(COLOR, np.float32)
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", "16", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
stdin=subprocess.PIPE)
i = 0
while raw := reader.stdout.read(w * h * 3):
img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
alpha = rain(i / fps)
if alpha is not None:
img = img.astype(np.float32) * DIM # an overcast dim so the streaks stand out
img = (img * (1 - alpha) + color * alpha).astype(np.uint8)
writer.stdin.write(img.tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





