How to add a verified badge with Claude

A white review card from Maya R. fades in under the bottle, then a green tick badge pops in right after her name and "Verified buyer" fades in beside it.

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 verified badge to input.mp4
    Name: Maya R.
    Label: Verified buyer
    Quote: “My skin glows after one week.”

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 verified 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
NAMEMaya R.Reviewer's name; the badge sits right after it.
LABELVerified buyerWords that fade in next to the badge.
QUOTE“My skin glows after one week.”The review line under the name.
BADGE_IN1.2Second the badge pops in.
CARD_Y0.78Card center, 0-1 down the frame.
GREEN(22, 163, 74)Badge and label color.

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 verified-buyer review card to input.mp4 (540x960). The card is white, 432x132 pixels with 22-pixel rounded corners, centered across with its center 78% down the frame. On it: a purple (124, 92, 210) circle avatar 64 pixels across, 20 pixels in from the card's left edge, with a white bold "M"; the name "Maya R." in 28-pixel bold dark gray (25, 25, 35) starting 100 pixels in, 42 pixels from the card's top; and under it, 78 pixels from the top, the quote “My skin glows after one week.” in 19-pixel bold gray (80, 80, 95). The card fades in from 0.3 to 0.6 seconds. At 1.2 seconds a green (22, 163, 74) circle 30 pixels across with a white tick pops in centered 22 pixels after the end of the name, on the name's line, scaling up over 0.3 seconds with a slight overshoot; the words "Verified buyer" in 19-pixel bold of the same green fade in just right of it from 1.35 to 1.6 seconds. Everything then stays to the end. Use Python with Pillow to draw it and ffmpeg to put it on the video (don't use ffmpeg's drawtext filter), keep the audio, and save it as output.mp4.

Run it without Claude

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

python3 verified-badge.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Verified badge: a green checkmark badge pops in beside a reviewer's name on a review card.

Usage: python3 recipe.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

NAME = "Maya R."                     # reviewer's name
LABEL = "Verified buyer"             # words that appear next to the badge
QUOTE = "\u201cMy skin glows after one week.\u201d"  # the review line under the name
CARD_IN = 0.3            # second the card fades in
BADGE_IN = 1.2           # second the badge pops in beside the name
POP = 0.3                # seconds the pop takes
CARD_W = 0.80            # card width as a share of the frame width
CARD_Y = 0.78            # card center, 0-1 down the frame
GREEN = (22, 163, 74)    # badge and label color
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 font(size):
    return ImageFont.truetype(FONT_FILE, round(size))


def card():
    """The white card with avatar, name and quote; returns it and where the badge goes on it."""
    cw = round(w * CARD_W)
    u = cw / 432                     # layout unit, so the card scales with the frame
    ch = round(132 * u)
    img = Image.new("RGBA", (cw, ch))
    draw = ImageDraw.Draw(img)
    draw.rounded_rectangle([0, 0, cw - 1, ch - 1], radius=round(22 * u), fill="white")
    draw.ellipse([20 * u, 22 * u, 84 * u, 86 * u], fill=(124, 92, 210))
    draw.text((52 * u, 54 * u), NAME[0], font=font(32 * u), anchor="mm", fill="white")
    name_font = font(28 * u)
    draw.text((100 * u, 42 * u), NAME, font=name_font, anchor="lm", fill=(25, 25, 35))
    draw.text((100 * u, 78 * u), QUOTE, font=font(19 * u), anchor="lm", fill=(80, 80, 95))
    badge_center = (100 * u + name_font.getlength(NAME) + 22 * u, 42 * u)
    return img, badge_center, u


def badge(d):
    """A green disc with a white tick, d pixels across."""
    img = Image.new("RGBA", (d, d))
    draw = ImageDraw.Draw(img)
    draw.ellipse([0, 0, d - 1, d - 1], fill=GREEN)
    draw.line([(d * 0.27, d * 0.52), (d * 0.44, d * 0.69), (d * 0.74, d * 0.34)], fill="white",
              width=max(2, round(d * 0.13)), joint="curve")
    return img


def overlay(t):
    img = Image.new("RGBA", (w, h))
    if t < CARD_IN:
        return img
    layer = base.copy()
    bx, by = badge_center
    scale = ease_out_back((t - BADGE_IN) / POP) if t >= BADGE_IN else 0
    if scale > 0.05:
        b = tick.resize((max(1, round(tick.width * scale)),) * 2, Image.LANCZOS)
        layer.alpha_composite(b, (round(bx - b.width / 2), round(by - b.height / 2)))
        fade = min(1, max(0, (t - BADGE_IN - 0.15) / 0.25))
        draw = ImageDraw.Draw(layer)
        draw.text((bx + 22 * u, by), LABEL, font=font(19 * u), anchor="lm", fill=GREEN + (round(255 * fade),))
    alpha = min(1, (t - CARD_IN) / 0.3)
    if alpha < 1:
        layer.putalpha(layer.getchannel("A").point(lambda a: round(a * alpha)))
    img.alpha_composite(layer, ((w - layer.width) // 2, round(h * CARD_Y - layer.height / 2)))
    return img


def ease_out_back(p):
    """0 -> 1 with a small overshoot past 1 before settling, which reads as a 'pop'."""
    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)
base, badge_center, u = card()
tick = badge(round(30 * u))
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