---
title: "How to add an infinite zoom with Claude (tested prompt and script)"
description: "At one second a white-framed copy of the shot appears in its own center with smaller copies nested inside it, and the camera keeps zooming into copy after copy, one a second, until five seconds. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/infinite-zoom
---

# How to add an infinite zoom with Claude

At one second a white-framed copy of the shot appears in its own center with smaller copies nested inside it, and the camera keeps zooming into copy after copy, one a second, until five seconds.

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 an infinite zoom to input.mp4
   Second the zoom starts: 1.0
   How many copies the camera flies into: 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 an infinite zoom 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 zoom starts. The copies fade in just before.                 |
| `LOOPS`        | `4`               | How many copies the camera flies into.                                  |
| `LOOP_TIME`    | `1.0`             | Seconds to zoom from one copy to the next. Smaller is faster.           |
| `FADE`         | `0.2`             | Seconds the copies take to fade in and out.                             |
| `SCALE`        | `0.5`             | Size of each copy as a share of the one around it.                      |
| `DEPTH`        | `6`               | How many copies are nested inside each other.                           |
| `BORDER`       | `4`               | Width of the frame round each copy, in pixels at full size. 0 hides it. |
| `BORDER_COLOR` | `(245, 245, 245)` | Color of the frame round each copy.                                     |

## Without the skill

Don’t want to install the skill? Paste this prompt into Claude Code and Claude writes the script itself.

```
Add an infinite zoom (Droste effect) to input.mp4 with Python and Pillow, decoding and encoding the frames with ffmpeg. For each frame F (540x960), build a nested picture D: start with F, then 6 times paste the current picture, shrunk to half size (270x480, Lanczos), centered on a fresh copy of F, after first filling a rectangle 4 px larger on every side with (245, 245, 245) as a border. Between 1.0s and 5.0s, zoom into the center with zoom z = 2^frac((t - 1) / 1.0), where t is the time in seconds, so z goes 1 -> 2 once a second (4 loops). Each output frame is F scaled up z times about its center and cropped back to 540x960 (bicubic), with D shrunk to (0.5 x z) of the frame size pasted in the center inside a border of 4 x z px of (245, 245, 245). At z = 2 the copy fills the screen and looks exactly like z = 1, so the loop has no seam. Fade the copies (border included) in from 0.8s to 1.0s and out from 5.0s to 5.2s by blending with the plain frame; before 0.8s and after 5.2s the frames are untouched. Keep the size, 30fps and the audio, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `infinite-zoom.py` and run:

```
python3 infinite-zoom.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Infinite zoom: a framed copy of the shot appears in its own center, and the camera zooms into it copy after copy.

Usage: python3 infinite-zoom.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import subprocess
import sys

from PIL import Image, ImageDraw

START = 1.0                   # second the zoom starts (the copies fade in just before)
LOOPS = 4                     # how many copies the camera flies into
LOOP_TIME = 1.0               # seconds to zoom from one copy to the next
FADE = 0.2                    # seconds the copies take to fade in before START and out after the last loop
SCALE = 0.5                   # size of each copy, as a share of the one around it
DEPTH = 6                     # how many copies are nested inside each other
BORDER = 4                    # frame round each copy, in pixels at full size
BORDER_COLOR = (245, 245, 245)
FPS = 30                      # output frame rate
END = START + LOOPS * LOOP_TIME

def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height", "-of", "json", path]))
    return info["streams"][0]["width"], info["streams"][0]["height"]

def frames(path):
    """Decode a clip as RGB frames at FPS."""
    ff = subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf", f"fps={FPS}",
                           "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], stdout=subprocess.PIPE)
    while True:
        data = ff.stdout.read(w * h * 3)
        if len(data) < w * h * 3:
            return
        yield Image.frombytes("RGB", (w, h), data)

def framed_copy(base, inner, scale, alpha=1.0):
    """base with inner shrunk to `scale` of the frame in its center, inside a thin border."""
    cw, ch = round(w * scale), round(h * scale)
    x0, y0 = (w - cw) // 2, (h - ch) // 2
    b = max(1, round(BORDER * scale / SCALE))
    out = base.copy()
    ImageDraw.Draw(out).rectangle([x0 - b, y0 - b, x0 + cw + b - 1, y0 + ch + b - 1], fill=BORDER_COLOR)
    out.paste(inner.resize((cw, ch), Image.LANCZOS), (x0, y0))
    return Image.blend(base, out, alpha) if alpha < 1 else out

def nested(frame):
    """The frame with DEPTH copies of itself, each inside the last."""
    img = frame
    for _ in range(DEPTH):
        img = framed_copy(frame, img, SCALE)
    return img

def zoomed(img, z):
    """img scaled up z times about its center, cropped back to the frame size."""
    cw, ch = w / z, h / z
    box = ((w - cw) / 2, (h - ch) / 2, (w + cw) / 2, (h + ch) / 2)
    return img.resize((w, h), Image.BICUBIC, box=box)

def infinite_zoom(frame, t):
    """Zoom z runs from 1 to 1/SCALE once per loop: by then the first copy fills the screen and looks exactly
    like the start, so the loop repeats without a seam. The copy is drawn at its on-screen size to stay sharp."""
    alpha = min(max((t - START + FADE) / FADE, 0), max((END + FADE - t) / FADE, 0), 1)
    if alpha == 0:
        return frame
    loop = min(max(t - START, 0), END - START) / LOOP_TIME % 1
    z = (1 / SCALE) ** loop
    return framed_copy(zoomed(frame, z), nested(frame), SCALE * z, alpha)

src, out = sys.argv[1:3]
w, h = probe(src)
enc = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                        "-r", str(FPS), "-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)
for i, frame in enumerate(frames(src)):
    enc.stdin.write(infinite_zoom(frame, i / FPS).tobytes())
enc.stdin.close()
if enc.wait():
    sys.exit("ffmpeg failed")
```

## More motion and camera

- [2.5D parallax photo **2.5D parallax photo**](https://www.recharm.com/effects/2-5d-parallax-photo)
- [Back-and-forth loop **Back-and-forth loop**](https://www.recharm.com/effects/back-and-forth-loop)
- [Background blur **Background blur**](https://www.recharm.com/effects/background-blur)
- [Bullet time **Bullet time**](https://www.recharm.com/effects/bullet-time)
- [Cinemagraph **Cinemagraph**](https://www.recharm.com/effects/cinemagraph)

## 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)
