---
title: "How to add a video call frame with Claude (tested prompt and script)"
description: "The clip plays as a rounded tile in a dark video-call screen, with a \"Maya\" name tag and red mute badge in its corner, a ticking call timer above and mic, camera and red hang-up buttons below. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/video-call-frame
---

# How to add a video call frame with Claude

The clip plays as a rounded tile in a dark video-call screen, with a "Maya" name tag and red mute badge in its corner, a ticking call timer above and mic, camera and red hang-up buttons below.

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 a video call frame to input.mp4
   Name: Maya
   ```

## 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 video call frame 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                                                     |
| ------------ | -------------------------- | ---------------------------------------------------------------- |
| `NAME`       | `Maya`                     | Name on the tag in the tile's corner.                            |
| `CALL_START` | `83`                       | Call timer reading at the first frame, in seconds (83 = 01:23).  |
| `BACKGROUND` | `(32, 33, 36)`             | App background around the tile.                                  |
| `BUTTON`     | `(60, 64, 67)`             | Mic and camera button color.                                     |
| `RED`        | `(234, 67, 53)`            | Hang-up button and mute badge color.                             |
| `TILE`       | `(0.04, 0.10, 0.96, 0.86)` | Video tile's left, top, right and bottom as shares of the frame. |
| `RADIUS`     | `0.045`                    | Tile corner radius as a share of the frame width.                |

## Without the skill

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

```
Make input.mp4 look like a live video call. Keep the 540x960 frame and fill it with a dark (32, 33, 36) app background. Put the video in a rounded tile from 4% to 96% across and 10% to 86% down (corner radius 4.5% of the frame width), scaled to cover the tile and cropped from the middle. In the tile's bottom-left corner, inset 3% of the frame width, add a name tag: a black pill at 60% opacity, 6.8% of the frame width tall, holding a red (234, 67, 53) circle with a white crossed-out microphone and the name "Maya" in white bold text 3.4% of the frame width tall. Centered above the tile at 5% down, show a call timer in white bold text 4.2% of the frame width tall with a small green (52, 168, 83) dot to its left, reading 01:23 at the start and counting up one second per second. Centered in the band below the tile (at 93% down), draw three controls centered at 33%, 50% and 69% across: a (60, 64, 67) circle 12% of the frame width across with a white crossed-out microphone, the same circle with a white camera icon, and a red (234, 67, 53) pill 16% of the frame width wide and 12% tall with a white hang-up handset. Show it all from the first frame to the end. Use Python with Pillow to draw everything (draw any text with Pillow, not ffmpeg's drawtext) and ffmpeg to read and write 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 `video-call-frame.py` and run:

```
python3 video-call-frame.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Video call frame: the clip plays as the live tile of a video call, with a name tag, mute icons and a call timer.

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

from PIL import Image, ImageDraw, ImageFont

NAME = "Maya"                # name on the tag in the tile's bottom-left corner
CALL_START = 83              # call timer reading at the first frame, in seconds (83 = 01:23)
BACKGROUND = (32, 33, 36)    # app background around the tile
BUTTON = (60, 64, 67)        # mic and camera button color
RED = (234, 67, 53)          # hang-up button and mute badge color
TILE = (0.04, 0.10, 0.96, 0.86)  # video tile: left, top, right, bottom as shares of the frame
RADIUS = 0.045               # tile corner radius 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")
SS = 3                       # shapes are drawn at 3x and scaled down, for smooth edges

def mic(draw, cx, cy, s, color, behind):
    """A microphone icon of height ~s with a slash through it; `behind` is the color it sits on."""
    draw.rounded_rectangle([cx - 0.2 * s, cy - 0.5 * s, cx + 0.2 * s, cy + 0.12 * s], 0.2 * s, fill=color)
    draw.arc([cx - 0.36 * s, cy - 0.3 * s, cx + 0.36 * s, cy + 0.3 * s], 0, 180, fill=color, width=round(0.09 * s))
    draw.line([cx, cy + 0.3 * s, cx, cy + 0.48 * s], fill=color, width=round(0.09 * s))
    draw.line([cx - 0.2 * s, cy + 0.48 * s, cx + 0.2 * s, cy + 0.48 * s], fill=color, width=round(0.09 * s))
    draw.line([cx - 0.42 * s, cy - 0.5 * s, cx + 0.42 * s, cy + 0.5 * s], fill=behind, width=round(0.22 * s))
    draw.line([cx - 0.42 * s, cy - 0.5 * s, cx + 0.42 * s, cy + 0.5 * s], fill=color, width=round(0.09 * s))

def camera(draw, cx, cy, s, color):
    draw.rounded_rectangle([cx - 0.5 * s, cy - 0.3 * s, cx + 0.15 * s, cy + 0.3 * s], 0.1 * s, fill=color)
    draw.polygon([(cx + 0.2 * s, cy), (cx + 0.5 * s, cy - 0.25 * s), (cx + 0.5 * s, cy + 0.25 * s)], fill=color)

def hang_up(draw, cx, cy, s, color):
    """A phone handset lying face down: a thick arc with an ear and a mouth piece."""
    r = 0.5 * s
    cy += 0.2 * s
    draw.arc([cx - r, cy - r, cx + r, cy + r], 200, 340, fill=color, width=round(r * 0.38))
    for ex in (cx - r * 0.81, cx + r * 0.81):
        ey = cy - r * 0.3
        draw.ellipse([ex - r * 0.3, ey - r * 0.3, ex + r * 0.3, ey + r * 0.3], fill=color)

def call_ui():
    """Everything that doesn't move, drawn once: the background with a hole for the tile, name tag and buttons."""
    W, H = w * SS, h * SS
    x0, y0, x1, y1 = tile_box(SS)
    ui = Image.new("RGBA", (W, H), BACKGROUND)
    hole = Image.new("L", (W, H), 255)
    ImageDraw.Draw(hole).rounded_rectangle([x0, y0, x1, y1], RADIUS * W, fill=0)
    ui.putalpha(hole)
    draw = ImageDraw.Draw(ui)

    # name tag: a dark pill with a red mute badge and the name
    font = ImageFont.truetype(FONT_FILE, round(0.034 * W))
    pad, tag_h = 0.018 * W, 0.068 * W
    badge = tag_h - 2 * 0.012 * W
    tx, ty = x0 + 0.03 * W, y1 - 0.03 * W - tag_h
    tag_w = 0.012 * W + badge + pad * 0.8 + draw.textlength(NAME, font=font) + pad
    tag = Image.new("RGBA", (W, H))
    ImageDraw.Draw(tag).rounded_rectangle([tx, ty, tx + tag_w, ty + tag_h], tag_h / 2, fill=(0, 0, 0, 150))
    ui.alpha_composite(tag)
    bx, by = tx + 0.012 * W + badge / 2, ty + tag_h / 2
    draw.ellipse([bx - badge / 2, by - badge / 2, bx + badge / 2, by + badge / 2], fill=RED)
    mic(draw, bx, by, badge * 0.55, "white", RED)
    draw.text((bx + badge / 2 + pad * 0.8, by), NAME, font=font, fill="white", anchor="lm")

    # control bar: muted mic, camera, red hang-up
    cy, r, gap = (y1 + H) / 2, 0.06 * W, 0.17 * W
    for cx in (W / 2 - gap, W / 2):
        draw.ellipse([cx - r, cy - r, cx + r, cy + r], fill=BUTTON)
    mic(draw, W / 2 - gap, cy, r * 0.9, "white", BUTTON)
    camera(draw, W / 2, cy, r * 0.9, "white")
    cx = W / 2 + gap + r * 0.35
    draw.rounded_rectangle([cx - r * 1.35, cy - r, cx + r * 1.35, cy + r], r, fill=RED)
    hang_up(draw, cx, cy, r * 1.1, "white")
    return ui.resize((w, h), Image.LANCZOS)

def tile_box(scale=1):
    return [round(TILE[0] * w * scale), round(TILE[1] * h * scale), round(TILE[2] * w * scale), round(TILE[3] * h * scale)]

def fill_tile(video):
    """Scale the video to cover the tile and crop the middle, as call apps do."""
    x0, y0, x1, y1 = tile_box()
    tw, th = x1 - x0, y1 - y0
    k = max(tw / w, th / h)
    big = video.resize((round(w * k), round(h * k)), Image.LANCZOS)
    left, top = (big.width - tw) // 2, (big.height - th) // 2
    frame = Image.new("RGB", (w, h))
    frame.paste(big.crop((left, top, left + tw, top + th)), (x0, y0))
    return frame

def call_frame(video, t):
    img = fill_tile(video).convert("RGBA")
    img.alpha_composite(ui)
    draw = ImageDraw.Draw(img)
    seconds = CALL_START + int(t)
    timer = f"{seconds // 60:02d}:{seconds % 60:02d}"
    cy = TILE[1] * h / 2
    tw = draw.textlength(timer, font=timer_font)
    dot = 0.012 * w
    draw.ellipse([w / 2 - tw / 2 - 3 * dot, cy - dot, w / 2 - tw / 2 - dot, cy + dot], fill=(52, 168, 83))
    draw.text((w / 2 + dot, cy), timer, font=timer_font, fill="white", anchor="mm")
    return img.convert("RGB")

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

src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
ui = call_ui()
timer_font = ImageFont.truetype(FONT_FILE, round(0.042 * w))
reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
                          stdout=subprocess.PIPE)
writer = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                           "-r", rate, "-i", "-", "-i", src, "-map", "0:v", "-map", "1:a?",
                           "-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
                          stdin=subprocess.PIPE)
i = 0
while chunk := reader.stdout.read(w * h * 3):
    writer.stdin.write(call_frame(Image.frombytes("RGB", (w, h), chunk), i / fps).tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

## More ugc and social proof

- [Comment reply overlay **Comment reply overlay**](https://www.recharm.com/effects/comment-reply-overlay)
- [Day counter **Day counter**](https://www.recharm.com/effects/day-counter)
- [Emoji rain **Emoji rain**](https://www.recharm.com/effects/emoji-rain)
- [Floating hearts **Floating hearts**](https://www.recharm.com/effects/floating-hearts)
- [Follow button animation **Follow button animation**](https://www.recharm.com/effects/follow-button-animation)

## 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)
