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 binocular view to input.mp4 Second the binoculars go up: 0.5 Seconds the circles take to shrink into place: 0.5
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 binocular view 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 | 0.5 | Second the binoculars go up. |
CLOSE | 0.5 | Seconds the circles take to shrink into place. |
RADIUS | 0.30 | Circle radius as a share of the frame width. |
GAP | 0.36 | Distance between the circle centers as a share of the frame width. |
ZOOM | 1.2 | How much closer the view gets inside the circles. |
RIM | 0.35 | How much the picture darkens toward each lens's rim. |
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
Make input.mp4 look like it's seen through binoculars from 0.5 seconds on. Mask the frame to two overlapping circles side by side: each circle's radius is 30% of the frame width, their centers sit 36% of the frame width apart, centered across the frame at half its height, with a 4-pixel soft edge, and everything outside them is pure black. Inside the circles darken the picture slightly toward the rim (by up to 35% at the edge). Over 0.5 seconds from 0.5s, with an ease-out, the circles shrink from 2.5 times their size into place while the picture zooms from 1x to 1.2x about the frame center; before 0.5s the video is untouched. Use Python with numpy (and Pillow if you need it) for the mask and ffmpeg for the video, keep the audio, and save it as output.mp4 at 30fps.Run it without Claude
This is the exact script we tested. Save it as binocular-view.py and run:
python3 binocular-view.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Binocular view: two overlapping circles close in from the edges and black out the rest, like looking through binoculars.
Usage: python3 binocular-view.py input.mp4 output.mp4
Needs ffmpeg, Pillow and numpy (pip install pillow numpy).
"""
import json
import subprocess
import sys
import numpy as np
from PIL import Image
START = 0.5 # second the binoculars go up
CLOSE = 0.5 # seconds the circles take to shrink into place
RADIUS = 0.30 # circle radius as a share of the frame width
GAP = 0.36 # distance between the two circle centers as a share of the frame width
Y = 0.5 # height of the circle centers (0 = top, 1 = bottom)
ZOOM = 1.2 # how much closer the view gets inside the circles
FEATHER = 4 # soft edge on the circles, in pixels
RIM = 0.35 # how much the picture darkens toward the rim of each lens (0 = none)
src, out = sys.argv[1:3]
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=width,height", "-of", "json", src]))
w, h = info["streams"][0]["width"], info["streams"][0]["height"]
ys, xs = np.mgrid[0:h, 0:w].astype(np.float32)
def lenses(scale):
"""Brightness multiplier for the two circles at `scale` times their final size: 1 inside, 0 outside."""
r = RADIUS * w * scale
best = np.full((h, w), np.inf, np.float32)
for cx in (w / 2 - GAP * w / 2, w / 2 + GAP * w / 2):
best = np.minimum(best, np.hypot(xs - cx, ys - Y * h) / r)
edge = np.clip((1 - best) * r / FEATHER, 0, 1)
return (edge * (1 - RIM * np.clip(best, 0, 1) ** 4))[..., None]
def zoomed(img, z):
"""The center of the frame scaled up by z, at the same size."""
cw, ch = w / z, h / z
box = ((w - cw) / 2, (h - ch) / 2, (w + cw) / 2, (h + ch) / 2)
return np.asarray(Image.fromarray(img).resize((w, h), Image.BICUBIC, box=box), np.float32)
def ease(p):
p = min(max(p, 0.0), 1.0)
return 1 - (1 - p) ** 3
final = lenses(1.0)
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-vf", "fps=30", "-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", "30", "-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:
raw = reader.stdout.read(w * h * 3)
if len(raw) < w * h * 3:
break
img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
t = i / 30
if t >= START:
p = ease((t - START) / CLOSE)
mask = final if p == 1 else lenses(1 + 1.5 * (1 - p)) # starts 2.5x too big, so the frame is still clear
img = (zoomed(img, 1 + (ZOOM - 1) * p) * mask).round().astype(np.uint8)
writer.stdin.write(img.tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





