Get this effect
Switch to Code in your Claude Desktop App
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/skillsAsk for the effect
Add a camcorder viewfinder to input.mp4 Date: JUN 14 1998
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 camcorder viewfinder 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 |
|---|---|---|
START | 0.5 | Second the camcorder starts recording and the viewfinder appears. |
COLOR | (255, 255, 255) | Bracket, icon and text color. |
REC_COLOR | (230, 30, 45) | Recording dot color. |
INSET | 36 | Corner brackets' distance from the frame edge, in pixels. |
BATTERY | 3 | Battery bars lit, out of 4. |
DATE | JUN 14 1998 | Date stamp, bottom right. |
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 camcorder viewfinder overlay to input.mp4, all lines and text in white (255, 255, 255) with a soft dark shadow (black, 3-pixel blur) behind them. Before 0.5 seconds the video is untouched; at 0.5s the whole overlay appears at once. Four L-shaped corner brackets, arms 60 pixels long and 4 pixels thick, corners 36 pixels in from each frame edge, and a small 28-pixel cross, 2 pixels thick, centered on the frame. Top left, 48 pixels below the top bracket's line and 34 pixels in from the left one: a red (230, 30, 45) dot with a radius of 3% of the frame width, lit for the first half of every second from 0.5s (on 0.5-1.0s, off 1.0-1.5s, and so on), with "REC" in bold white 12 pixels to its right (it stays when the dot blinks off). Top right at the same height, its right edge 34 pixels inside the right bracket: a battery outline 11% of the frame width wide and 5% tall, 3 pixels thick, with a small nub on the right and 3 of 4 bars filled. Bottom row, 48 pixels above the bottom bracket's line and 24 pixels inside the brackets: a running mm:ss:ff counter that starts at 00:00:00 at 0.5s on the left and "JUN 14 1998" right-aligned on the right. Text is bold, 5% of the frame width tall. Leave the middle of the footage unchanged. Draw it with Python and Pillow (don't use ffmpeg's drawtext filter), composite it with ffmpeg, keep the audio, and save it as output.mp4 at 30fps.Run it without Claude
This is the exact script we tested. Save it as camcorder-viewfinder.py and run:
python3 camcorder-viewfinder.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Camcorder viewfinder: a blinking red REC dot, battery icon, corner brackets, timecode and date stamp frame the video.
Usage: python3 camcorder-viewfinder.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
START = 0.5 # second the camcorder starts recording and the viewfinder appears
COLOR = (255, 255, 255) # bracket, icon and text color
REC_COLOR = (230, 30, 45) # recording dot color
INSET = 36 # corner brackets' distance from the frame edge, in pixels
ARM = 60 # length of each bracket arm, in pixels
LINE = 4 # bracket thickness in pixels
BATTERY = 3 # battery bars lit, out of 4
DATE = "JUN 14 1998" # date stamp, bottom right
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 brackets(draw):
"""L-shaped brackets in the four corners and a small cross in the middle."""
for sx, sy in [(1, 1), (-1, 1), (1, -1), (-1, -1)]:
x = INSET if sx > 0 else w - 1 - INSET
y = INSET if sy > 0 else h - 1 - INSET
draw.line([(x, y + sy * ARM), (x, y), (x + sx * ARM, y)], fill=COLOR, width=LINE, joint="curve")
cx, cy = w / 2, h / 2
draw.line([(cx - 14, cy), (cx + 14, cy)], fill=COLOR, width=2)
draw.line([(cx, cy - 14), (cx, cy + 14)], fill=COLOR, width=2)
def rec(draw, t):
"""Red dot (lit for the first half of every second) and "REC", top left inside the brackets."""
r, x, y = round(w * 0.03), INSET + 34, INSET + 48
if (t - START) % 1 < 0.5:
draw.ellipse([x - r, y - r, x + r, y + r], fill=REC_COLOR)
draw.text((x + r + 12, y), "REC", font=font, fill=COLOR, anchor="lm")
def battery(draw):
"""Battery outline with BATTERY of 4 bars lit, top right inside the brackets."""
bw, bh = round(w * 0.11), round(w * 0.05)
x1, y = w - INSET - 34, INSET + 48
x0 = x1 - bw
draw.rectangle([x0, y - bh / 2, x1, y + bh / 2], outline=COLOR, width=3)
draw.rectangle([x1 + 1, y - bh / 5, x1 + 6, y + bh / 5], fill=COLOR)
bar = (bw - 10 - 3 * 3) / 4
for i in range(BATTERY):
bx = x0 + 5 + i * (bar + 3)
draw.rectangle([bx, y - bh / 2 + 5, bx + bar, y + bh / 2 - 5], fill=COLOR)
def stamps(draw, t):
"""Running recording time bottom left, the date stamp bottom right."""
frames = round((t - START) * 30)
y = h - INSET - 48
draw.text((INSET + 24, y), f"{frames // 1800:02d}:{frames // 30 % 60:02d}:{frames % 30:02d}", font=font,
fill=COLOR, anchor="lm")
draw.text((w - INSET - 24, y), DATE, font=font, fill=COLOR, anchor="rm")
def overlay(t):
img = Image.new("RGBA", (w, h))
if t < START:
return img
draw = ImageDraw.Draw(img)
brackets(draw)
rec(draw, t)
battery(draw)
stamps(draw, t)
shadow = Image.new("RGBA", (w, h), (0, 0, 0))
shadow.putalpha(img.getchannel("A").filter(ImageFilter.GaussianBlur(3)).point(lambda a: min(160, a * 2)))
shadow.alpha_composite(img)
return shadow
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 * 0.05))
render(src, out)





