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 shooting stars to input.mp4 Meteors: 8 meteors from 0.8s to 4.3s
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 shooting stars 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 |
|---|---|---|
NIGHT | (8, 12, 40) | Color the picture is darkened toward. |
DARK_TOP, DARK_BOTTOM | 0.85, 0.45 | How dark the sky gets at the top and bottom edges (0 = not at all, 1 = fully night blue). |
METEORS | 8 meteors from 0.8s to 4.3s | When each meteor appears and where it starts, as shares across and down the frame. |
FLIGHT | 0.9 | Seconds each meteor takes to cross. |
ANGLE | 25 | Degrees below horizontal the meteors fall at. |
CURVE | 0.06 | How much the path bends downward. 0 is a straight line. |
TAIL | 0.5 | Tail length as a share of the path. |
GLOW | (150, 190, 255) | Color of the glow around each streak. |
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 shooting stars to input.mp4. From 0.3s to 0.7s, darken the picture linearly toward night blue (8, 12, 40) with a vertical gradient: 85% of the way there at the top edge, 45% at the bottom edge; it stays dark to the end. Then 8 meteors cross the frame, appearing at 0.8, 1.2, 1.7, 2.2, 2.7, 3.2, 3.7 and 4.3 seconds and starting at (across, down, as shares of the frame) (0.98, 0.06), (0.75, 0.02), (1.05, 0.16), (0.90, 0.12), (0.65, 0.03), (1.02, 0.14), (0.80, 0.08) and (1.00, 0.12). Each takes 0.9 seconds to travel 80% of the frame width heading down and to the left at 25 degrees below horizontal, with an extra drop of 6% of the frame height at the end that grows with the square of the progress, so the path arcs. Draw each with Pillow as a pure white (255, 255, 255) head 8px across and a tail covering the last 50% of the path behind it, tapering from 5px wide and full brightness at the head to 1px and dark at the end, plus a pale blue (150, 190, 255) glow made by blurring the white streaks with a Gaussian radius of 6 and tripling its strength (capped at full). Each meteor fades in over the first 15% of its flight and out over the last 35%. Before 0.3s the video must be unchanged and nothing is drawn. Use Python with numpy and Pillow for the frames and ffmpeg to read and write them (no drawtext), keep the size, 30fps, length and audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as shooting-stars.py and run:
python3 shooting-stars.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Shooting stars: the sky darkens to night blue and white meteors with glowing tails arc down across it.
Usage: python3 shooting-stars.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
NIGHT = (8, 12, 40) # color the picture is darkened toward
DARK_TOP, DARK_BOTTOM = 0.85, 0.45 # how far toward NIGHT at the top and bottom edges (0 = not at all, 1 = fully)
DARK_IN = (0.3, 0.7) # seconds the darkening fades in between
METEORS = [(0.8, 0.98, 0.06), (1.2, 0.75, 0.02), (1.7, 1.05, 0.16), # (second it appears, start across, start down)
(2.2, 0.90, 0.12), (2.7, 0.65, 0.03), (3.2, 1.02, 0.14),
(3.7, 0.80, 0.08), (4.3, 1.00, 0.12)]
FLIGHT = 0.9 # seconds each meteor takes to cross
TRAVEL = 0.8 # distance each meteor travels, as a share of the frame width
ANGLE = 25 # degrees below horizontal, heading down and to the left
CURVE = 0.06 # extra drop at the end of the path, as a share of the frame height (the arc)
TAIL = 0.5 # tail length, as a share of the path
WIDTH = 5 # head width in pixels; the tail tapers to 1
GLOW = (150, 190, 255) # pale blue glow around each streak
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 path_point(x0, y0, s):
"""Where a meteor that starts at (x0, y0) is after s (0-1) of its path: a straight line bending downward."""
a = math.radians(ANGLE)
return (w * (x0 - s * TRAVEL * math.cos(a)),
h * y0 + w * s * TRAVEL * math.sin(a) + h * CURVE * s * s)
def streaks(t):
"""Every meteor in flight at time t, drawn white on black as a grayscale image."""
img = Image.new("L", (w, h))
draw = ImageDraw.Draw(img)
for born, x0, y0 in METEORS:
p = (t - born) / FLIGHT
if not 0 < p < 1:
continue
fade = min(1, p / 0.15, (1 - p) / 0.35) # quick fade in, slower fade out
steps = 40
for k in range(steps): # tail end first, so the brighter head draws on top
s0 = p - TAIL * (1 - k / steps)
s1 = p - TAIL * (1 - (k + 1) / steps)
if s1 <= 0:
continue
near = (k + 1) / steps # 0 at the tail's end, 1 at the head
draw.line([path_point(x0, y0, max(s0, 0)), path_point(x0, y0, s1)],
fill=round(255 * fade * near ** 1.5), width=max(1, round(WIDTH * near)))
hx, hy = path_point(x0, y0, p)
r = WIDTH * 0.8
draw.ellipse([hx - r, hy - r, hx + r, hy + r], fill=round(255 * fade))
return img
def frame_at(img, t):
"""One frame: darkened toward NIGHT, then the meteors' blue glow and white cores on top."""
dark = min(max((t - DARK_IN[0]) / (DARK_IN[1] - DARK_IN[0]), 0), 1)
shade = dark * np.linspace(DARK_TOP, DARK_BOTTOM, h, dtype=np.float32)[:, None, None]
out = img * (1 - shade) + np.array(NIGHT, np.float32) * shade
core = streaks(t)
glow = np.asarray(core.filter(ImageFilter.GaussianBlur(6)), np.float32)[..., None] / 255 * 3
core = np.asarray(core, np.float32)[..., None] / 255
out = out + (np.array(GLOW, np.float32) - out) * np.clip(glow, 0, 1)
out = out + (255 - out) * core
return out.clip(0, 255).astype(np.uint8)
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
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).astype(np.float32)
writer.stdin.write(frame_at(img, i / fps).tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





