---
title: "How to add stained glass with Claude (tested prompt and script)"
description: "At one second the shot shatters into hundreds of irregular flat panes of saturated color, each held in thick black lead lines, so the bottle becomes a leaded window of purple and cream glass against pale blue panes. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/stained-glass
---

# How to add stained glass with Claude

At one second the shot shatters into hundreds of irregular flat panes of saturated color, each held in thick black lead lines, so the bottle becomes a leaded window of purple and cream glass against pale blue panes.

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 stained glass to input.mp4
   Second the picture turns into stained glass: 1.0
   Average pane size in pixels: 30
   ```

## 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 stained glass 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 picture turns into stained glass.                     |
| `CELL`       | `30`           | Average pane size in pixels. Bigger gives fewer, chunkier panes. |
| `LEAD`       | `5`            | Lead line thickness in pixels.                                   |
| `LEAD_COLOR` | `(22, 20, 24)` | Color of the lead lines.                                         |
| `SATURATION` | `2.2`          | Color boost for the panes.                                       |
| `BEVEL`      | `10`           | Pixels over which each pane darkens toward its lead.             |
| `SEED`       | `3`            | Random seed for the pane layout.                                 |

## 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 (540x960) into a stained glass window from 1.0 seconds to the end; before 1.0 seconds leave it untouched. Lay out the panes once, with a fixed random seed so they stay in place from frame to frame: scatter 576 random points over the frame (one per 30x30 pixels on average) and give every pixel to its nearest point, making irregular Voronoi cells. Mark the pixels where neighboring cells meet, plus a 1 pixel border around the frame, and thicken them with a 5x5 elliptical dilation into lead lines. For each frame from 1.0 s, fill every cell with the average color of the frame under it, push that color away from its gray by 2.2x (clip to 0-255), darken each pixel of a pane toward its lead to 72% at the lead and 100% from 10 pixels in, for a beveled glass look, and draw the lead lines over it in near-black (22, 20, 24) with a slightly softened edge. The lead should cover about a quarter of the frame. Use Python with numpy and OpenCV for the frames and ffmpeg to read and write the video (don't use ffmpeg's drawtext if you add any text), keep the size, 30fps, the 6 second length and the audio, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 stained-glass.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Stained glass: at one second the shot breaks into irregular flat panes of glowing color held in thick dark lead lines.

Usage: python3 stained-glass.py input.mp4 output.mp4
Needs ffmpeg, numpy and OpenCV for the cell layout (pip install numpy opencv-python-headless).
"""
import json
import subprocess
import sys

import cv2
import numpy as np

START = 1.0                # second the picture turns into stained glass
CELL = 30                  # average pane size in pixels
LEAD = 5                   # lead line thickness in pixels
LEAD_COLOR = (22, 20, 24)  # color of the lead lines
SATURATION = 2.2           # color boost for the panes
BEVEL = 10                 # pixels over which each pane darkens toward its lead, like thick glass
SEED = 3                   # same seed, same pane layout: the lead stays put and only the colors change

def layout(w, h):
    """Irregular panes: each pixel labeled with its nearest random seed point (a Voronoi diagram),
    plus the lead mask along the borders and each pixel's shading from its distance to the lead."""
    rng = np.random.default_rng(SEED)
    count = round(w * h / CELL ** 2)
    seeds = np.full((h, w), 255, np.uint8)
    seeds[rng.integers(0, h, count), rng.integers(0, w, count)] = 0
    _, labels = cv2.distanceTransformWithLabels(seeds, cv2.DIST_L2, 5, labelType=cv2.DIST_LABEL_CCOMP)
    border = np.zeros((h, w), np.uint8)
    border[:, 1:] |= labels[:, 1:] != labels[:, :-1]
    border[1:, :] |= labels[1:, :] != labels[:-1, :]
    border[[0, -1], :] = border[:, [0, -1]] = 1
    lead = cv2.dilate(border, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (LEAD, LEAD)))
    to_lead = cv2.distanceTransform(1 - lead, cv2.DIST_L2, 5)
    shade = 0.72 + 0.28 * np.clip(to_lead / BEVEL, 0, 1)
    edge = cv2.GaussianBlur(lead.astype(np.float32), (3, 3), 0)[..., None]   # soften the lead's edge
    return labels.ravel(), labels.max() + 1, shade[..., None], edge

def stained(img):
    """Fill every pane with the average color of the frame under it, saturated and beveled, then lead it."""
    flat = img.reshape(-1, 3).astype(np.float32)
    area = np.bincount(labels, minlength=panes)
    means = np.stack([np.bincount(labels, flat[:, c], panes) for c in range(3)], 1) / np.maximum(area, 1)[:, None]
    gray = means.mean(1, keepdims=True)
    means = np.clip(gray + (means - gray) * SATURATION, 0, 255)
    glass = means[labels].reshape(img.shape) * shade
    return (glass * (1 - edge) + np.array(LEAD_COLOR, np.float32) * edge).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)
labels, panes, shade, edge = layout(w, h)

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 raw := reader.stdout.read(w * h * 3):
    img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    if i / fps >= START:
        img = stained(img)
    writer.stdin.write(img.tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

## More stylize and art

- [Anime style **Anime style**](https://www.recharm.com/effects/anime-style)
- [ASCII art **ASCII art**](https://www.recharm.com/effects/ascii-art)
- [Blueprint **Blueprint**](https://www.recharm.com/effects/blueprint)
- [Charcoal drawing **Charcoal drawing**](https://www.recharm.com/effects/charcoal-drawing)
- [Claymation style **Claymation style**](https://www.recharm.com/effects/claymation-style)

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