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 lens flare to input.mp4 Ghosts: 4 reflections
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 lens flare 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 |
|---|---|---|
SUN | (0.80, 0.14) | Where the light source sits, 0-1 across and down the frame. |
START, FADE_IN | 0.8, 0.6 | Second the flare starts to bloom, and how many seconds it takes. |
SUN_COLOR, GLOW_COLOR | (255, 244, 214), (255, 140, 40) | Colors of the hot center and rays, and of the glow around them. |
CORE | 0.07 | Radius of the hot center, as a share of the frame width. |
RAYS | 10 | Number of light rays around the sun. |
HALO | 0.32 | Radius of the rainbow ring around the sun, as a share of the frame width. |
GHOSTS | 4 reflections | Each reflection's place along the line through the frame center, radius, color and shape (disc or ring). |
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 lens flare to input.mp4 with Python (numpy) and ffmpeg. The light source is a point 80% across and 14% down the frame; W is the frame width and r the distance in pixels from the source. Build one light layer (RGB, 0-1) from: a warm white (255, 244, 214) hot center exp(-(r/(0.07*W))^2) plus 10 rays of the same color, 0.7 * (0.5 + 0.5*cos(10*angle))^16 * exp(-r/(0.35*W)), with center plus rays capped at 1; an orange (255, 140, 40) glow 0.8 * exp(-r/(0.25*W)); and a faint rainbow ring of radius 0.32*W, strength 0.35 * exp(-q^2) with q = (r - 0.32*W)/(0.03*W), colored 0.4 + 0.6 * (clip(q, 0, 1), 1 - |q|, clip(-q, 0, 1)) so it runs red outside to blue inside. Add four lens reflections at 0.45 strength, on the line from the source through the frame center, at 0.5, 1.3, 1.7 and 2.2 times the distance from the source to the center: an amber (255, 190, 90) disc of radius 0.03*W, a teal (90, 230, 210) disc of radius 0.07*W, a violet (170, 120, 255) hollow ring of radius 0.11*W whose rim is 12% of its radius thick, and a green (140, 255, 140) disc of radius 0.04*W, all with 3-pixel soft edges. Clip the layer to 1. Leave every frame before 0.8 seconds untouched; from 0.8 to 1.4 seconds the flare's strength rises from 0 to 1 with a smoothstep, then it stays to the end with a slight flicker (multiply by 1 - 0.04*sin(2*pi*4*t)). Combine strength * layer with each frame by a screen blend (1 - (1 - frame) * (1 - light), values 0-1). There's no text to draw (if you add any, draw it with Pillow, not ffmpeg's drawtext). Keep the size, 30fps and the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as lens-flare.py and run:
python3 lens-flare.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Lens flare: a warm sun blooms in the top-right corner with rays, a halo ring and a chain of colored reflections.
Usage: python3 lens-flare.py input.mp4 output.mp4
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys
import numpy as np
SUN = (0.80, 0.14) # where the light source sits (0-1 across and down the frame)
START, FADE_IN = 0.8, 0.6 # second the flare starts to bloom, and how many seconds it takes
SUN_COLOR = (255, 244, 214) # the hot center and the rays: warm white
GLOW_COLOR = (255, 140, 40) # the glow around the sun: orange
CORE = 0.07 # radius of the hot center, as a share of the frame width
RAYS = 10 # number of light rays around the sun
HALO = 0.32 # radius of the faint rainbow ring around the sun, as a share of the frame width
# reflections on the line from the sun through the frame center:
# (distance along it, 1 = the frame center; radius as a share of the width; color; "disc" or "ring")
GHOSTS = [(0.5, 0.03, (255, 190, 90), "disc"), (1.3, 0.07, (90, 230, 210), "disc"),
(1.7, 0.11, (170, 120, 255), "ring"), (2.2, 0.04, (140, 255, 140), "disc")]
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 flare():
"""The whole flare as an RGB light layer (0-1) at full strength."""
y, x = np.mgrid[0:h, 0:w].astype(np.float32)
sx, sy = SUN[0] * w, SUN[1] * h
dx, dy = x - sx, y - sy
r = np.hypot(dx, dy)
core = np.exp(-(r / (CORE * w)) ** 2)
rays = 0.7 * (0.5 + 0.5 * np.cos(RAYS * np.arctan2(dy, dx))) ** 16 * np.exp(-r / (0.35 * w))
glow = 0.8 * np.exp(-r / (0.25 * w))
layer = np.minimum(core + rays, 1)[..., None] * rgb(SUN_COLOR) + glow[..., None] * rgb(GLOW_COLOR)
ring_pos = (r - HALO * w) / (0.03 * w) # -1..1 across the halo ring, red outside to blue inside
hue = np.stack([np.clip(ring_pos, 0, 1), 1 - np.abs(ring_pos), np.clip(-ring_pos, 0, 1)], axis=-1)
layer += 0.35 * np.exp(-ring_pos ** 2)[..., None] * (0.4 + 0.6 * hue)
for k, radius, color, shape in GHOSTS:
gx, gy = sx + k * (w / 2 - sx), sy + k * (h / 2 - sy)
edge = radius * w - np.hypot(x - gx, y - gy) # pixels inside the reflection's rim
disc = np.clip(edge / 3, 0, 1)
if shape == "ring":
disc *= np.clip((0.12 * radius * w - edge) / 3, 0, 1)
layer += 0.45 * disc[..., None] * rgb(color)
return np.minimum(layer, 1)
def rgb(color):
return np.array(color, np.float32) / 255
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
light = flare()
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", out],
stdin=subprocess.PIPE)
size, i = w * h * 3, 0
while raw := reader.stdout.read(size):
t = i / fps
p = np.clip((t - START) / FADE_IN, 0, 1)
strength = p * p * (3 - 2 * p) * (1 - 0.04 * np.sin(2 * np.pi * 4 * t)) # blooms in, then flickers slightly
if strength > 0:
frame = np.frombuffer(raw, np.uint8).reshape(h, w, 3) / 255.0
raw = ((1 - (1 - frame) * (1 - strength * light)) * 255).round().astype(np.uint8).tobytes() # screen blend
writer.stdin.write(raw)
i += 1
writer.stdin.close()
if writer.wait() or reader.wait():
sys.exit("ffmpeg failed")





