---
title: "How to add a black hole with Claude (tested prompt and script)"
description: "At 3 seconds the shot's edges bend inward into an hourglass and the whole picture is sucked into the center of the frame, leaving black by 5 seconds. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/black-hole
---

# How to add a black hole with Claude

At 3 seconds the shot's edges bend inward into an hourglass and the whole picture is sucked into the center of the frame, leaving black by 5 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 a black hole to input.mp4
   Second the pull begins: 3.0
   Seconds until the picture has vanished into the center: 2.0
   ```

## 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 black hole 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`  | `3.0`   | Second the pull begins.                                                 |
| `LENGTH` | `2.0`   | Seconds until the picture has vanished into the center.                 |
| `PINCH`  | `3.0`   | How hard the middle is squeezed by the end; 0 just shrinks the picture. |

## Without the skill

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

```
Add a black hole effect to input.mp4 with Python, numpy and ffmpeg: from 3.0 to 5.0 seconds, pull the whole picture into the center of the frame until it vanishes. For each output pixel, take its distance r from the frame center; the picture fills a disc of radius R*(1-p), where R is half the frame's diagonal and p = ((t-3)/2)^2 runs 0 to 1 (so the pull speeds up). Inside the disc, with u = r/(R*(1-p)), sample the input at the same angle and distance R*u^(1/(1+3p)), with bilinear filtering, which squeezes the middle and makes the edges bow inward; anything outside the disc or sampled from outside the frame is black (0, 0, 0). Leave the video untouched before 3.0 seconds and fully black after 5.0. Keep the size, frame rate, length and audio, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 black-hole.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Black hole: the picture bows inward and is sucked into the center of the frame until nothing is left.

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

import numpy as np

START = 3.0     # second the pull begins
LENGTH = 2.0    # seconds until the picture has vanished into the center
PINCH = 3.0     # how hard the middle is squeezed by the end (0 = the picture just shrinks)

def pull(img, p):
    """Shrink the picture into a disc whose radius falls from the frame's half-diagonal to 0 as p goes 0 -> 1,
    squeezing the middle harder than the rim so the edges bow inward. Outside the disc is black."""
    radius = half_diagonal * (1 - p)
    u = np.minimum(dist / max(radius, 1e-6), 1)
    src_r = half_diagonal * u ** (1 / (1 + PINCH * p))
    sx = w / 2 + dx / np.maximum(dist, 1e-6) * src_r - 0.5
    sy = h / 2 + dy / np.maximum(dist, 1e-6) * src_r - 0.5
    inside = (dist < radius) & (sx >= 0) & (sx <= w - 1) & (sy >= 0) & (sy <= h - 1)
    x0 = np.clip(np.floor(sx).astype(int), 0, w - 2)
    y0 = np.clip(np.floor(sy).astype(int), 0, h - 2)
    fx, fy = (sx - x0)[..., None], (sy - y0)[..., None]
    top = img[y0, x0] * (1 - fx) + img[y0, x0 + 1] * fx
    bottom = img[y0 + 1, x0] * (1 - fx) + img[y0 + 1, x0 + 1] * fx
    return ((top * (1 - fy) + bottom * fy) * inside[..., None]).astype(np.uint8)

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)
ys, xs = np.mgrid[0:h, 0:w].astype(np.float32)
dx, dy = xs + 0.5 - w / 2, ys + 0.5 - h / 2
dist = np.hypot(dx, dy)
half_diagonal = float(np.hypot(w, h)) / 2

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 True:
    raw = reader.stdout.read(w * h * 3)
    if len(raw) < w * h * 3:
        break
    frame = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    p = min(max((i / fps - START) / LENGTH, 0), 1) ** 2  # eases in, so the pull speeds up like falling
    if p > 0:
        frame = pull(frame.astype(np.float32), p)
    writer.stdin.write(frame.tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

## More distortion and glitch

- [Anaglyph 3D **Anaglyph 3D**](https://www.recharm.com/effects/anaglyph-3d)
- [Bulge and pinch **Bulge and pinch**](https://www.recharm.com/effects/bulge-pinch)
- [Datamosh **Datamosh**](https://www.recharm.com/effects/datamosh)
- [Disintegration **Disintegration**](https://www.recharm.com/effects/disintegration)
- [Drunk vision **Drunk vision**](https://www.recharm.com/effects/drunk-vision)

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