---
title: "How to add an audio-reactive text with Claude (tested prompt and script)"
description: "A yellow \"GLOW UP\" headline above the bottle swells to half again its size on each drum hit, then shrinks smoothly back as the sound dies away. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/audio-reactive-text
---

# How to add an audio-reactive text with Claude

A yellow "GLOW UP" headline above the bottle swells to half again its size on each drum hit, then shrinks smoothly back as the sound dies away.

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 an audio-reactive text to input.mp4
   Text: GLOW UP
   ```

## 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 an audio-reactive text 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                                                                   |
| ---------------- | ----------------------------- | ------------------------------------------------------------------------------ |
| `TEXT`           | `GLOW UP`                     | The headline.                                                                  |
| `X, Y`           | `0.5, 0.12`                   | Where the text is centered (0-1 across and down the frame).                    |
| `SIZE`           | `0.55`                        | Text width when the music is quiet, as a share of the frame width.             |
| `PUMP`           | `0.5`                         | Extra size at the loudest moment (0.5 = 1.5 times as big).                     |
| `RELEASE`        | `0.8`                         | Share of the swell kept each frame as the sound fades, so the text eases back. |
| `COLOR, OUTLINE` | `(255, 214, 0), (40, 10, 60)` | Text and outline colors.                                                       |

## Without the skill

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

```
Add audio-reactive text to input.mp4 (it has a drum loop). Draw "GLOW UP" in a heavy bold font, yellow (255, 214, 0) with a dark purple (40, 10, 60) outline 6% of the font size thick, centered 50% across and 12% down the frame. Measure the loudness at each video frame: the RMS of the mono audio during that frame, divided by the loudest frame's RMS so it runs 0 to 1, and let it fall back by multiplying by 0.8 each frame instead of dropping at once (level = max(this frame's loudness, previous level x 0.8)). Scale the text about its center so its width is 55% of the frame width x (1 + 0.5 x level): about 55% wide when quiet and about 82% wide at the loudest hit, shrinking smoothly between hits. Use Python with numpy for the loudness and Pillow to draw the text (not ffmpeg's drawtext filter), ffmpeg to put it on the video, keep the audio, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 audio-reactive-text.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Audio-reactive text: a headline swells with the loudness of the music and shrinks back as it quietens.

Usage: python3 audio-reactive-text.py input.mp4 output.mp4  (the input needs an audio track)
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys
from pathlib import Path

import numpy as np
from PIL import Image, ImageDraw, ImageFont

TEXT = "GLOW UP"            # the headline
X, Y = 0.5, 0.12            # text center (0-1 across and down the frame)
SIZE = 0.55                 # text width at silence, as a share of the frame width
PUMP = 0.5                  # extra size at the loudest moment (0.5 = 1.5 times as big)
RELEASE = 0.8               # share of the swell kept each frame as the sound fades, so it eases back
COLOR = (255, 214, 0)       # text color
OUTLINE = (40, 10, 60)      # outline color
RATE = 48000                # audio sample rate the analysis uses
FONTS = ["/System/Library/Fonts/Supplemental/Arial Black.ttf",    # macOS
         "/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 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 loudness_per_frame(src, frames):
    """Loudness (0-1) at each video frame: the RMS of the audio during that frame over the loudest frame's,
    held back by RELEASE so the text shrinks smoothly instead of snapping."""
    pcm = subprocess.check_output(["ffmpeg", "-v", "error", "-i", src, "-ac", "1", "-ar", str(RATE), "-f", "f32le", "-"])
    audio = np.frombuffer(pcm, np.float32)
    step = round(RATE / fps)
    rms = np.array([np.sqrt(np.mean(audio[i * step:(i + 1) * step] ** 2)) if i * step < len(audio) else 0.0
                    for i in range(frames)])
    rms /= max(rms.max(), 1e-9)
    level, prev = [], 0.0
    for r in rms:
        prev = max(r, prev * RELEASE)
        level.append(prev)
    return level

def headline():
    """The text at silence size, drawn large so scaling it up stays sharp."""
    font = ImageFont.truetype(FONT_FILE, 200)
    stroke = 12
    x0, y0, x1, y1 = font.getbbox(TEXT, stroke_width=stroke)
    img = Image.new("RGBA", (x1 - x0, y1 - y0))
    ImageDraw.Draw(img).text((-x0, -y0), TEXT, font=font, fill=COLOR, stroke_width=stroke, stroke_fill=OUTLINE)
    return img

def overlay(i):
    img = Image.new("RGBA", (w, h))
    width = round(w * SIZE * (1 + PUMP * level[i]))
    t = text.resize((width, round(text.height * width / text.width)), Image.LANCZOS)
    img.alpha_composite(t, (round(w * X - t.width / 2), round(h * Y - t.height / 2)))
    return img

def render(src, out):
    """Draw overlay(i) 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(frames):
        ff.stdin.write(overlay(i).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")

src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
frames = math.ceil(duration * fps)
level = loudness_per_frame(src, frames)
text = headline()
render(src, out)
```

## More music-synced visuals

- [AI voice orb **AI voice orb**](https://www.recharm.com/effects/ai-voice-orb)
- [Audio-reactive blob **Audio-reactive blob**](https://www.recharm.com/effects/audio-reactive-blob)
- [Audio-reactive particles **Audio-reactive particles**](https://www.recharm.com/effects/audio-reactive-particles)
- [Audio waveform **Audio waveform**](https://www.recharm.com/effects/audio-waveform)
- [Audiogram **Audiogram**](https://www.recharm.com/effects/audiogram)

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