---
title: "How to add an old film with Claude (tested prompt and script)"
description: "The orbit turns into a faded sepia print whose exposure jitters every frame, with thin off-white scratches running the full height of the frame and black dust specks blinking in and out. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/old-film
---

# How to add an old film with Claude

The orbit turns into a faded sepia print whose exposure jitters every frame, with thin off-white scratches running the full height of the frame and black dust specks blinking in and out.

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 old film to input.mp4
   Sepia: how much of the gray red, green and blue each keep: (1.0, 0.88, 0.70)
   Where black lands and how much of the range is kept: (40, 0.8)
   ```

## 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 old film 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                                                                                    |
| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
| `TINT`            | `(1.0, 0.88, 0.70)` | Sepia: how much of the gray red, green and blue each keep. (1, 1, 1) gives black and white.     |
| `FADE`            | `(40, 0.8)`         | Where black lands and how much of the range is kept. Higher first number looks more washed out. |
| `FLICKER`         | `0.07`              | How far the exposure jumps brighter or darker each frame. 0 turns it off.                       |
| `SCRATCHES`       | `(2, 3)`            | Fewest and most vertical scratches on screen at once.                                           |
| `DUST`            | `20`                | Dark dust specks per frame; each lasts a single frame.                                          |
| `GRAIN, VIGNETTE` | `7, 0.35`           | Film grain strength and how much the corners darken (0-1).                                      |

## 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 a worn old film print from the first frame to the last. Use Python with numpy and ffmpeg (read raw frames from ffmpeg, process them in numpy, pipe them back). For each frame: turn it to gray (0.299 R + 0.587 G + 0.114 B), fade it to 40 + 0.8 x gray, multiply by a random exposure between 0.93 and 1.07 picked fresh every frame (flicker), darken the corners by up to 35% with a vignette, add Gaussian grain with a standard deviation of 7, then tint it sepia by multiplying red, green and blue by 1.0, 0.88 and 0.70, so the silver backdrop comes out around RGB 185, 163, 130. Scratches: keep 2 to 3 white vertical scratches on screen in every frame, each 2 px wide and running the full height of the frame, in off-white (250, 246, 235) at 90% opacity with a 1 px soft edge at 35% on each side; each one appears at a random x between 5% and 95% of the width, lasts 4 to 12 frames and drifts sideways up to 1.5 px a frame. Dust: in every frame draw 20 new solid near-black (20, 16, 12) oval specks at random places, each 3 to 8 px across, that last only that frame. Don't add any text (if you ever do, draw it with Pillow, not ffmpeg's drawtext). Keep the size (540x960), 30fps, the 6 second length and the audio. Encode H.264 yuv420p and save it as output.mp4.
```

## Run it without Claude

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

```
python3 old-film.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Old film: a faded sepia print with flickering exposure, white vertical scratches and dark dust specks.

Usage: python3 old-film.py input.mp4 output.mp4
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys

import numpy as np

TINT = (1.0, 0.88, 0.70)            # sepia: how much of the gray each of red, green and blue keeps
FADE = (40, 0.8)                    # blacks lift to 40 and the range shrinks to 80%, like a faded print
FLICKER = 0.07                      # exposure jumps up to 7% brighter or darker every frame
GRAIN = 7                           # film grain strength (0 turns it off)
VIGNETTE = 0.35                     # how much the corners darken (0-1)
SCRATCHES = (2, 3)                  # white vertical scratches on screen at once (fewest, most)
SCRATCH_COLOR = (250, 246, 235)     # off-white, like light through the scraped emulsion
DUST = 20                           # dark dust specks per frame; each lasts one frame
DUST_COLOR = (20, 16, 12)           # near-black
SEED = 7                            # change for a different pattern of scratches and dust

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"]

def new_scratch():
    """A full-height scratch 2 px wide that stays 4 to 12 frames, drifting sideways a little."""
    return {"x": rng.uniform(0.05 * w, 0.95 * w), "drift": rng.uniform(-1.5, 1.5), "frames": int(rng.integers(4, 13))}

def age(img):
    """Grade one frame (h x w x 3, 0-255 floats) and scratch and dust it."""
    gray = img @ np.array([0.299, 0.587, 0.114], np.float32)
    exposure = 1 + rng.uniform(-FLICKER, FLICKER)
    gray = (FADE[0] + gray * FADE[1]) * exposure * vignette + rng.normal(0, GRAIN, (h, w)).astype(np.float32)
    out = gray[..., None] * np.array(TINT, np.float32)

    wanted = rng.integers(SCRATCHES[0], SCRATCHES[1] + 1)
    while len(scratches) < wanted:
        scratches.append(new_scratch())
    for s in list(scratches):
        x = int(np.clip(s["x"], 1, w - 3))
        out[:, x:x + 2] = out[:, x:x + 2] * 0.1 + np.array(SCRATCH_COLOR) * 0.9
        out[:, [x - 1, x + 2]] = out[:, [x - 1, x + 2]] * 0.65 + np.array(SCRATCH_COLOR) * 0.35  # soft edge
        s["x"] += s["drift"]
        s["frames"] -= 1
        if s["frames"] == 0:
            scratches.remove(s)

    for _ in range(DUST):
        cx, cy = rng.uniform(0, w), rng.uniform(0, h)
        rx, ry = rng.uniform(1.5, 4), rng.uniform(1.5, 4)  # irregular, slightly oval blobs
        x0, y0, x1, y1 = max(int(cx - rx), 0), max(int(cy - ry), 0), min(int(cx + rx) + 1, w), min(int(cy + ry) + 1, h)
        yy, xx = np.mgrid[y0:y1, x0:x1]
        out[y0:y1, x0:x1][((xx - cx) / rx) ** 2 + ((yy - cy) / ry) ** 2 <= 1] = DUST_COLOR
    return np.clip(out, 0, 255).astype(np.uint8)

src, out = sys.argv[1:3]
w, h, rate = probe(src)
rng = np.random.default_rng(SEED)
yy, xx = np.mgrid[0:h, 0:w].astype(np.float32)
vignette = 1 - VIGNETTE * (((xx / w - 0.5) ** 2 + (yy / h - 0.5) ** 2) / 0.5) ** 1.5
scratches = []

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", "16", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
                          stdin=subprocess.PIPE)
while raw := reader.stdout.read(w * h * 3):
    writer.stdin.write(age(np.frombuffer(raw, np.uint8).reshape(h, w, 3).astype(np.float32)).tobytes())
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)
