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 lightning to input.mp4 Second the bolt strikes: 1.0 How dark the storm gets before the strike (0 = not at all, 1 = black): 0.55
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 lightning 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 |
|---|---|---|
AT | 1.0 | Second the bolt strikes. |
DIM | 0.55 | How dark the storm gets before the strike (0 = not at all, 1 = black). |
TOP, BOTTOM | (0.66, 0.0), (0.86, 0.66) | Where the bolt enters and where it ends, as shares across and down the frame. |
BRANCHES | 4 | Side forks off the main bolt. |
ROUGH | 0.16 | How jagged the bolt is. 0 is a straight line. |
GLOW | (110, 150, 255) | Color of the glow round the white core. |
FLASH_AMOUNT | 0.35 | How much the whole frame lights up while the bolt is on. |
SEED | 2 | Change it for a different bolt shape. |
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 lightning strike to input.mp4. From 0.5s to 1.0s, darken the picture linearly toward dark blue (15, 20, 45), ending at 55% of the way there. At 1.0s a lightning bolt strikes: a jagged line (midpoint displacement, each midpoint pushed sideways by up to 16% of its segment's length, down to 12-pixel segments) from 66% across at the top edge to 86% across and 66% down, with 4 thinner jagged forks 12-25% of the frame height long branching off it downward. Draw it with Pillow as a 6px pure white (255, 255, 255) core (3px for the forks) over an electric blue (110, 150, 255) glow made by blurring 5x-wider copies of the lines (Gaussian blur radius 6 and 28). The strike's strength is 100% from 1.00s to 1.08s, 15% from 1.08s to 1.14s, 100% again from 1.14s to 1.26s, then fades linearly to 0 by 1.66s; the bolt's opacity follows it, and the whole frame also lightens toward pale blue (200, 215, 255) by 35% times the strength. After 1.66s the darkening lifts linearly back to the untouched picture by 2.46s. Before 0.5s and after 2.46s the video must be unchanged. 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 lightning.py and run:
python3 lightning.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Lightning: the picture darkens to a stormy blue, then a jagged white bolt forks down from the top, flickers and flashes.
Usage: python3 lightning.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import subprocess
import sys
import numpy as np
from PIL import Image, ImageDraw, ImageFilter
AT = 1.0 # second the bolt strikes
DIM_IN = 0.5 # seconds the picture takes to darken before the strike
DIM = 0.55 # how dark the storm gets (0 = not at all, 1 = black)
FADE = 0.4 # seconds the bolt takes to die away after the second flicker
CLEAR = 0.8 # seconds the storm takes to lift after the bolt has gone
TOP = (0.66, 0.0) # where the bolt enters (0-1 across and down the frame)
BOTTOM = (0.86, 0.66) # where the main bolt ends
BRANCHES = 4 # side forks off the main bolt
ROUGH = 0.16 # how jagged the bolt is (0 = straight line)
STORM = (15, 20, 45) # the dark blue the picture is pulled toward
GLOW = (110, 150, 255) # electric blue glow round the white core
FLASH = (200, 215, 255) # pale blue the whole frame lights toward at the strike
FLASH_AMOUNT = 0.35 # how far it lights up (0-1)
SEED = 2 # change for a different bolt shape
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 jagged(a, b, rough):
"""Points of a jagged line from a to b: split each segment at its midpoint and push that point sideways."""
length = np.hypot(b[0] - a[0], b[1] - a[1])
if length < 12:
return [a, b]
nx, ny = (a[1] - b[1]) / length, (b[0] - a[0]) / length # unit vector across the segment
push = rng.uniform(-rough, rough) * length
mid = ((a[0] + b[0]) / 2 + nx * push, (a[1] + b[1]) / 2 + ny * push)
return jagged(a, mid, rough)[:-1] + jagged(mid, b, rough)
def bolt():
"""The bolt as two masks (0-1): the thin white core and the soft glow around it."""
main = jagged((TOP[0] * w, TOP[1] * h), (BOTTOM[0] * w, BOTTOM[1] * h), ROUGH)
forks = []
for _ in range(BRANCHES):
x, y = main[rng.integers(len(main) // 5, len(main) * 3 // 4)]
angle = rng.uniform(0.3, 1.1) * rng.choice([-1, 1]) # radians off straight down
reach = rng.uniform(0.12, 0.25) * h
forks.append(jagged((x, y), (x + reach * np.sin(angle), y + reach * np.cos(angle)), ROUGH))
core, halo = Image.new("L", (w, h)), Image.new("L", (w, h))
for line, width in [(main, 6)] + [(f, 3) for f in forks]:
ImageDraw.Draw(core).line(line, fill=255, width=width, joint="curve")
ImageDraw.Draw(halo).line(line, fill=255, width=width * 5, joint="curve")
halo = np.maximum(np.asarray(halo.filter(ImageFilter.GaussianBlur(6)), np.float32) * 0.8,
np.asarray(halo.filter(ImageFilter.GaussianBlur(28)), np.float32) * 1.5)
return np.asarray(core, np.float32)[..., None] / 255, np.clip(halo / 255, 0, 0.85)[..., None]
def strength(t):
"""How bright the strike is at time t (0-1): on, a quick dip, on again, then fading out."""
dt = t - AT
if dt < 0:
return 0.0
if dt < 0.08:
return 1.0
if dt < 0.14:
return 0.15
if dt < 0.26:
return 1.0
return max(0.0, 1 - (dt - 0.26) / FADE)
def darkness(t):
"""How far the storm has darkened the picture at time t (0-1)."""
if t < AT:
return float(np.clip((t - (AT - DIM_IN)) / DIM_IN, 0, 1))
return float(np.clip(1 - (t - AT - 0.26 - FADE) / CLEAR, 0, 1))
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
rng = np.random.default_rng(SEED)
core, halo = bolt()
storm, glow, flash = (np.array(c, np.float32) for c in (STORM, GLOW, FLASH))
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):
img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
d, s = darkness(i / fps) * DIM, strength(i / fps)
if d > 0:
frame = img * (1 - d) + storm * d # stormy darkening
frame += (flash - frame) * FLASH_AMOUNT * s # the whole sky lights up
frame = frame * (1 - halo * s) + glow * halo * s # blue glow round the bolt
frame = frame * (1 - core * s) + 255 * core * s # white-hot core on top
img = np.clip(frame, 0, 255).astype(np.uint8)
writer.stdin.write(img.tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





