---
title: "How to add a binocular view with Claude (tested prompt and script)"
description: "At half a second the frame closes down to two overlapping circles side by side, black all around, and the view inside them moves 20% closer, like looking through binoculars. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/binocular-view
---

# How to add a binocular view with Claude

At half a second the frame closes down to two overlapping circles side by side, black all around, and the view inside them moves 20% closer, like looking through binoculars.

Made by the script on this page, from our own test clip.

## Get this effect

1. ### Switch to Code in your [Claude Desktop App](https://claude.com/download)

2. ### Install the skill (once)

   ```
   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 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.

```
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.mp4
```

```
#!/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")
```

## More layouts and reframing

- [Background replacement **Background replacement**](https://www.recharm.com/effects/background-replacement)
- [Blur region **Blur region**](https://www.recharm.com/effects/blur-region)
- [Cinematic bars **Cinematic bars**](https://www.recharm.com/effects/cinematic-bars)
- [Comic panel layout **Comic panel layout**](https://www.recharm.com/effects/comic-panel-layout)
- [Corner pin **Corner pin**](https://www.recharm.com/effects/corner-pin)

## 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](https://www.recharm.com/get-started)
