---
title: "How to add a CRT TV with Claude (tested prompt and script)"
description: "The whole clip bulges onto a rounded glass tube inside a black bezel, striped with thin dark scanlines and softened by a hazy glow and slight color fringing, like footage playing on an old television. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/crt-tv
---

# How to add a CRT TV with Claude

The whole clip bulges onto a rounded glass tube inside a black bezel, striped with thin dark scanlines and softened by a hazy glow and slight color fringing, like footage playing on an old television.

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 CRT TV to input.mp4
   How much the screen bulges; 0 is flat glass, 0.25 is a fishbowl: 0.12
   Radius of the screen's rounded corners, in pixels: 60
   ```

## 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 crt tv 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                                                         |
| --------------- | ------- | -------------------------------------------------------------------- |
| `CURVE`         | `0.12`  | How much the screen bulges; 0 is flat glass, 0.25 is a fishbowl.     |
| `CORNER`        | `60`    | Radius of the screen's rounded corners, in pixels.                   |
| `EDGE`          | `6`     | Pixels over which the picture fades into the black bezel.            |
| `SCANLINE`      | `4`     | Rows per scanline: the last row of each is darkened.                 |
| `SCANLINE_DARK` | `0.4`   | Brightness left in the dark row (1 hides the scanlines).             |
| `BLEED`         | `2`     | Pixels red is pushed right and blue left, like a soft analog signal. |
| `GLOW`          | `0.35`  | How strongly a blurred copy is screened back over the picture (0-1). |
| `VIGNETTE`      | `0.45`  | How much the screen darkens toward its corners (0-1).                |
| `FLICKER`       | `0.04`  | Frame-to-frame brightness wobble (0.04 = up to 4%).                  |

## 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 it's playing on an old CRT television, from the first frame to the last, using Python with numpy and Pillow and ffmpeg for reading and writing frames. For each 540x960 frame: (1) Color bleed: shift the red channel 2 pixels right and the blue channel 2 pixels left. (2) Soften with a Gaussian blur of radius 1, then add a glow by screen-blending a radius-10 Gaussian-blurred copy at 0.35 strength. (3) Bulge it onto curved glass: with u and v running from -1 to 1 across and down the output, take each output pixel from the source at (u, v) * (1 + 0.12 * (u^2 + v^2)). Anywhere that lands outside the source frame is black bezel; give the screen 60-pixel rounded corners (measured in source pixels) and fade the edge to black over 6 pixels. This leaves the left and right edges black about 26 pixels in at mid-height and the top and bottom about 46 pixels in, with the bezel thicker toward the corners. (4) Darken toward the corners: multiply by 1 - 0.45 * (su^2 + sv^2) / 2, where su, sv are the warped source coordinates. (5) Scanlines: every 4th row of the output (rows 3, 7, 11...) is multiplied by 0.4, straight across the frame. (6) Flicker: multiply frame i's brightness by 1 + 0.04 * sin(2.3i) * sin(0.7i). Keep the 540x960 size, 30fps, 6-second length and the audio. Save it as output.mp4.
```

## Run it without Claude

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

```
python3 crt-tv.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""CRT TV: the picture bulges onto a curved glass screen with dark scanlines, soft color bleed and a glow.

Usage: python3 crt-tv.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import subprocess
import sys

import numpy as np
from PIL import Image, ImageFilter

CURVE = 0.12         # how much the screen bulges; 0 is flat glass, 0.25 is a fishbowl
CORNER = 60          # radius of the screen's rounded corners, in pixels
EDGE = 6             # pixels over which the picture fades into the black bezel
SCANLINE = 4         # rows per scanline: the last row of each is darkened
SCANLINE_DARK = 0.4  # brightness left in the dark row (1 hides the scanlines)
BLEED = 2            # pixels red is pushed right and blue left, like a soft analog signal
GLOW = 0.35          # how strongly a blurred copy is screened back over the picture (0-1)
VIGNETTE = 0.45      # how much the screen darkens toward its corners (0-1)
FLICKER = 0.04       # frame-to-frame brightness wobble (0.04 = up to 4%)

def screen_map():
    """For every output pixel, the source pixel it shows through the curved glass, and how much of it is lit."""
    v, u = np.mgrid[-1:1:h * 1j, -1:1:w * 1j].astype(np.float32)
    bulge = 1 + CURVE * (u * u + v * v)
    su, sv = u * bulge, v * bulge
    xs = np.clip(((su + 1) / 2 * (w - 1)).round().astype(int), 0, w - 1)
    ys = np.clip(((sv + 1) / 2 * (h - 1)).round().astype(int), 0, h - 1)
    # distance inside the screen's rounded edge, in pixels: negative means outside, on the bezel
    qx = np.abs(su) * w / 2 - (w / 2 - CORNER)
    qy = np.abs(sv) * h / 2 - (h / 2 - CORNER)
    inside = CORNER - np.hypot(np.maximum(qx, 0), np.maximum(qy, 0)) - np.minimum(np.maximum(qx, qy), 0)
    lit = np.clip(inside / EDGE, 0, 1)
    lit *= 1 - VIGNETTE * (su * su + sv * sv) / 2
    lit *= np.where(np.arange(h)[:, None] % SCANLINE == SCANLINE - 1, SCANLINE_DARK, 1.0)
    return ys, xs, lit[..., None].astype(np.float32)

def crt(img, i):
    bled = img.copy()
    bled[:, BLEED:, 0] = img[:, :-BLEED, 0]
    bled[:, :-BLEED, 2] = img[:, BLEED:, 2]
    soft = np.asarray(Image.fromarray(bled).filter(ImageFilter.GaussianBlur(1)), np.float32)
    halo = np.asarray(Image.fromarray(bled).filter(ImageFilter.GaussianBlur(10)), np.float32)
    glowing = 255 - (255 - soft) * (255 - GLOW * halo) / 255
    flicker = 1 + FLICKER * np.sin(i * 2.3) * np.sin(i * 0.7)
    return np.clip(glowing[ys, xs] * lit * flicker, 0, 255).astype(np.uint8)

src, out = sys.argv[1:3]
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                           "stream=width,height", "-of", "json", src]))["streams"][0]
w, h = info["width"], info["height"]
ys, xs, lit = screen_map()

reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-r", "30", "-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", "30", "-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
    writer.stdin.write(crt(np.frombuffer(raw, np.uint8).reshape(h, w, 3), i).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)
