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 digital rain to input.mp4 Chars: "0123456789ABCDEFHKMNTXZ:=+*<>"
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 digital rain 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 rain starts falling in from the top. |
DIM | 0.3 | How bright the clip stays under the rain (0-1). |
COLUMNS | 30 | Character columns across the frame. |
SPEED | (10, 22) | Slowest and fastest fall, in characters per second. |
TRAIL | (8, 20) | Shortest and longest trail, in characters. |
GREEN | (0, 255, 70) | Trail color. |
HEAD | (210, 255, 215) | Color of the leading character in each column. |
CHARS | "0123456789ABCDEFHKMNTXZ:=+*<>" | Characters the rain is made of, drawn mirrored. |
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 digital rain (falling code) effect to input.mp4 (540x960). From 0.5 seconds dim the clip to 30% brightness over 0.5 seconds, and from 0.5 seconds stream green characters down over it. Split the frame into 30 columns of cells 18 pixels wide and 24 pixels tall (40 rows). In each cell draw one character from "0123456789ABCDEFHKMNTXZ:=+*<>" in Menlo Bold 19 pixels tall, centered in the cell and mirrored left to right. Give each column a random fall speed between 10 and 22 cells per second, a random trail length between 8 and 20 cells, and a random delay of 0 to 12 cells before its head enters at the top; once a column's trail has left the bottom, it starts again from the top after a further 4 to 20 cells. The head cell is pale green (210, 255, 215); the cells behind it are green (0, 255, 70) with opacity (1 - distance behind the head / trail length) ^ 1.5, so the trail fades out. Each frame, 6% of the characters change to a random new one. Add a glow by adding a Gaussian blur (radius 6 pixels) of the character layer in green at 0.6 strength. Nothing changes before 0.5 seconds, and the rain keeps falling to the end. Use Python with Pillow to draw the characters and ffmpeg to put them on the video (don't rely on ffmpeg's drawtext filter), keep the audio, and save it as output.mp4.Run it without Claude
This is the exact script we tested. Save it as digital-rain.py and run:
python3 digital-rain.py input.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Digital rain: the clip dims and columns of mirrored green characters stream down it, each led by a pale glowing head.
Usage: python3 digital-rain.py input.mp4 output.mp4
Needs ffmpeg, Pillow and numpy (pip install pillow numpy).
"""
import json
import subprocess
import sys
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont, ImageOps
START = 0.5 # second the rain starts falling in from the top
FADE = 0.5 # seconds the clip takes to dim
DIM = 0.3 # how bright the clip stays under the rain (0-1)
COLUMNS = 30 # character columns across the frame
SPEED = (10, 22) # slowest and fastest fall, in characters per second
TRAIL = (8, 20) # shortest and longest trail, in characters
GREEN = (0, 255, 70) # trail color
HEAD = (210, 255, 215) # color of the leading character in each column
GLOW = 0.6 # strength of the soft glow around the characters
CHARS = "0123456789ABCDEFHKMNTXZ:=+*<>" # characters the rain is made of, drawn mirrored
SEED = 3 # change for a different arrangement
FONTS = [("/System/Library/Fonts/Menlo.ttc", 1), # macOS (index 1 is Menlo Bold)
("/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf", 0), # Linux
("C:/Windows/Fonts/consolab.ttf", 0)] # Windows
FONT_FILE, FONT_INDEX = next((f for f in FONTS if Path(f[0]).exists()), None) or sys.exit("No monospace font found: add one to FONTS")
def glyphs(cw, ch):
"""Every character as a mirrored grayscale cell (0-1), shape (len(CHARS), ch, cw)."""
font = ImageFont.truetype(FONT_FILE, round(ch * 0.8), index=FONT_INDEX)
cells = []
for c in CHARS:
img = Image.new("L", (cw, ch))
ImageDraw.Draw(img).text((cw / 2, ch / 2), c, font=font, anchor="mm", fill=255)
cells.append(np.asarray(ImageOps.mirror(img), np.float32) / 255)
return np.stack(cells)
def rain(t):
"""Brightness (0-1) and head flag of every character cell at time t, shape (rows, COLUMNS)."""
fallen = (t - START) * speed - offset # how far each column's head has fallen, in cells
head = np.where(fallen >= 0, fallen % period, -1e9)
d = head[None, :] - np.arange(rows)[:, None] # cells behind the head
on = (d >= 0) & (d < trail)
return np.where(on, np.clip(1 - d / trail, 0, 1) ** 1.5, 0), on & (d < 1)
def tile(cells):
"""Lay a (rows, COLUMNS, ch, cw) block of cells out as one frame-sized image."""
return cells.transpose(0, 2, 1, 3).reshape(rows * ch, COLUMNS * cw)[:h, :w]
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)
cw = -(-w // COLUMNS)
ch = round(cw * 1.35)
rows = -(-h // ch)
atlas = glyphs(cw, ch)
rng = np.random.default_rng(SEED)
speed = rng.uniform(*SPEED, COLUMNS)
trail = rng.uniform(*TRAIL, COLUMNS)
offset = rng.uniform(0, 12, COLUMNS) # staggers when each column enters
period = rows + trail + rng.uniform(4, 20, COLUMNS) # a column starts again after its trail leaves the bottom
chars = rng.integers(len(CHARS), size=(rows, COLUMNS))
green, pale = np.array(GREEN, np.float32), np.array(HEAD, np.float32)
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 raw := reader.stdout.read(w * h * 3):
t = i / fps
img = np.frombuffer(raw, np.uint8).reshape(h, w, 3).astype(np.float32)
if t >= START:
flip = rng.random((rows, COLUMNS)) < 0.06 # a few characters change every frame
chars[flip] = rng.integers(len(CHARS), size=flip.sum())
level, is_head = rain(t)
shape = atlas[chars]
alpha = tile(shape * level[..., None, None])[..., None]
head = tile(shape * is_head[..., None, None])[..., None]
glow = np.asarray(Image.fromarray((alpha[..., 0] * 255).astype(np.uint8)).filter(ImageFilter.GaussianBlur(cw / 3)),
np.float32)[..., None] / 255
color = green + (pale - green) * np.minimum(head / np.maximum(alpha, 1e-6), 1)
img *= 1 - (1 - DIM) * min((t - START) / FADE, 1)
img = img * (1 - alpha) + color * alpha + green * glow * GLOW
writer.stdin.write(np.clip(img, 0, 255).astype(np.uint8).tobytes())
i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
sys.exit("ffmpeg failed")





