How to add a best seller badge with Claude

A gold laurel-wreath medal reading "Best Seller" drops in from above, swings round to a slight tilt with a small bounce and settles to the left of the bottle's neck.

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 best seller badge to input.mp4
    Top line, bottom line: BEST, SELLER

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 best seller badge 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
TOP_LINE, BOTTOM_LINEBEST, SELLERThe two lines on the medal.
START0.6Second the badge starts to drop in.
SETTLE0.6Seconds it takes to land and settle.
SIZE0.36Badge width as a share of the frame width.
X, Y0.19, 0.36Where the badge settles, 0-1 across and down.
GOLD(240, 190, 50)Wreath, rim and lettering color.
NAVY(25, 35, 75)Medal center color.
TILT-8Degrees it rests at.

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 best seller badge to input.mp4. Draw it with Python and Pillow (don't use ffmpeg's drawtext filter), at 3x size and scaled down for smooth edges, 36% of the frame width across: a gold (240, 190, 50) disc 60% of the badge across with a navy (25, 35, 75) center 53% across and a thin gold ring inside it; "BEST" in bold gold letters 12.5% of the badge tall with "SELLER" 7.5% tall under it and three small gold stars above; around the disc a gold laurel wreath, two branches of 10 pairs of pointed leaves on a thin stem rising from the bottom and nearly meeting at the top. At 0.6 seconds it fades in over 0.24 seconds while dropping from a quarter of the frame height above its spot, shrinking from 1.5x and turning from 22 to -8 degrees (clockwise tilt) over 0.6 seconds with a slight overshoot, then it stays still, centered at 19% across and 36% down, beside the bottle. Keep the audio and save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as best-seller-badge.py and run:

python3 best-seller-badge.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Best seller badge: a gold laurel-wreath medal reading "BEST SELLER" drops in and settles beside the product.

Usage: python3 best-seller-badge.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

TOP_LINE, BOTTOM_LINE = "BEST", "SELLER"
START = 0.6              # second the badge starts to drop in
SETTLE = 0.6             # seconds it takes to land and settle
SIZE = 0.36              # badge width as a share of the frame width
X, Y = 0.19, 0.36        # where the badge settles (0-1 across and down the frame)
GOLD = (240, 190, 50)    # wreath, rim and lettering color
NAVY = (25, 35, 75)      # medal center color
TILT = -8                # degrees it rests at
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 leaf(draw, cx, cy, angle, length, width, color):
    """A pointed leaf centered on (cx, cy), its long axis along angle (radians)."""
    ux, uy, vx, vy = math.cos(angle), math.sin(angle), -math.sin(angle), math.cos(angle)
    along = [i / 6 - 1 for i in range(13)]                          # tip to tip, -1 .. 1
    outline = [(s, 1) for s in along] + [(s, -1) for s in reversed(along)]
    draw.polygon([(cx + ux * s * length / 2 + vx * side * width / 2 * math.sqrt(1 - s * s),
                   cy + uy * s * length / 2 + vy * side * width / 2 * math.sqrt(1 - s * s)) for s, side in outline],
                 fill=color)


def badge():
    """The medal, drawn at 3x and scaled down for smooth edges."""
    d = round(w * SIZE) * 3
    img = Image.new("RGBA", (d, d))
    draw = ImageDraw.Draw(img)
    c, r = d / 2, d * 0.37                                  # center, and radius of the wreath's stems
    draw.arc([c - r, c - r, c + r, c + r], 100, 240, fill=GOLD, width=d // 90)
    draw.arc([c - r, c - r, c + r, c + r], -60, 80, fill=GOLD, width=d // 90)
    for side in (-1, 1):                                    # two laurel branches rising from the bottom
        for k in range(10):
            a = math.radians(90 - side * (12 + k * 14))     # round the side towards the top
            px, py = c + r * math.cos(a), c + r * math.sin(a)
            rising = a - side * math.pi / 2                 # the stem's direction, heading up
            for lean in (-1, 1):                            # a leaf to the outside and to the inside
                angle = rising + side * lean * 0.55
                length = d * (0.115 - k * 0.003)
                leaf(draw, px + math.cos(angle) * length / 2, py + math.sin(angle) * length / 2,
                     angle, length, d * 0.045, GOLD)
    draw.ellipse([d * 0.20, d * 0.20, d * 0.80, d * 0.80], fill=GOLD)
    draw.ellipse([d * 0.235, d * 0.235, d * 0.765, d * 0.765], fill=NAVY)
    draw.ellipse([d * 0.26, d * 0.26, d * 0.74, d * 0.74], outline=GOLD, width=d // 120)
    draw.text((c, d * 0.45), TOP_LINE, font=ImageFont.truetype(FONT_FILE, round(d * 0.125)), anchor="mm", fill=GOLD)
    draw.text((c, d * 0.575), BOTTOM_LINE, font=ImageFont.truetype(FONT_FILE, round(d * 0.075)), anchor="mm", fill=GOLD)
    for i in (-1, 0, 1):                                    # three small stars above the words
        star(draw, c + i * d * 0.06, d * 0.345 - (i == 0) * d * 0.01, d * 0.023, GOLD)
    return img.resize((d // 3, d // 3), Image.LANCZOS)


def star(draw, cx, cy, r, color):
    pts = [(cx + (r if i % 2 == 0 else r * 0.42) * math.sin(i * math.pi / 5),
            cy - (r if i % 2 == 0 else r * 0.42) * math.cos(i * math.pi / 5)) for i in range(10)]
    draw.polygon(pts, fill=color)


def overlay(t):
    """The badge drops from above, shrinking from 1.5x and turning to TILT, with a small bounce as it lands."""
    img = Image.new("RGBA", (w, h))
    if t < START:
        return img
    p = ease_out_back((t - START) / SETTLE)
    scale = 1.5 - 0.5 * p
    b = full.resize((round(full.width * scale), round(full.height * scale)), Image.LANCZOS)
    b = b.rotate(TILT + 30 * (1 - p), resample=Image.BICUBIC, expand=True)
    alpha = min(1, (t - START) / (SETTLE * 0.4))
    b.putalpha(b.getchannel("A").point(lambda a: round(a * alpha)))
    img.alpha_composite(b, (round(w * X - b.width / 2), round(h * Y - h * 0.25 * (1 - p) - b.height / 2)))
    return img


def ease_out_back(p):
    """0 -> 1 with a small overshoot past 1 before settling."""
    p = min(max(p, 0), 1)
    return 1 + 2.70158 * (p - 1) ** 3 + 1.70158 * (p - 1) ** 2


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)
full = badge()
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