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 background blur to input.mp4 Background blur (Gaussian sigma) as a share of the frame width: 16 px on a 540-wide video: 0.03 Second the background starts to go soft: 0.5
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 background blur 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 |
|---|---|---|
BLUR | 0.03 | Background blur (Gaussian sigma) as a share of the frame width: 16 px on a 540-wide video. |
START | 0.5 | Second the background starts to go soft. |
EASE | 0.5 | Seconds it takes to reach full blur. |
EDGE | 1.5 | Softness of the cut-out's edge in pixels. |
MASK_EVERY | 2 | Cut the product out on every Nth frame and reuse it in between. |
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
Blur the background of input.mp4 (540x960, the camera circles a purple serum bottle standing on an acrylic block) like a portrait-mode lens, keeping the bottle sharp. Leave the video untouched before 0.5 seconds. From 0.5 seconds, cut the bottle (with its dropper cap) out of every frame with rembg (u2net model), or every second frame and reuse the cut-out in between, and soften the cut-out's edge with a 1.5 px Gaussian blur. Paint the bottle out of the frame first by filling its area from the surrounding backdrop, so the blur doesn't spread a purple halo, then Gaussian-blur that plate; the blur's sigma eases from 0 to 16 px (3% of the frame width) between 0.5 and 1.0 seconds with a smoothstep curve and stays at 16 px to the end. Everything that isn't the bottle goes soft, the acrylic block included. Put the untouched bottle back on top through its cut-out. Use Python with OpenCV and numpy for the blur and ffmpeg to read and write the frames (no text is drawn; if you add any, draw it with Pillow, not ffmpeg's drawtext), keep the size, 30fps and the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as background-blur.py and run:
python3 background-blur.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Background blur: everything behind the product melts into a soft, creamy blur while the product itself stays pin sharp.
Usage: python3 background-blur.py input.mp4 output.mp4
Needs ffmpeg, OpenCV, numpy, Pillow and rembg to cut the product out of every frame
(pip install opencv-python-headless numpy pillow "rembg[cpu]"; rembg downloads its u2net model on first use).
"""
import json
import subprocess
import sys
import cv2
import numpy as np
from PIL import Image
from rembg import new_session
BLUR = 0.03 # background blur (Gaussian sigma) as a share of the frame width: 16 px on a 540-wide video
START = 0.5 # second the background starts to go soft
EASE = 0.5 # seconds it takes to reach full blur
EDGE = 1.5 # softness of the cut-out's edge in pixels
MASK_EVERY = 2 # cut the product out on every Nth frame and reuse it in between (the camera moves slowly)
def blur(img, sigma):
return cv2.GaussianBlur(img, (0, 0), sigma, borderType=cv2.BORDER_REFLECT) if sigma > 0.3 else img
def amount(t):
"""0 before START, easing to 1 over EASE seconds."""
p = min(max((t - START) / EASE, 0), 1)
return p * p * (3 - 2 * p)
def cutout(rgb):
"""The product's matte, 0-1, with a slightly softened edge."""
return blur(np.asarray(session.predict(Image.fromarray(rgb))[0], np.float32) / 255, EDGE)
def portrait(rgb, mask, t):
"""Blur the backdrop with the product painted out of it, then put the sharp product back on top."""
img = rgb.astype(np.float32)
# fill the product's hole from the backdrop around it (worked out at quarter size, it's only a rough fill),
# so the blur doesn't smear a halo of the product's colors
keep = 1 - cv2.dilate(mask, np.ones((9, 9), np.uint8))
small = cv2.resize(np.dstack([img * keep[:, :, None], keep]), (w // 4, h // 4), interpolation=cv2.INTER_AREA)
small = blur(small, w * 0.0125)
fill = cv2.resize(small[:, :, :3] / np.maximum(small[:, :, 3:], 1e-3), (w, h))
backdrop = blur(img * keep[:, :, None] + fill * (1 - keep[:, :, None]), BLUR * w * amount(t))
m = mask[:, :, None]
return (img * m + backdrop * (1 - m)).clip(0, 255).round().astype(np.uint8)
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)
def render(src, out):
"""Read src frame by frame, blur each one's background and save to out, keeping the audio."""
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, mask = 0, None
while raw := reader.stdout.read(w * h * 3):
rgb = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
t = i / fps
if t >= START and (mask is None or i % MASK_EVERY == 0):
mask = cutout(rgb)
writer.stdin.write((rgb if t < START else portrait(rgb, mask, t)).tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")
src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
session = new_session("u2net")
render(src, out)





