How to add a LED screen with Claude

At one second the shot turns into a black panel of glowing round LED dots, 36 across, each lit in the color of the footage behind it, so the bottle reads like it's on a stadium screen.

Get this effect

  1. Switch to Code in your Claude Desktop App

  2. Install the skill (once)

    Show the prompt
    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 LED screen to input.mp4
    Second the picture switches to LEDs: 1.0
    Pixels from one LED's center to the next: 15

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 led screen 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.

SettingDefaultWhat it does
START1.0Second the picture switches to LEDs.
PITCH15Pixels from one LED's center to the next. Bigger means fewer, chunkier LEDs.
DOT0.38LED radius as a share of PITCH. Smaller leaves wider black gaps.
GAIN1.1How much brighter each LED glows than the color it stands for.
SATURATION1.8How much richer the LED colors are than the footage (1 = unchanged).
GLOW0.2Strength of the soft halo around each LED.

Without the skill

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

Show the prompt
Turn input.mp4 (540x960, 30fps) into an LED screen from 1.0 seconds to the end, using Python with numpy and ffmpeg. Before 1.0 seconds leave the frames untouched. From 1.0 seconds on, split each frame into a grid of 15x15 pixel cells (36 across, 64 down) and average each cell's color. Push that color away from its own gray by 1.8x for richer color and multiply it by 1.1, clipped to 0-255. Then redraw the frame on black: in the center of each cell draw one round LED of radius 5.7 pixels (0.38 of the cell) in that cell's color, with a softly anti-aliased edge and a faint halo at 20% brightness fading out toward the cell edge, so dark gaps show between the dots. Keep the size, 30fps, the 6 second length and the audio. There's no text to draw, but if you add any, draw it with Pillow, not ffmpeg's drawtext. Save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as led-screen.py and run:

python3 led-screen.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""LED screen: at one second the shot is rebuilt from glowing round LED dots on a black panel, like a stadium screen.

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

import numpy as np

START = 1.0      # second the picture switches to LEDs
PITCH = 15       # pixels from one LED's center to the next (540 / 15 = 36 LEDs across)
DOT = 0.38       # LED radius as a share of PITCH
GAIN = 1.1       # how much brighter each LED glows than the average color it stands for
SATURATION = 1.8 # how much richer each LED's color is than the footage's (1 = unchanged)
GLOW = 0.2       # strength of the soft halo around each LED


def led_cell():
    """One LED as brightness 0-1 over a PITCH x PITCH cell: a bright round core fading into a soft halo."""
    y, x = np.mgrid[0:PITCH, 0:PITCH] + 0.5 - PITCH / 2
    r = np.hypot(x, y) / (DOT * PITCH)
    core = np.clip((1.15 - r) * 4, 0, 1)
    halo = GLOW * np.exp(-((r - 1).clip(0) ** 2) * 2.5)
    return np.maximum(core, halo).astype(np.float32)


def led_frame(img):
    """Average each PITCH x PITCH cell's color, make it richer, then light that cell's LED in it."""
    cells = img[:rows * PITCH, :cols * PITCH].reshape(rows, PITCH, cols, PITCH, 3).mean(axis=(1, 3))
    gray = cells.mean(axis=2, keepdims=True)
    cells = np.clip((gray + (cells - gray) * SATURATION) * GAIN, 0, 255)
    pic = cell[None, :, None, :, None] * cells[:, None, :, None, :]
    out = np.zeros_like(img)
    out[:rows * PITCH, :cols * PITCH] = pic.reshape(rows * PITCH, cols * PITCH, 3)
    return out


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)
cols, rows = w // PITCH, h // PITCH
cell = led_cell()

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).astype(np.float32)
    if i / fps >= START:
        img = led_frame(img)
    writer.stdin.write(np.clip(img, 0, 255).astype(np.uint8).tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")

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