How to add a neon sign text with Claude

"GLOW UP" in hot pink glass tubes on a dark panel stutters on and off like a buzzing neon sign, then glows steadily above the bottle.

Get this effect

  1. Switch to Code in your Claude Desktop App

  2. 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/skills
  3. Ask for the effect

    Add a neon sign 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 a neon sign 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.

SettingDefaultWhat it does
TEXTGLOW UPThe sign's words.
START0.6Second the sign first sparks.
FLICKER[(0.7, 0.8), (0.87, 1.0), (1.05, 1.12), (3.6, 3.67)]From and to seconds the tubes drop out.
Y0.14Text center, 0-1 down the frame.
SIZE0.16Font size as a share of the frame width.
TUBE(255, 40, 160)Lit tube and glow color.
BOARD(18, 8, 24, 215)Backing panel color and opacity.

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 neon sign to the top of input.mp4: the words "GLOW UP" drawn as hollow tube outlines (each letter's outline, 10 pixels thick) in a bold rounded font at size 86 pixels (16% of the frame width), centered horizontally 14% of the way down. Each tube is hot pink (255, 40, 160) with a 4-pixel white-hot center line (255, 235, 245) along it and a pink glow around it (the tube blurred by 6 pixels with its opacity boosted 1.6x, and by 22 pixels with its opacity boosted 2.2x, both laid underneath). Behind the letters put a dark panel (18, 8, 24) at 84% opacity (alpha 215) with rounded corners, padded 27 pixels (5% of the frame width) around the letters. Nothing shows before 0.6 seconds. From 0.6 seconds the panel is there and the sign flickers on: lit until 0.7s, off 0.7-0.8s, lit, off 0.87-1.0s, lit, off 1.05-1.12s, then lit for good apart from one more dropout at 3.6-3.67s. When off, the tubes stay visible as dark unlit glass (95, 45, 80) with no glow. Use Python with Pillow to draw it and ffmpeg to put it 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 neon-sign-text.py and run:

python3 neon-sign-text.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Neon sign text: hot pink tube letters stutter on like a neon sign, then glow steadily.

Usage: python3 neon-sign-text.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, ImageChops, ImageDraw, ImageFilter, ImageFont

TEXT = "GLOW UP"                    # the sign's words
START = 0.6                         # second the sign first sparks
FLICKER = [(0.7, 0.8), (0.87, 1.0), (1.05, 1.12), (3.6, 3.67)]  # (from, to) seconds the tubes drop out
Y = 0.14                            # text center, 0-1 down the frame
SIZE = 0.16                         # font size as a share of the frame width
TUBE = (255, 40, 160)               # lit tube and glow color
CORE = (255, 235, 245)              # white-hot center line of each tube
UNLIT = (95, 45, 80)                # dark glass tube when switched off
BOARD = (18, 8, 24, 215)            # dark backing panel behind the tubes (RGBA)
FONTS = ["/System/Library/Fonts/Supplemental/Arial Rounded 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 tube(width):
    """A mask of each letter's outline, `width` pixels thick, so the letters read as bent glass tubes."""
    font = ImageFont.truetype(FONT_FILE, round(w * SIZE))
    r = width // 2
    outer = Image.new("L", (w, h))
    ImageDraw.Draw(outer).text((w / 2, h * Y), TEXT, font=font, anchor="mm", fill=255, stroke_width=r, stroke_fill=255)
    inner = Image.new("L", (w, h))
    ImageDraw.Draw(inner).text((w / 2, h * Y), TEXT, font=font, anchor="mm", fill=255)
    return ImageChops.subtract(outer, inner.filter(ImageFilter.MinFilter(2 * r + 1)))


def layer(color, mask, strength=1.0):
    img = Image.new("RGBA", (w, h), color)
    img.putalpha(mask.point(lambda v: min(255, round(v * strength))))
    return img


def sign():
    """The lit sign (wide glow, tight glow, tube, white core) and the switched-off sign, both on the panel."""
    body, core = tube(10), tube(4)
    x0, y0, x1, y1 = body.getbbox()
    pad = round(w * 0.05)
    board = Image.new("RGBA", (w, h))
    ImageDraw.Draw(board).rounded_rectangle([x0 - pad, y0 - pad, x1 + pad, y1 + pad], radius=pad, fill=BOARD)
    lit, unlit = board.copy(), board.copy()
    lit.alpha_composite(layer(TUBE, body.filter(ImageFilter.GaussianBlur(22)), 2.2))
    lit.alpha_composite(layer(TUBE, body.filter(ImageFilter.GaussianBlur(6)), 1.6))
    lit.alpha_composite(layer(TUBE, body))
    lit.alpha_composite(layer(CORE, core))
    unlit.alpha_composite(layer(UNLIT, body))
    return lit, unlit


def overlay(t):
    if t < START:
        return blank
    return unlit if any(a <= t < b for a, b in FLICKER) else lit


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):
    """Draw overlay(t) 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(math.ceil(duration * fps)):
        ff.stdin.write(overlay(i / fps).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")


src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
lit, unlit = sign()
blank = Image.new("RGBA", (w, h))
render(src, out)

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