---
title: "How to add animated feature callouts with Claude (tested prompt and script)"
description: "Pink lines draw out one after another from the dropper, the glass and the base of the bottle to rounded labels reading \"Glass dropper\", \"15% vitamin C\" and \"Fragrance free\". Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/animated-feature-callouts
---

# How to add animated feature callouts with Claude

Pink lines draw out one after another from the dropper, the glass and the base of the bottle to rounded labels reading "Glass dropper", "15% vitamin C" and "Fragrance free".

Made by the script on this page, from our own test clip.

## Get this effect

1. ### Switch to Code in your [Claude Desktop App](https://claude.com/download)

2. ### Install the skill (once)

   ```
   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 animated feature callouts to input.mp4
   Callouts: Glass dropper, 15% vitamin C, Fragrance free
   ```

## 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 animated feature callouts 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                                                                            |
| ------------------ | ---------------------------------------------- | --------------------------------------------------------------------------------------- |
| `CALLOUTS`         | `Glass dropper, 15% vitamin C, Fragrance free` | Each label, the point on the product, the elbow, the line end and the second it starts. |
| `COLOR`            | `(235, 40, 120)`                               | Color of the lines, dots and label boxes.                                               |
| `DOT, DRAW, LABEL` | `0.15, 0.4, 0.25`                              | Seconds the dot pop, the line draw and the label fade take.                             |
| `TEXT`             | `0.037`                                        | Text height as a share of the frame width.                                              |
| `LINE`             | `3`                                            | Line width in pixels.                                                                   |

## Without the skill

Don’t want to install the skill? Paste this prompt into Claude Code and Claude writes the script itself.

```
Add three animated feature callouts to input.mp4 (540x960) in pink (235, 40, 120). Each one has a dot on the product (radius 7 px, with a thinner ring at radius 14 px), a 3 px line from the dot to an elbow and then flat to a label, and the label: white bold text 20 px high on a pink pill with 12 px padding left and right, 10 px top and bottom, and fully rounded ends. Positions as fractions of the frame (across, down): 1) "Glass dropper": dot at (0.54, 0.27), elbow (0.62, 0.20), line ends at (0.68, 0.20) with the label's left edge there, starting at 0.5 s. 2) "15% vitamin C": dot (0.45, 0.45), elbow (0.38, 0.40), line ends at (0.32, 0.40) with the label's right edge there, starting at 1.0 s. 3) "Fragrance free": dot (0.47, 0.75), elbow (0.38, 0.80), line ends at (0.32, 0.80) with the label's right edge there, starting at 1.5 s. For each: the dot pops in over 0.15 s, then the line draws out from the dot over 0.4 s, then the label fades in while sliding 12 px toward its final spot over 0.25 s, all eased out; everything then stays to the end. Nothing is on screen before 0.5 s. Draw the overlay with Python and Pillow (not ffmpeg's drawtext filter) and composite it with ffmpeg, keep the audio, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `animated-feature-callouts.py` and run:

```
python3 animated-feature-callouts.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Animated feature callouts: pink lines draw out from the dropper, the glass and the base to labels beside the bottle.

Usage: python3 animated-feature-callouts.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

# label, point on the product, elbow where the line turns flat, end of the line (the label's near edge),
# second it starts. Positions are 0-1 across and down the frame; a label sits right of its line end if the
# line heads right, left of it if the line heads left.
CALLOUTS = [("Glass dropper", (0.54, 0.27), (0.62, 0.20), (0.68, 0.20), 0.5),
            ("15% vitamin C", (0.45, 0.45), (0.38, 0.40), (0.32, 0.40), 1.0),
            ("Fragrance free", (0.47, 0.75), (0.38, 0.80), (0.32, 0.80), 1.5)]
COLOR = (235, 40, 120)   # lines, dots and label boxes
DOT = 0.15               # seconds the dot takes to pop onto the product
DRAW = 0.4               # seconds the line takes to draw out
LABEL = 0.25             # seconds the label takes to slide and fade in
TEXT = 0.037             # text height as a share of the frame width
LINE = 3                 # line width in pixels
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")
SS = 2                   # draw at twice the size and shrink, for smooth lines and circles

def progress(t, start, length):
    p = min(max((t - start) / length, 0), 1)
    return 1 - (1 - p) ** 3   # ease out

def label(text):
    """A rounded box with the label in white, at SS times the size."""
    font = ImageFont.truetype(FONT_FILE, round(w * TEXT * SS))
    pad = round(w * 0.022 * SS)
    tw, th = font.getlength(text), font.size
    img = Image.new("RGBA", (round(tw + 2 * pad), round(th + 1.6 * pad)))
    ImageDraw.Draw(img).rounded_rectangle([0, 0, img.width - 1, img.height - 1], radius=img.height // 2, fill=COLOR)
    ImageDraw.Draw(img).text((img.width / 2, img.height / 2), text, font=font, anchor="mm", fill="white")
    return img

def along(points, p):
    """The polyline cut off after share p of its length."""
    lengths = [math.dist(a, b) for a, b in zip(points, points[1:])]
    left = p * sum(lengths)
    out = [points[0]]
    for (a, b), n in zip(zip(points, points[1:]), lengths):
        f = min(left / n, 1)
        out.append((a[0] + (b[0] - a[0]) * f, a[1] + (b[1] - a[1]) * f))
        left -= n
        if left <= 0:
            break
    return out

def overlay(t):
    img = Image.new("RGBA", (w * SS, h * SS))
    draw = ImageDraw.Draw(img)
    for (text, *spots, start), box in zip(CALLOUTS, boxes):
        if t < start:
            continue
        anchor, elbow, end = [(x * w * SS, y * h * SS) for x, y in spots]
        r = progress(t, start, DOT) * 7 * SS
        draw.ellipse([anchor[0] - 2 * r, anchor[1] - 2 * r, anchor[0] + 2 * r, anchor[1] + 2 * r], outline=COLOR + (150,), width=2 * SS)
        draw.ellipse([anchor[0] - r, anchor[1] - r, anchor[0] + r, anchor[1] + r], fill=COLOR)
        drawn = progress(t, start + DOT, DRAW)
        if drawn > 0:
            draw.line(along([anchor, elbow, end], drawn), fill=COLOR, width=LINE * SS, joint="curve")
        shown = progress(t, start + DOT + DRAW, LABEL)
        if shown > 0:
            heads = 1 if end[0] > elbow[0] else -1
            x = end[0] - (12 * SS * (1 - shown) if heads > 0 else box.width - 12 * SS * (1 - shown))
            faded = box.copy()
            faded.putalpha(faded.getchannel("A").point(lambda a: round(a * shown)))
            img.alpha_composite(faded, (round(x), round(end[1] - box.height / 2)))
    return img.resize((w, h), Image.LANCZOS)

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)
boxes = [label(text) for text, *_ in CALLOUTS]
render(src, out)
```

## More product shots

- [Before/after slider **Before/after slider**](https://www.recharm.com/effects/before-after-slider)
- [Best seller badge **Best seller badge**](https://www.recharm.com/effects/best-seller-badge)
- [Callout arrows and circles **Callout arrows and circles**](https://www.recharm.com/effects/callout-arrows-and-circles)
- [Carousel swipe **Carousel swipe**](https://www.recharm.com/effects/carousel-swipe)
- [Comparison table **Comparison table**](https://www.recharm.com/effects/comparison-table)

## 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](https://www.recharm.com/get-started)
