How to add a TV static with Claude

At 2 seconds the picture dissolves into hissing black-and-white snow with a bright band rolling down it, then tunes back in a second later.

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 TV static to input.mp4
    Second the static starts to break through: 2.0
    Seconds from the first speck to the clean picture again: 1.2

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 tv static 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
START2.0Second the static starts to break through.
LENGTH1.2Seconds from the first speck to the clean picture again.
FADE0.2Seconds the static takes to cover the picture, and to clear.
GRAIN2Size of one static speck, in pixels.
BAND_SPEED1.5Screen heights per second the bright band rolls down.
HISS0.2Loudness of the hiss, 0 to 1.
DUCK0.2How loud the clip's own sound stays under the hiss, 0 to 1.
SEED7Random seed; change it for a different but repeatable pattern of static.

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
Add a burst of TV static to input.mp4. Use Python with numpy and ffmpeg, working frame by frame at 30fps. The static runs from 2.0s to 3.2s: it fades in over 0.2 seconds (2.0s to 2.2s), fully covers the picture from 2.2s to 3.0s, and fades out over 0.2 seconds (3.0s to 3.2s), mixed as (1 - p) * picture + p * static. Leave every frame outside 2.0s to 3.2s untouched. Static is black-and-white snow: a random gray value from 0 to 255 for every 2x2 pixel block, new each frame, multiplied by 0.75 plus 0.4 times a horizontal Gaussian band (width 6% of the frame height) that rolls down the screen at 1.5 screen heights per second and wraps back to the top, with every odd row darkened to 80% for scan lines. Sound: keep the clip's audio, turned down to 20% volume from 2.0s to 3.2s, and mix in a white-noise hiss at amplitude 0.2 over the same 1.2 seconds with 0.2 second fades in and out, without normalizing. Use a fixed random seed so it repeats. Keep the video 6 seconds long and save it as output.mp4 (H.264, yuv420p).

Run it without Claude

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

python3 tv-static.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""TV static: halfway through the clip the picture dissolves into hissing black-and-white snow with a bright band rolling down it, then tunes back in.

Usage: python3 tv-static.py input.mp4 output.mp4  (the clip needs an audio track)
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys

import numpy as np

START = 2.0         # second the static starts to break through
LENGTH = 1.2        # seconds from the first speck to the clean picture again
FADE = 0.2          # seconds the static takes to cover the picture, and to clear again
GRAIN = 2           # size of one static speck, in pixels
BAND_SPEED = 1.5    # screen heights per second the bright band rolls down the static
HISS = 0.2          # loudness of the static's hiss (0-1)
DUCK = 0.2          # how loud the clip's own sound stays under the hiss (0-1)
SEED = 7            # change for a different (but repeatable) pattern of static


def static(t):
    """One frame of black-and-white snow with scan lines and a bright band that rolls down over time."""
    specks = rng.integers(0, 256, (h // GRAIN + 1, w // GRAIN + 1)).astype(np.float32)
    snow = specks.repeat(GRAIN, 0).repeat(GRAIN, 1)[:h, :w]
    rows = np.arange(h)[:, None]
    band = np.exp(-((rows - (t * BAND_SPEED % 1) * h) / (h * 0.06)) ** 2)
    snow = snow * (0.75 + 0.4 * band) * np.where(rows % 2, 0.8, 1.0)
    return np.repeat(np.clip(snow, 0, 255)[:, :, None], 3, axis=2)


def cover(t):
    """How much of the picture the static hides at time t: 0 outside the burst, 1 at full static."""
    into, left = t - START, START + LENGTH - t
    return float(np.clip(min(into, left) / FADE, 0, 1))


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"]
rng = np.random.default_rng(SEED)

# Sound: the clip's own audio drops to DUCK under a white-noise hiss for the length of the static
end = START + LENGTH
sound = (f"[1:a]volume='if(between(t,{START},{end}),{DUCK},1)':eval=frame[a];"
         f"anoisesrc=d={LENGTH}:c=white:a={HISS}:r=48000,afade=t=in:d={FADE},afade=t=out:st={LENGTH - FADE}:d={FADE},"
         f"adelay={START * 1000:.0f}:all=1[hiss];"
         f"[a][hiss]amix=inputs=2:duration=first:normalize=0[au]")
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-vf", "fps=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, "-filter_complex", sound, "-map", "0:v", "-map", "[au]",
                           "-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p", "-c:a", "aac", "-shortest", out],
                          stdin=subprocess.PIPE)
i = 0
while True:
    raw = reader.stdout.read(w * h * 3)
    if len(raw) < w * h * 3:
        break
    img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    p = cover(i / 30)
    if p > 0:
        img = ((1 - p) * img + p * static(i / 30)).astype(np.uint8)
    writer.stdin.write(img.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