How to add wrong and right marks with Claude

Two white option cards appear above and below the product, then a red X stamps onto "10-step routine" and greys it out, and a green tick stamps onto "One serum".

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 wrong and right marks to input.mp4
    Wrong, right: 10-step routine, One serum

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 wrong and right marks 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
WRONG, RIGHT10-step routine, One serumThe option that gets the X, and the one that gets the tick.
CARDS_IN0.3Second the two cards fade in.
WRONG_AT1.0Second the red X stamps down.
RIGHT_AT2.0Second the green tick stamps down.
STAMP0.25Seconds a stamp takes to land.
WRONG_Y, RIGHT_Y0.16, 0.84Card centers (0 = top, 1 = bottom).
RED, GREEN(230, 30, 45), (34, 197, 94)X and tick colors.

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 wrong and right marks to input.mp4. Draw two white (255, 255, 255) rounded cards at 94% opacity with a soft black shadow (35% opacity, 6 px lower, blurred by 10 px), each 84% of the frame width and 20% of the frame width tall with corners rounded by 30% of their height, centered horizontally with their centers 16% and 84% of the way down. The top card says "10-step routine" and the bottom card "One serum", in dark (20, 20, 30) bold text 7% of the frame width tall, left-aligned 6% of the frame width in from the card's left edge and centered vertically. Both cards fade in over 0.3 seconds at 0.3 seconds. At 1.0 seconds a red (230, 30, 45) X stamps onto the right end of the top card, and at 2.0 seconds a green (34, 197, 94) tick stamps onto the right end of the bottom card. Each mark sits in a box 26% of the frame width across, centered 79% across and on its card's center line, drawn with round-ended strokes 15% of the box wide inside a white outline 24% of the box wide, and tilted 8 degrees clockwise. A stamp lands over 0.25 seconds, shrinking from twice its size to its normal size with an ease-out while fading in. Once the X has landed, the top card's text turns grey (150, 150, 158). Everything stays on to the end. Use Python with Pillow to draw it and ffmpeg to put it on the video (don't rely on 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 wrong-right-marks.py and run:

python3 wrong-right-marks.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Wrong and right marks: two option cards appear, then a red X stamps onto the top one and a green tick onto the bottom one.

Usage: python3 wrong-right-marks.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, ImageFilter, ImageFont

WRONG, RIGHT = "10-step routine", "One serum"  # the option that gets the X, and the one that gets the tick
CARDS_IN = 0.3           # second the two cards fade in
WRONG_AT = 1.0           # second the red X stamps down
RIGHT_AT = 2.0           # second the green tick stamps down
STAMP = 0.25             # seconds a stamp takes to land
WRONG_Y, RIGHT_Y = 0.16, 0.84  # card centers (0 = top, 1 = bottom)
RED = (230, 30, 45)      # X color
GREEN = (34, 197, 94)    # tick color
SIZE = 0.07              # text height as a share of the frame width
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 ease_out(p):
    p = min(max(p, 0), 1)
    return 1 - (1 - p) ** 3


def mark(points, color):
    """A thick stroked mark (lists of points as fractions of its box) with a white outline, tilted like a stamp."""
    d = round(w * 0.26)
    img = Image.new("RGBA", (d, d))
    draw = ImageDraw.Draw(img)
    for fill, width in (("white", round(d * 0.24)), (color, round(d * 0.15))):
        for line in points:
            xy = [(x * d, y * d) for x, y in line]
            draw.line(xy, fill=fill, width=width, joint="curve")
            for x, y in (xy[0], xy[-1]):  # round the ends
                draw.ellipse([x - width / 2, y - width / 2, x + width / 2, y + width / 2], fill=fill)
    return img.rotate(-8, resample=Image.BICUBIC)


def card(text, cy, t, struck):
    """A white rounded card with a soft shadow and the option on it; once struck, the text greys out."""
    a = ease_out((t - CARDS_IN) / 0.3)
    cw, ch = round(w * 0.84), round(w * 0.2)
    x0, y0 = (w - cw) // 2, round(h * cy - ch / 2)
    layer = Image.new("RGBA", (w, h))
    ImageDraw.Draw(layer).rounded_rectangle([x0, y0 + 6, x0 + cw, y0 + ch + 6], radius=round(ch * 0.3),
                                            fill=(0, 0, 0, round(90 * a)))
    layer = layer.filter(ImageFilter.GaussianBlur(10))
    draw = ImageDraw.Draw(layer)
    draw.rounded_rectangle([x0, y0, x0 + cw, y0 + ch], radius=round(ch * 0.3), fill=(255, 255, 255, round(240 * a)))
    ink = (150, 150, 158) if struck else (20, 20, 30)
    draw.text((x0 + round(w * 0.06), y0 + ch / 2), text, font=font, anchor="lm", fill=ink + (round(255 * a),))
    return layer


def stamp(img, m, cy, t):
    """Drop a mark onto the right end of a card: it lands from twice its size and fades in."""
    p = ease_out(t / STAMP)
    if p <= 0:
        return
    s = 2 - p
    m = m.resize((round(m.width * s), round(m.height * s)), Image.LANCZOS)
    m.putalpha(m.getchannel("A").point(lambda v: round(v * p)))
    img.alpha_composite(m, (round(w * 0.79 - m.width / 2), round(h * cy - m.height / 2)))


def overlay(t):
    img = Image.new("RGBA", (w, h))
    if t < CARDS_IN:
        return img
    img.alpha_composite(card(WRONG, WRONG_Y, t, t >= WRONG_AT + STAMP))
    img.alpha_composite(card(RIGHT, RIGHT_Y, t, False))
    stamp(img, cross, WRONG_Y, t - WRONG_AT)
    stamp(img, tick, RIGHT_Y, t - RIGHT_AT)
    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)
font = ImageFont.truetype(FONT_FILE, round(w * SIZE))
cross = mark([[(0.25, 0.25), (0.75, 0.75)], [(0.75, 0.25), (0.25, 0.75)]], RED)
tick = mark([[(0.2, 0.52), (0.42, 0.74), (0.8, 0.28)]], GREEN)
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