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 color pulse on beat to input.mp4 Degrees the colors turn round the color wheel on a beat: 60 Color strength right on a beat: 1.8
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 color pulse on beat 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 |
|---|---|---|
HUE_SHIFT | 60 | Degrees the colors turn round the color wheel on a beat. 60 takes purple to hot pink. |
SATURATION | 1.8 | Color strength right on a beat. 1 leaves it unchanged. |
FADE | 0.3 | Seconds to fade back to the original colors after each beat. |
BASS_HZ | 150 | Beats are found in the sound below this frequency, where the kick drum sits. |
RATE | 22050 | Audio sample rate the beat tracking uses. |
HOP | 256 | Audio samples per analysis step. Smaller gives more exact beat times. |
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 color pulse on the beat to input.mp4. Find the kick drum beats with librosa: take the onset strength of the audio (mono, 22050 Hz) with hop_length 256, fmax 150 Hz and n_mels 8, run librosa.beat.beat_track on that envelope, move each beat back to where its hit starts with librosa.onset.onset_backtrack, convert to seconds and round each to the nearest video frame. For every frame work out a pulse amount a = (1 - p)^2, where p is the seconds since the latest beat divided by 0.3 (a = 0 once 0.3 seconds have passed, and before the first beat). Convert the frame to HSV in floating point (OpenCV, hue 0-360), add 60 x a degrees to the hue (wrapping at 360), multiply the saturation by 1 + 0.8 x a (so 1.8x right on a beat, clipped to 1), keep the brightness, and convert back. The purple bottle should turn hot pink on each beat and look untouched between beats. No text is needed; if you add any, draw it with Pillow, not ffmpeg's drawtext. Keep the size (540x960), 30fps, 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 color-pulse-on-beat.py and run:
python3 color-pulse-on-beat.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Color pulse on beat: on every kick drum the colors swing round the color wheel and surge in saturation, then fade back.
Usage: python3 color-pulse-on-beat.py input.mp4 output.mp4 (the input needs a music track with a beat)
Needs ffmpeg, librosa, numpy and OpenCV (pip install librosa numpy opencv-python-headless).
"""
import json
import subprocess
import sys
import cv2
import librosa
import numpy as np
HUE_SHIFT = 60 # degrees the colors turn round the color wheel on a beat (60 takes purple to hot pink)
SATURATION = 1.8 # color strength on a beat (1 = unchanged)
FADE = 0.3 # seconds to fade back to the original colors after each beat
BASS_HZ = 150 # beats are found in the sound below this frequency (the kick drum)
RATE = 22050 # audio sample rate the beat tracking uses
HOP = 256 # audio samples per analysis step (smaller = more exact beat times)
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)
def beat_times(src):
"""Kick drum beats in seconds: track the beat in the bass, then move each one back to where its hit starts."""
pcm = subprocess.check_output(["ffmpeg", "-v", "error", "-i", src, "-ac", "1", "-ar", str(RATE), "-f", "f32le", "-"])
audio = np.frombuffer(pcm, np.float32)
bass = librosa.onset.onset_strength(y=audio, sr=RATE, hop_length=HOP, fmax=BASS_HZ, n_mels=8)
_, beats = librosa.beat.beat_track(onset_envelope=bass, sr=RATE, hop_length=HOP)
beats = librosa.onset.onset_backtrack(beats, bass)
return librosa.frames_to_time(beats, sr=RATE, hop_length=HOP)
def pulse_at(t, beats):
"""1 right on a beat, fading out to 0 over FADE seconds."""
past = beats[beats <= t + 1e-6]
if not len(past) or t - past[-1] >= FADE:
return 0.0
return (1 - (t - past[-1]) / FADE) ** 2
def recolor(img, amount):
"""Turn the hue by HUE_SHIFT and raise the saturation to SATURATION, both scaled by amount (0-1)."""
hsv = cv2.cvtColor(img.astype(np.float32) / 255, cv2.COLOR_RGB2HSV) # hue 0-360, saturation 0-1
hsv[..., 0] = (hsv[..., 0] + HUE_SHIFT * amount) % 360
hsv[..., 1] = np.clip(hsv[..., 1] * (1 + (SATURATION - 1) * amount), 0, 1)
return (cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB) * 255).round().astype(np.uint8)
src, out = sys.argv[1], sys.argv[2]
w, h, rate, fps = probe(src)
beats = np.round(beat_times(src) * fps) / fps # line each beat up with a video frame
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
stdout=subprocess.PIPE)
writer = subprocess.Popen(["ffmpeg", "-v", "error", "-y", "-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 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)
amount = pulse_at(i / fps, beats)
writer.stdin.write(recolor(img, amount).tobytes() if amount else img.tobytes())
i += 1
writer.stdin.close()
reader.wait()
sys.exit(writer.wait())





