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 snow to input.mp4 Second the first flakes appear at the top edge: 0.3 Number of flakes: 230
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 snow 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 flakes appear at the top edge. |
COUNT | 230 | Number of flakes. |
RADIUS | (2.0, 7.5) | Radius of the farthest and nearest flakes, in pixels. |
SPEED | (0.14, 0.42) | Fall speed of the farthest and nearest flakes, in frame heights per second. |
OPACITY | (0.6, 1.0) | How solid the farthest and nearest flakes are. |
SHADE | 0.18 | Strength of the soft shadow round each flake. |
SWAY | 12 | Side-to-side drift, in pixels. |
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 falling snow to input.mp4. Use 230 round white (255, 255, 255) flakes, each given a random depth: far flakes have a 2 pixel radius, 60% opacity and fall at 0.14 frame heights per second, near flakes a 7.5 pixel radius, full opacity and 0.42 frame heights per second, with most flakes far away. Give each flake a solid middle with a soft edge and a faint soft shadow (18% darker at most, about 1.5 times the flake's radius) so it shows on a light background, and sway it side to side by 12 pixels over 2.5 to 5 seconds. Nothing shows before 0.3 seconds; then the flakes fall in from above the top edge (spread over one frame height above it, so the bottom of the frame only fills in after about 2 seconds), and each one wraps back to the top once it passes the bottom, so it keeps snowing to the end. Use Python with numpy 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 snow.py and run:
python3 snow.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Snow: soft white snowflakes drift down from the top of the frame, big near ones falling fast and small far ones slowly.
Usage: python3 snow.py input.mp4 output.mp4
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys
import numpy as np
START = 0.3 # second the first flakes appear at the top edge
COUNT = 230 # number of flakes
RADIUS = (2.0, 7.5) # radius of the farthest and nearest flakes, in pixels
SPEED = (0.14, 0.42) # fall speed of the farthest and nearest flakes, in frame heights per second
OPACITY = (0.6, 1.0) # how solid the farthest and nearest flakes are
SHADE = 0.18 # faint soft shadow round each flake, so it stands out on light backgrounds
SWAY = 12 # side-to-side drift, in pixels
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 snow(t):
"""How much white covers each pixel at time t, and how much its shadow darkens it (both 0-1), or None before the snow starts."""
if t < START:
return None
white, shade = np.zeros((h, w), np.float32), np.zeros((h, w), np.float32)
span = h + 2 * MARGIN
for f in flakes:
fallen = f["y"] + f["speed"] * h * (t - START) # starts above the frame, so the snow falls in from the top
if fallen < -MARGIN:
continue
y = (fallen + MARGIN) % span - MARGIN # wraps back to the top once it passes the bottom
x = (f["x"] + SWAY * np.sin(2 * np.pi * t / f["sway"] + f["phase"])) % w
r = int(f["radius"] * 2.2) + 1
x0, y0, x1, y1 = max(int(x) - r, 0), max(int(y) - r, 0), min(int(x) + r + 1, w), min(int(y) + r + 1, h)
if x0 >= x1 or y0 >= y1:
continue
yy, xx = np.mgrid[y0:y1, x0:x1].astype(np.float32)
d = np.sqrt((xx - x) ** 2 + (yy - y) ** 2) / f["radius"]
flake = np.clip(1.6 - d, 0, 1) ** 1.5 # solid middle with a soft edge
shadow = np.exp(-(d / 1.5) ** 2) # a soft shadow a little wider than the flake
white[y0:y1, x0:x1] = np.maximum(white[y0:y1, x0:x1], f["opacity"] * flake)
shade[y0:y1, x0:x1] = np.maximum(shade[y0:y1, x0:x1], SHADE * f["opacity"] * shadow)
return white[..., None], shade[..., None]
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
MARGIN = RADIUS[1] * 3
rng = np.random.default_rng(SEED)
flakes = []
for _ in range(COUNT):
depth = rng.uniform() ** 1.6 # 0 = far, 1 = near; most flakes are far away
flakes.append({"x": rng.uniform(0, w),
"y": -MARGIN - rng.uniform(0, h + 2 * MARGIN), # spread over one screen's height above the frame
"speed": SPEED[0] + (SPEED[1] - SPEED[0]) * depth * rng.uniform(0.85, 1.15),
"radius": RADIUS[0] + (RADIUS[1] - RADIUS[0]) * depth,
"opacity": OPACITY[0] + (OPACITY[1] - OPACITY[0]) * depth,
"sway": rng.uniform(2.5, 5), "phase": rng.uniform(0, 2 * np.pi)})
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)
flakes_now = snow(i / fps)
if flakes_now is not None:
white, shade = flakes_now
img = (img * (1 - shade) * (1 - white) + 255 * white).astype(np.uint8) # shade a little, then mix toward white
writer.stdin.write(img.tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





