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 shatter to input.mp4 Second the glass is struck and the frame freezes: 1.5 Second the shards start to fall: 2.0
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 shatter 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 |
|---|---|---|
IMPACT | 1.5 | Second the glass is struck and the frame freezes. |
FALL | 2.0 | Second the shards start to fall. |
REVEAL, FADE | 2.6, 0.6 | When the video starts fading back up behind the shards, and how long that takes. |
X, Y | 0.5, 0.45 | Impact point, as a share of the frame across and down. |
RINGS, PER_RING | 6, 9 | How many shards: rings around the impact point, and shards in each ring. |
CRACK_COLOR | (255, 255, 255) | Color of the crack lines. |
SEED | 3 | Change it for a different crack pattern. |
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 glass shatter effect to input.mp4. Play it normally until 1.5 seconds, then freeze that frame and crack it: split the frame into about 55 Voronoi shards from seeds placed in 6 jittered rings around an impact point 50% across and 45% down (small shards near the point, bigger toward the edges), and draw the shard borders as pure white (255, 255, 255) lines 3 pixels wide, growing outward from the impact point so they cover the whole frame by 1.8 seconds. At 2.0 seconds the shards fall away: each one drifts outward from the impact point at 80 to 220 pixels per second, spins up to 120 degrees per second and falls under gravity of 2600 pixels per second squared, with shards farther from the point starting up to 0.35 seconds later. Behind them is black until 2.6 seconds, then the live video (not the frozen frame) fades up over 0.6 seconds and plays normally to the end. Keep the size, 30fps, the 6-second length and the audio. Use Python with numpy and Pillow for the shards and ffmpeg to read and write the video (don't rely on ffmpeg's drawtext filter), and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as shatter.py and run:
python3 shatter.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Shatter: the frame freezes, white cracks spread from a point like struck glass, then the shards fall away into black and the video fades back up.
Usage: python3 shatter.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import subprocess
import sys
import numpy as np
from PIL import Image
IMPACT = 1.5 # second the glass is struck (the frame freezes here)
CRACK = 0.3 # seconds the cracks take to spread across the frame
FALL = 2.0 # second the shards start to fall
REVEAL = 2.6 # second the video starts fading back up behind the shards
FADE = 0.6 # seconds that fade takes
X, Y = 0.5, 0.45 # impact point (0-1 across and down the frame)
RINGS, PER_RING = 6, 9 # shards: rings around the impact point, and shards per ring
CRACK_WIDTH = 3 # crack line width in pixels
CRACK_COLOR = (255, 255, 255)
GRAVITY = 2600 # pixels per second squared
FPS = 30 # output frame rate
SEED = 3 # change for a different crack pattern
def probe(path):
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=width,height:format=duration", "-of", "json", path]))
return info["streams"][0]["width"], info["streams"][0]["height"], float(info["format"]["duration"])
def frames(path):
ff = subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf", f"fps={FPS}",
"-f", "rawvideo", "-pix_fmt", "rgb24", "-"], stdout=subprocess.PIPE)
while True:
data = ff.stdout.read(w * h * 3)
if len(data) < w * h * 3:
return
yield np.frombuffer(data, np.uint8).reshape(h, w, 3)
def shard_map():
"""Label every pixel with its shard: a Voronoi pattern from seeds in jittered rings around the impact,
small near the point and larger toward the edges, like a struck pane."""
seeds = [(cx, cy)]
reach = np.hypot(w, h)
for ring in range(1, RINGS + 1):
radius = reach * (ring / RINGS) ** 1.6 * 0.75
for k in range(PER_RING):
a = (k + rng.uniform(-0.35, 0.35) + ring * 0.5) / PER_RING * 2 * np.pi
r = radius * rng.uniform(0.8, 1.15)
seeds.append((cx + r * np.cos(a), cy + r * np.sin(a)))
best, labels = np.full((h, w), np.inf), np.zeros((h, w), int)
for i, (sx, sy) in enumerate(seeds):
d = np.hypot(xs - sx, ys - sy)
labels[d < best] = i
best = np.minimum(best, d)
return labels
def crack_lines():
"""Pixels on a shard border, thickened to CRACK_WIDTH."""
edge = np.zeros((h, w), bool)
edge[:, 1:] |= labels[:, 1:] != labels[:, :-1]
edge[1:, :] |= labels[1:, :] != labels[:-1, :]
thick = edge.copy()
for d in range(1, CRACK_WIDTH):
thick[d:, :] |= edge[:-d, :]
thick[:, d:] |= edge[:, :-d]
return thick
def cracked(still, t):
"""The frozen frame with the cracks drawn out to a radius that grows from the impact point."""
radius = np.hypot(w, h) * min((t - IMPACT) / CRACK, 1)
img = still.copy()
img[lines & (from_impact <= radius)] = CRACK_COLOR
return img
def cut_shards(glass):
"""Each shard as an RGBA cutout with its center and its own drift, spin and start time."""
shards = []
for i in range(labels.max() + 1):
ys, xs = np.nonzero(labels == i)
if not len(xs):
continue
x0, x1, y0, y1 = xs.min(), xs.max() + 1, ys.min(), ys.max() + 1
rgba = np.dstack([glass[y0:y1, x0:x1], (labels[y0:y1, x0:x1] == i) * np.uint8(255)])
mx, my = xs.mean(), ys.mean()
away = np.array([mx - cx, my - cy])
away = away / max(np.hypot(*away), 1)
shards.append({"img": Image.fromarray(rgba), "center": (mx, my),
"push": away * rng.uniform(80, 220), "spin": rng.uniform(-120, 120),
"start": FALL + 0.35 * np.hypot(mx - cx, my - cy) / np.hypot(w, h) + rng.uniform(0, 0.08)})
return shards
def falling(live, t):
"""Shards still in place or falling, over black that fades up to the live video."""
level = np.clip((t - REVEAL) / FADE, 0, 1)
img = Image.fromarray((live * level).astype(np.uint8))
for s in shards:
dt = max(t - s["start"], 0)
x = s["center"][0] + s["push"][0] * dt
y = s["center"][1] + s["push"][1] * dt + 0.5 * GRAVITY * dt * dt
piece = s["img"].rotate(s["spin"] * dt, resample=Image.BICUBIC, expand=True) if dt else s["img"]
left, top = round(x - piece.width / 2), round(y - piece.height / 2)
if top < h and top + piece.height > 0 and left < w and left + piece.width > 0:
img.paste(piece, (left, top), piece)
return np.asarray(img)
src, out = sys.argv[1:3]
w, h, length = probe(src)
rng = np.random.default_rng(SEED)
cx, cy = w * X, h * Y
ys, xs = np.mgrid[0:h, 0:w]
labels = shard_map()
lines = crack_lines()
from_impact = np.hypot(xs - cx, ys - cy)
enc = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
"-r", str(FPS), "-i", "-", "-i", src, "-map", "0:v", "-map", "1:a?",
"-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p", "-c:a", "copy", out],
stdin=subprocess.PIPE)
still, shards = None, None
for i, frame in enumerate(frames(src)):
t = i / FPS
if t < IMPACT:
result = frame
elif t < FALL:
still = frame if still is None else still
result = cracked(still, t)
else:
shards = shards or cut_shards(cracked(still, FALL))
result = falling(frame, t)
enc.stdin.write(np.ascontiguousarray(result).tobytes())
enc.stdin.close()
if enc.wait():
sys.exit("ffmpeg failed")





