How to add fisheye lens with Claude

At one second the bottle swells toward the camera to nearly twice its size while the acrylic block's straight edges bow into curves and the corners fall into shadow, and the shot stays that way.

Get this effect

  1. Switch to Code in your Claude Desktop App

  2. 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/skills
  3. Ask 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.

SettingDefaultWhat it does
START1.0Second the lens starts to bend the picture.
RAMP0.4Seconds it takes to reach full strength; it then holds to the end.
BULGE0.8How much the center is magnified (0.8 = 1.8x); the corners stay put.
VIGNETTE0.45How 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.mp4
Show 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")

Find 3x winning ads

Recharm cuts your ad footage into tagged, searchable clips: hooks, testimonials, product shots. Find the shots that win, add the effect with Claude, and ship 3x more ad edits.

Start 14-day free trial