---
title: "How to add a text slam with Claude (tested prompt and script)"
description: "Two giant yellow words fall onto the screen one after the other, and each lands with a white flash and a jolt of the frame. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/text-slam
---

# How to add a text slam with Claude

Two giant yellow words fall onto the screen one after the other, and each lands with a white flash and a jolt of the frame.

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 text slam to input.mp4
   Words: [("GLASS", 0.4, 0.14), ("SKIN", 0.9, 0.265)]
   ```

## 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 text slam 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                                                                    |
| ------------- | ---------------------------------------------- | ------------------------------------------------------------------------------- |
| `WORDS`       | `[("GLASS", 0.4, 0.14), ("SKIN", 0.9, 0.265)]` | Each word, the second it lands and where its center sits (0 = top, 1 = bottom). |
| `DROP`        | `0.1`                                          | Seconds a word takes to fall into place.                                        |
| `START_SCALE` | `2.6`                                          | How big a word starts its drop, relative to its final size.                     |
| `WIDTH`       | `0.8`                                          | A word's final width as a share of the frame width.                             |
| `COLOR`       | `(255, 214, 0)`                                | Text color.                                                                     |
| `OUTLINE`     | `0.025`                                        | Black outline thickness as a share of the frame width.                          |
| `FLASH`       | `0.9`                                          | White flash strength on impact (1 = solid white).                               |
| `FLASH_LEN`   | `0.2`                                          | Seconds the flash takes to fade.                                                |
| `SHAKE`       | `14`                                           | How far the frame jumps on impact, in pixels.                                   |
| `SHAKE_LEN`   | `0.4`                                          | Seconds until the shake settles.                                                |
| `PUNCH`       | `0.08`                                         | Extra zoom on impact, easing back to none.                                      |

## Without the skill

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

```
Add a text slam to input.mp4: two huge words in yellow (255, 214, 0) with a black outline 2.5% of the frame width thick, each 80% of the frame width wide and centered horizontally. "GLASS" lands at 0.4 seconds centered 14% of the way down, then "SKIN" lands at 0.9 seconds centered 26.5% of the way down; both stay to the end. Each word falls in over the 0.1 seconds before it lands, shrinking from 2.6 times its size while fading in and speeding up as it drops. On each landing, flash the whole frame toward white (90% white, fading to nothing over 0.2 seconds) and shake the frame, words included: jolt it about 14 pixels in changing directions with an extra 8% zoom that both die down to still over 0.4 seconds, never showing an edge. Nothing changes before 0.3 seconds. Draw the text with Python and Pillow (don't rely on ffmpeg's drawtext filter), keep the size and audio, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `text-slam.py` and run:

```
python3 text-slam.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Text slam: huge words drop onto the screen one at a time, each landing with a white flash and a screen shake.

Usage: python3 text-slam.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, ImageFont

WORDS = [("GLASS", 0.4, 0.14),   # (word, second it lands, where its center sits: 0 = top, 1 = bottom)
         ("SKIN", 0.9, 0.265)]
DROP = 0.1                # seconds a word takes to fall from big to its place
START_SCALE = 2.6         # how big a word starts its drop, relative to its final size
WIDTH = 0.8               # a word's final width as a share of the frame width
COLOR = (255, 214, 0)     # text color
OUTLINE = 0.025           # black outline thickness as a share of the frame width
FLASH = 0.9               # white flash strength on impact (1 = solid white)
FLASH_LEN = 0.2           # seconds the flash takes to fade
SHAKE = 14                # how far the frame jumps on impact, in pixels
SHAKE_LEN = 0.4           # seconds until the shake settles
PUNCH = 0.08              # extra zoom on impact, easing back to none (also hides the shake's edges)
FONTS = ["/System/Library/Fonts/Supplemental/Arial Black.ttf",    # macOS
         "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",  # Linux
         "C:/Windows/Fonts/ariblk.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 word_image(word):
    """One word in color with a black outline, sized to fill WIDTH of the frame."""
    stroke = round(w * OUTLINE)
    probe_font = ImageFont.truetype(FONT_FILE, 100)
    x0, _, x1, _ = probe_font.getbbox(word, stroke_width=stroke)
    font = ImageFont.truetype(FONT_FILE, round(100 * w * WIDTH / (x1 - x0)))
    x0, y0, x1, y1 = font.getbbox(word, stroke_width=stroke)
    img = Image.new("RGBA", (x1 - x0, y1 - y0))
    ImageDraw.Draw(img).text((-x0, -y0), word, font=font, fill=COLOR, stroke_width=stroke, stroke_fill="black")
    return img

def add_text(img, t):
    for (word, land, y), pic in zip(WORDS, pictures):
        if t < land - DROP:
            continue
        p = min((t - (land - DROP)) / DROP, 1)
        scale = START_SCALE + (1 - START_SCALE) * p * p   # speeds up as it falls, like a dropped weight
        pic_t = pic.resize((round(pic.width * scale), round(pic.height * scale)), Image.LANCZOS)
        if p < 1:
            pic_t.putalpha(pic_t.getchannel("A").point(lambda a: a * p))
        img.alpha_composite(pic_t, (round(w / 2 - pic_t.width / 2), round(h * y - pic_t.height / 2)))
    return img

def last_hit(t):
    """Seconds since the latest word landed, or None before the first."""
    since = [t - land for _, land, _ in WORDS if t >= land]
    return min(since) if since else None

def shake(img, t):
    s = last_hit(t)
    if s is None or s >= SHAKE_LEN:
        return img
    k = 1 - s / SHAKE_LEN                                 # 1 on impact, 0 once settled
    zoom = 1 + PUNCH * k
    dx, dy = SHAKE * k * math.sin(t * 83), SHAKE * 0.8 * k * math.cos(t * 61)
    cw, ch = w / zoom, h / zoom
    x0, y0 = (w - cw) / 2 + dx / zoom, (h - ch) / 2 + dy / zoom
    return img.resize((w, h), Image.BICUBIC, box=(x0, y0, x0 + cw, y0 + ch))

def flash(img, t):
    s = last_hit(t)
    if s is None or s >= FLASH_LEN:
        return img
    return Image.blend(img, Image.new("RGB", img.size, "white"), FLASH * (1 - s / FLASH_LEN))

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):
    """Read every frame of src, add the words, shake and flash, and save to out with 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)
    for i in range(math.ceil(duration * fps)):
        raw = reader.stdout.read(w * h * 3)
        if len(raw) < w * h * 3:
            break
        t = i / fps
        img = add_text(Image.frombuffer("RGB", (w, h), raw).convert("RGBA"), t).convert("RGB")
        writer.stdin.write(flash(shake(img, t), t).tobytes())
    writer.stdin.close()
    reader.wait()
    if writer.wait():
        sys.exit("ffmpeg failed")

src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
pictures = [word_image(word) for word, _, _ in WORDS]
render(src, out)
```

## More hook openers

- [Blur-to-focus reveal **Blur-to-focus reveal**](https://www.recharm.com/effects/blur-to-focus-reveal)
- [Breaking news banner **Breaking news banner**](https://www.recharm.com/effects/breaking-news-banner)
- [Camera shake **Camera shake**](https://www.recharm.com/effects/camera-shake)
- [Color reveal **Color reveal**](https://www.recharm.com/effects/color-reveal)
- [Countdown intro **Countdown intro**](https://www.recharm.com/effects/countdown-intro)

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