Get this effect
Switch to Code in your Claude Desktop App
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/skillsAsk for the effect
Add a retro grid visualizer to input.mp4 Where the sky meets the floor, as a share of the frame height: 0.62 Sun diameter as a share of the frame width: 0.64
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 retro grid visualizer 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 |
|---|---|---|
HORIZON | 0.62 | Where the sky meets the floor, as a share of the frame height. |
SUN_SIZE | 0.64 | Sun diameter as a share of the frame width. |
SUN_TOP, SUN_LOW | (255, 215, 70), (255, 60, 140) | Sun color at its top and at the horizon. |
GRID | (255, 40, 200) | Grid line color. |
BEAT | 0.5 | Seconds for the grid to scroll one row toward the viewer; 60 / BPM keeps it on the beat. |
BARS | 28 | Number of bars along the horizon, low notes on the left. |
BAR_COLOR | (60, 235, 255) | Bar and card border color. |
MAX_HEIGHT | 0.10 | Tallest bar as a share of the frame height. |
RANGE | 36 | Decibels between an empty bar and a full one. |
FALL | 0.80 | Share of its height a bar keeps each frame as the sound drops. |
CARD | 0.60 | Width of the square video card as a share of the frame width. |
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 into a synthwave music visualizer driven by its own audio, on every frame from start to end. Build the scene in Python with numpy and Pillow: the horizon sits 62% of the way down. The sky above is a vertical gradient from (12, 4, 35) at the top to (110, 25, 120) at the horizon; the floor below runs from (45, 8, 75) at the horizon to (14, 2, 30) at the bottom. A half sun 64% of the frame width across sits on the horizon, centered across, colored from (255, 215, 70) at its top to (255, 60, 140) at the horizon, with 6 horizontal stripes cut through its lower half that get thicker toward the horizon, and a soft glow around it. The floor has a magenta (255, 40, 200) perspective grid with a 3-pixel line along the horizon: 11 lines fanning out from the center of the horizon to the bottom edge, spaced 32% of the frame width apart at the bottom, and horizontal rows where a row at distance z sits (bottom - horizon) / z below the horizon, rows at z = n - shift for n = 1, 2, 3..., with shift = (time / 0.5) mod 1 so the grid scrolls exactly one row toward the viewer every 0.5 seconds; rows are max(2, round(6 / z)) pixels thick. Along the horizon stand 28 cyan (60, 235, 255) bars with rounded tops, spread from 4% to 96% across, each half its slot wide, standing on the horizon and at most 10% of the frame height tall, with a faint 30%-opacity reflection 0.4 times their height on the floor below. Bar heights come from an FFT of the audio: for each video frame take a 4096-sample Hann window centered on that frame's time, split 60 Hz to 12 kHz into 28 log-spaced bands (low notes on the left), convert each band's average magnitude to decibels, and scale each band so its 99th-percentile level over the clip is full height and 36 dB below that is empty; bars rise instantly and fall back by keeping 80% of their height per frame. Give the grid and bars an 8-pixel glow. Put the clip's center square (full width, centered vertically) scaled to 60% of the frame width in a card centered across with its top 6% down, with rounded corners and a 4-pixel cyan (60, 235, 255) glowing border. Pipe raw frames from and back to ffmpeg. Add no text (any text should be drawn with Pillow, not ffmpeg's drawtext). Keep the size, a 30fps frame rate, the 6 second length and the audio, encode H.264 yuv420p and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as retro-grid-visualizer.py and run:
python3 retro-grid-visualizer.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Retro grid visualizer: a neon grid floor scrolls toward a striped sunset sun while cyan bars on the horizon pulse to the track.
Usage: python3 retro-grid-visualizer.py input.mp4 output.mp4
The clip plays in a framed square card above the sun. Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys
import numpy as np
from PIL import Image, ImageDraw, ImageFilter
HORIZON = 0.62 # where the sky meets the floor (0-1 down the frame)
SKY_TOP, SKY_LOW = (12, 4, 35), (110, 25, 120) # sky color at the top and at the horizon
FLOOR_FAR, FLOOR_NEAR = (45, 8, 75), (14, 2, 30) # floor color at the horizon and at the bottom
SUN_SIZE = 0.64 # sun diameter as a share of the frame width
SUN_TOP, SUN_LOW = (255, 215, 70), (255, 60, 140) # sun color at its top and at the horizon
GRID = (255, 40, 200) # grid line color
COLUMNS = 11 # grid lines fanning out from the vanishing point
BEAT = 0.5 # seconds for the grid to scroll one row toward the viewer (120 BPM)
BARS = 28 # spectrum bars along the horizon, low notes on the left
BAR_COLOR = (60, 235, 255) # bar color
MAX_HEIGHT = 0.10 # tallest bar as a share of the frame height
LOW_HZ, HIGH_HZ = 60, 12000 # frequency range the bars cover
RANGE = 36 # decibels between an empty bar and a full one
FALL = 0.80 # share of its height a bar keeps each frame as the sound drops
CARD = 0.60 # width of the square video card as a share of the frame width
CARD_TOP = 0.06 # top of the card (0-1 down the frame)
GLOW = 8 # glow blur radius in pixels
RATE = 48000 # audio sample rate used for the analysis
FPS = 30 # output frame rate
def gradient(top, bottom, height):
"""A vertical color ramp, `height` rows tall and one pixel wide."""
mix = np.linspace(0, 1, height)[:, None]
return (np.array(top) * (1 - mix) + np.array(bottom) * mix)[:, None, :]
def backdrop():
"""The still part of the scene: sky, floor and the striped sun sitting on the horizon."""
sky = np.broadcast_to(gradient(SKY_TOP, SKY_LOW, hy), (hy, w, 3))
floor = np.broadcast_to(gradient(FLOOR_FAR, FLOOR_NEAR, h - hy), (h - hy, w, 3))
img = Image.fromarray(np.concatenate([sky, floor]).astype(np.uint8))
r = round(w * SUN_SIZE / 2)
sun = Image.fromarray(np.broadcast_to(gradient(SUN_TOP, SUN_LOW, r), (r, 2 * r, 3)).astype(np.uint8))
mask = Image.new("L", (2 * r, r))
draw = ImageDraw.Draw(mask)
draw.ellipse([0, 0, 2 * r, 2 * r], fill=255)
for i in range(6): # stripes cut through the lower half, thicker toward the horizon
y = r * (0.5 + i * 0.09)
draw.rectangle([0, y, 2 * r, y + 2 + i * 1.6], fill=0)
layer = Image.new("RGBA", (w, h))
layer.paste(sun, (w // 2 - r, hy - r), mask)
glow = layer.filter(ImageFilter.GaussianBlur(GLOW * 3))
glow.putalpha(glow.getchannel("A").point(lambda a: a // 2))
return Image.alpha_composite(Image.alpha_composite(img.convert("RGBA"), glow), layer)
def spectrum(src):
"""Height (0-1) of every bar on every frame, from an FFT of the audio around that frame."""
pcm = subprocess.check_output(["ffmpeg", "-v", "error", "-i", src, "-vn", "-ac", "1", "-ar", str(RATE),
"-f", "f32le", "-"])
audio = np.frombuffer(pcm, np.float32)
size = 4096
window = np.hanning(size)
freqs = np.fft.rfftfreq(size, 1 / RATE)
edges = np.geomspace(LOW_HZ, HIGH_HZ, BARS + 1)
bands = [(freqs >= lo) & (freqs < hi) for lo, hi in zip(edges, edges[1:])]
padded = np.concatenate([np.zeros(size), audio, np.zeros(size)])
levels = []
for i in range(frames):
mid = size + round(i / FPS * RATE)
mag = np.abs(np.fft.rfft(padded[mid - size // 2:mid + size // 2] * window))
levels.append([20 * np.log10(mag[b].mean() + 1e-9) for b in bands])
db = np.array(levels)
top = np.percentile(db, 99, axis=0)
heights = np.clip((db - (top - RANGE)) / RANGE, 0, 1)
for i in range(1, frames): # rise at once, fall back gently
heights[i] = np.maximum(heights[i], heights[i - 1] * FALL)
return heights
def neon(t, levels):
"""The moving lines: grid rows sliding toward the viewer, and the bars with a faint reflection on the floor."""
img = Image.new("RGBA", (w, h))
draw = ImageDraw.Draw(img)
depth = h - hy
for k in range(-COLUMNS // 2 + 1, COLUMNS // 2 + 1):
draw.line([(w / 2, hy), (w / 2 + k * w * 0.32, h)], fill=GRID, width=3)
shift = (t / BEAT) % 1
for n in range(1, 40): # a row at distance z sits depth / z below the horizon
z = n - shift
if z > 0.95:
draw.line([(0, hy + depth / z), (w, hy + depth / z)], fill=GRID, width=max(2, round(6 / z)))
draw.line([(0, hy), (w, hy)], fill=GRID, width=3)
slot = 0.92 * w / BARS
bar_w = slot * 0.5
for i, level in enumerate(levels):
x0 = 0.04 * w + i * slot + (slot - bar_w) / 2
tall = max(bar_w, level * MAX_HEIGHT * h)
draw.rounded_rectangle([x0, hy - tall, x0 + bar_w, hy - 2], radius=bar_w / 2, fill=BAR_COLOR)
draw.rectangle([x0, hy + 3, x0 + bar_w, hy + 3 + tall * 0.4], fill=BAR_COLOR + (70,))
return Image.alpha_composite(img.filter(ImageFilter.GaussianBlur(GLOW)), img)
def card(frame):
"""The clip's center square, scaled into the card."""
side = round(w * CARD)
sq = min(w, h)
crop = frame.crop(((w - sq) // 2, (h - sq) // 2, (w + sq) // 2, (h + sq) // 2)).resize((side, side), Image.LANCZOS)
mask = Image.new("L", (side, side))
ImageDraw.Draw(mask).rounded_rectangle([0, 0, side - 1, side - 1], radius=side // 14, fill=255)
return crop, mask
def probe(path):
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=width,height:format=duration", "-of", "json", path]))
s = info["streams"][0]
return s["width"], s["height"], float(info["format"]["duration"])
def render(src, out, heights):
"""Build the scene for every frame of src, the clip in its card, and save to out with the audio."""
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-vf", f"fps={FPS}", "-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", str(FPS), "-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)
still = backdrop()
side = round(w * CARD)
x, y = (w - side) // 2, round(h * CARD_TOP)
border = Image.new("RGBA", (w, h))
ImageDraw.Draw(border).rounded_rectangle([x - 4, y - 4, x + side + 3, y + side + 3], radius=side // 14 + 4,
outline=BAR_COLOR, width=4)
border = Image.alpha_composite(border.filter(ImageFilter.GaussianBlur(GLOW)), border)
for i, levels in enumerate(heights):
buf = reader.stdout.read(w * h * 3)
if len(buf) < w * h * 3:
break
scene = Image.alpha_composite(still, neon(i / FPS, levels))
scene = Image.alpha_composite(scene, border).convert("RGB")
clip, mask = card(Image.frombytes("RGB", (w, h), buf))
scene.paste(clip, (x, y), mask)
writer.stdin.write(scene.tobytes())
writer.stdin.close()
reader.stdout.close()
if writer.wait():
sys.exit("ffmpeg failed")
src, out = sys.argv[1:3]
w, h, duration = probe(src)
hy = round(h * HORIZON)
frames = math.ceil(duration * FPS)
render(src, out, spectrum(src))





