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 fisheye lens to input.mp4 Second the lens starts to bend the picture: 1.0 Seconds it takes to reach full strength; it then holds to the end: 0.4
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 fisheye lens 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 | 1.0 | Second the lens starts to bend the picture. |
RAMP | 0.4 | Seconds it takes to reach full strength; it then holds to the end. |
BULGE | 0.8 | How much the center is magnified (0.8 = 1.8x); the corners stay put. |
VIGNETTE | 0.45 | How much the corners darken (0.45 = 45% darker at the very corner). |
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
Give input.mp4 a fisheye lens look with Python, numpy and OpenCV, using ffmpeg to read and write the frames. Leave it untouched before 1.0 seconds; from 1.0 to 1.4 seconds ease the lens in (smoothstep from 0 to full strength) and hold it at full strength to the end. Bend each frame about its center with cv2.remap: an output pixel at radius r (0 at the center, 1 at a corner, measured in pixels so the bend is round) samples the source at radius r * (1 + k*r^2) / (1 + k), with k = 0.8 times the strength, so the center is magnified 1.8x, the corners stay put and straight lines bow outward. Use cubic interpolation and reflect at the borders. Then darken the rim: multiply each pixel by 1 - 0.45 * strength * smoothstep((r - 0.6) / 0.4), so nothing inside 60% of the radius darkens and the corners end up 45% darker. There's no text to draw, but if you add any, draw it with Pillow, not ffmpeg's drawtext. Keep the size, the 30fps frame rate and the audio, and save it as output.mp4 (H.264, yuv420p).Run it without Claude
This is the exact script we tested. Save it as fisheye-lens.py and run:
python3 fisheye-lens.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Fisheye lens: the middle of the frame swells toward you and straight lines bow outward, with darkened corners, like an ultra-wide lens.
Usage: python3 fisheye-lens.py input.mp4 output.mp4
Needs ffmpeg, OpenCV and numpy (pip install opencv-python-headless numpy).
"""
import json
import subprocess
import sys
import cv2
import numpy as np
START = 1.0 # second the lens starts to bend the picture
RAMP = 0.4 # seconds it takes to reach full strength; it then holds to the end
BULGE = 0.8 # how much the center is magnified (0.8 = 1.8x); the corners stay put
VIGNETTE = 0.45 # how much the corners darken (0.45 = 45% darker at the very corner)
def strength(t):
"""0 before START, easing up to 1 over RAMP seconds (smoothstep)."""
u = min(max((t - START) / RAMP, 0), 1)
return u * u * (3 - 2 * u)
def through_lens(img, s):
"""img seen through the fisheye at strength s: a pixel at radius r (1 = corner) samples the source at
r * (1 + k r^2) / (1 + k), and the rim darkens toward the corners."""
k = BULGE * s
scale = (1 + k * r2) / (1 + k)
bent = cv2.remap(img, (cx + dx * scale).astype(np.float32), (cy + dy * scale).astype(np.float32),
cv2.INTER_CUBIC, borderMode=cv2.BORDER_REFLECT)
return (bent * (1 - VIGNETTE * s * falloff)[..., None]).clip(0, 255).astype(np.uint8)
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)
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
# every output pixel's offset from the center, its squared radius as a share of the corner distance,
# and how far into the darkened rim it sits (0 inside 60% of the radius, easing to 1 at the corners)
cx, cy = (w - 1) / 2, (h - 1) / 2
ys, xs = np.mgrid[0:h, 0:w].astype(np.float32)
dx, dy = xs - cx, ys - cy
r2 = (dx ** 2 + dy ** 2) / (cx ** 2 + cy ** 2)
edge = np.clip((np.sqrt(r2) - 0.6) / 0.4, 0, 1)
falloff = edge * edge * (3 - 2 * edge)
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 True:
buf = reader.stdout.read(w * h * 3)
if len(buf) < w * h * 3:
break
img = np.frombuffer(buf, np.uint8).reshape(h, w, 3)
s = strength(i / fps)
if s > 0:
img = through_lens(img, s)
writer.stdin.write(img.tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





