How to add an equalizer icon with Claude

A dark "now playing" card slides up near the bottom, and four little yellow bars beside the song title bounce up and down in time with the beat.

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 an equalizer icon to input.mp4
    Title, artist: Golden Hour, Summer Drive

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 an equalizer icon 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
TITLE, ARTISTGolden Hour, Summer DriveThe song title and the line under it.
START0.5Second the card slides in.
BPM120Tempo the bars bounce to; set it to your track's.
SPEEDS(1.0, 1.5, 0.75, 1.25)Bounces per beat for each of the four bars.
LOW0.25Shortest a bar gets, as a share of its full height.
BAR_COLOR(255, 205, 40)Bar color.
Y0.84Card center, as a share of the frame height.

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 "now playing" card with an animated equalizer icon to input.mp4 (540x960, 30fps, a 120 BPM drum loop). The card is a dark (18, 18, 22) rounded pill at 84% opacity, 80% of the frame width by 9% of the frame height (432x86 pixels), fully rounded ends, centered horizontally with its center 84% of the way down. On it, starting 1.15 card-heights from its left edge, draw "Golden Hour" in bold white at 30% of the card height, its middle at 38% down the card, and under it "Summer Drive" in bold gray (185, 185, 195) at 21% of the card height, its middle at 70% down the card. At the card's left draw four yellow (255, 205, 40) bars with rounded ends, each 10% of the card height wide with gaps of 6.5% of the card height, the first starting 38% of the card height from the card's left edge, all standing on a shared baseline so that a full bar (48% of the card height) is centered vertically in the card. Bar i's height is full x (0.25 + 0.75 x |cos(pi x (beat x speed_i + phase_i))|), where beat = seconds x 120 / 60, speeds are 1.0, 1.5, 0.75, 1.25 and phases 0, 0.35, 0.6, 0.2, so the first bar is full height on every beat and at its shortest halfway between beats. Nothing is on screen before 0.5 seconds; the card then fades in while sliding up from 5% of the frame height lower over 0.4 seconds with an ease-out (1 - (1 - p)^3), and stays to the end. Use Python with Pillow to draw the card, text and bars (don't rely on ffmpeg's drawtext filter) and ffmpeg to put them on the video, keep the audio, and save it as output.mp4.

Run it without Claude

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

python3 equalizer-icon.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Equalizer icon: a "now playing" card slides up with four yellow bars bouncing to the beat beside the song title.

Usage: python3 equalizer-icon.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import math
import subprocess
import sys
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

TITLE, ARTIST = "Golden Hour", "Summer Drive"
START = 0.5                       # second the card slides in
SLIDE = 0.4                       # seconds the slide takes
BPM = 120                         # tempo the bars bounce to
SPEEDS = (1.0, 1.5, 0.75, 1.25)   # bounces per beat, one per bar
PHASES = (0.0, 0.35, 0.6, 0.2)    # where each bar starts in its bounce (0-1)
LOW = 0.25                        # shortest a bar gets, as a share of its full height
BAR_COLOR = (255, 205, 40)        # bar color
CARD_W, CARD_H = 0.80, 0.09       # card size as shares of the frame width and height
Y = 0.84                          # card center (0 = top, 1 = bottom)
FONTS = ["/System/Library/Fonts/Supplemental/Arial Bold.ttf",     # macOS
         "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",  # Linux
         "C:/Windows/Fonts/arialbd.ttf"]                          # Windows
FONT_FILE = next((f for f in FONTS if Path(f).exists()), None) or sys.exit("No bold font found: add one to FONTS")


def card():
    """The dark rounded card with the title and artist, and the box where the bars go."""
    cw, ch = round(w * CARD_W), round(h * CARD_H)
    img = Image.new("RGBA", (cw, ch))
    draw = ImageDraw.Draw(img)
    draw.rounded_rectangle([0, 0, cw - 1, ch - 1], radius=ch // 2, fill=(18, 18, 22, 215))
    text_x = round(ch * 1.15)
    draw.text((text_x, ch * 0.38), TITLE, font=ImageFont.truetype(FONT_FILE, round(ch * 0.30)), anchor="lm", fill="white")
    draw.text((text_x, ch * 0.70), ARTIST, font=ImageFont.truetype(FONT_FILE, round(ch * 0.21)), anchor="lm",
              fill=(185, 185, 195))
    return img


def bars(img, t):
    """Four bars standing on a shared baseline at the card's left, each bouncing at its own speed."""
    ch = img.height
    draw = ImageDraw.Draw(img)
    full = ch * 0.48
    bar_w, gap = ch * 0.10, ch * 0.065
    x = ch * 0.38
    base = ch / 2 + full / 2
    beat = t * BPM / 60
    for speed, phase in zip(SPEEDS, PHASES):
        level = LOW + (1 - LOW) * abs(math.cos(math.pi * (beat * speed + phase)))
        draw.rounded_rectangle([x, base - full * level, x + bar_w, base], radius=bar_w / 2, fill=BAR_COLOR)
        x += bar_w + gap


def overlay(t):
    img = Image.new("RGBA", (w, h))
    p = min(max((t - START) / SLIDE, 0), 1)
    if p == 0:
        return img
    ease = 1 - (1 - p) ** 3
    c = base_card.copy()
    bars(c, t)
    c.putalpha(c.getchannel("A").point(lambda a: round(a * ease)))
    img.alpha_composite(c, (round((w - c.width) / 2), round(h * Y - c.height / 2 + (1 - ease) * h * 0.05)))
    return img


def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height,r_frame_rate:format=duration", "-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), float(info["format"]["duration"])


def render(src, out):
    """Draw overlay(t) on every frame of src and save to out, keeping the audio."""
    ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
                           "-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
                           "-filter_complex", "[0:v][1:v]overlay=shortest=1,format=yuv420p[v]",
                           "-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out],
                          stdin=subprocess.PIPE)
    for i in range(math.ceil(duration * fps)):
        ff.stdin.write(overlay(i / fps).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")


src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
base_card = card()
render(src, out)

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