---
title: "How to add an instant photo frame with Claude (tested prompt and script)"
description: "Half a second in, the video shrinks into a slightly tilted instant photo with a white border and a deep bottom margin, lying on a kraft-brown table with a soft shadow. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/instant-photo-frame
---

# How to add an instant photo frame with Claude

Half a second in, the video shrinks into a slightly tilted instant photo with a white border and a deep bottom margin, lying on a kraft-brown table with a soft shadow.

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 instant photo frame to input.mp4
   Second the video starts to shrink into the photo: 0.5
   Seconds the shrink and tilt take: 0.7
   ```

## 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 instant photo frame 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 video starts to shrink into the photo.                                             |
| `SETTLE`          | `0.7`                              | Seconds the shrink and tilt take.                                                             |
| `WIDTH`           | `0.78`                             | Photo width, border included, as a share of the frame width.                                  |
| `BORDER, BOTTOM`  | `0.055, 4`                         | Side and top border as a share of the photo width, and the bottom margin as a multiple of it. |
| `TILT`            | `-3`                               | Degrees of tilt (negative leans clockwise).                                                   |
| `PAPER, BACKDROP` | `(250, 248, 242), (198, 172, 138)` | Border and table colors.                                                                      |
| `SHADOW`          | `0.5`                              | Shadow strength (0-1).                                                                        |

## Without the skill

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

```
Turn input.mp4 into an instant photo. Play it full screen until 0.5 seconds, then over 0.7 seconds (ease-out) shrink it into an instant-photo print: the video inside a warm white (250, 248, 242) border that is 5.5% of the print's width on the top, left and right and 4 times that at the bottom. The print ends up 78% of the frame width across (border included), its center 50% across and 49% down, tilted 3 degrees clockwise (the tilt grows with the shrink), on a solid (198, 172, 138) kraft-brown backdrop. Give it a soft black drop shadow at 50% opacity, blurred by 2.5% of the frame width and shifted down by 2% of the frame width. Hold it to the end. Use Python with Pillow for the border, tilt and shadow (draw anything with Pillow, not ffmpeg's drawtext) and ffmpeg to read and write the video, keep the audio and the original size, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `instant-photo-frame.py` and run:

```
python3 instant-photo-frame.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Instant photo frame: the video settles into a tilted white instant-photo border with a deep bottom margin.

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

from PIL import Image, ImageDraw, ImageFilter

START = 0.5                # second the video starts to shrink into the photo
SETTLE = 0.7               # seconds the shrink and tilt take
WIDTH = 0.78               # photo width (border included) as a share of the frame width
BORDER = 0.055             # side and top border as a share of the photo width
BOTTOM = 4                 # bottom margin as a multiple of the side border
X, Y = 0.5, 0.49           # where the photo's center ends up (0-1 across and down the frame)
TILT = -3                  # degrees (negative leans clockwise)
PAPER = (250, 248, 242)    # border color (warm white)
BACKDROP = (198, 172, 138) # table color behind the photo (kraft brown)
SHADOW = 0.5               # shadow strength (0-1)
SHADOW_BLUR = 0.025        # shadow softness as a share of the frame width
SHADOW_DROP = 0.02         # how far the shadow falls below the photo, as a share of the frame width

def ease_out_cubic(p):
    p = min(max(p, 0), 1)
    return 1 - (1 - p) ** 3

def photo_frame(video, t):
    """The frame at time t: the video inside its paper border, scaled, tilted and dropped onto the backdrop."""
    p = ease_out_cubic((t - START) / SETTLE)
    if p == 0:
        return video

    # the photo at full size: the video is the picture, the paper is built around it
    side = round(w * BORDER / (1 - 2 * BORDER))
    card = Image.new("RGBA", (w + 2 * side, h + side + BOTTOM * side), PAPER)
    card.paste(video, (side, side))

    # start with the picture filling the frame, end at WIDTH across, centered on X, Y
    end_scale = WIDTH * w / card.width
    scale = 1 + (end_scale - 1) * p
    start_x, start_y = w / 2, h / 2 + (card.height / 2 - side - h / 2)
    cx = start_x + (w * X - start_x) * p
    cy = start_y + (h * Y - start_y) * p
    card = card.resize((round(card.width * scale), round(card.height * scale)), Image.LANCZOS)
    card = card.rotate(TILT * p, resample=Image.BICUBIC, expand=True)
    pos = (round(cx - card.width / 2), round(cy - card.height / 2))

    shadow = Image.new("L", (w, h))
    drop = round(w * SHADOW_DROP * p)
    shadow.paste(card.getchannel("A").point(lambda a: round(a * SHADOW * p)), (pos[0], pos[1] + drop))
    shadow = shadow.filter(ImageFilter.GaussianBlur(w * SHADOW_BLUR))

    img = Image.new("RGB", (w, h), BACKDROP)
    img.paste((0, 0, 0), (0, 0), shadow)
    img.paste(card, pos, card)
    return img

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)
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 chunk := reader.stdout.read(w * h * 3):
    writer.stdin.write(photo_frame(Image.frombytes("RGB", (w, h), chunk), i / fps).tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

## More retro and film

- [1950s color film look **1950s color film look**](https://www.recharm.com/effects/1950s-color-film-look)
- [35mm film look **35mm film look**](https://www.recharm.com/effects/35mm-film-look)
- [70s film look **70s film look**](https://www.recharm.com/effects/70s-film-look)
- [Camcorder date stamp **Camcorder date stamp**](https://www.recharm.com/effects/camcorder-date-stamp)
- [Camcorder viewfinder **Camcorder viewfinder**](https://www.recharm.com/effects/camcorder-viewfinder)

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