---
title: "How to add a sci-fi HUD overlay with Claude (tested prompt and script)"
description: "At half a second mint-green corner brackets slide in, a ringed crosshair with two spinning arcs closes onto the center, and \"TARGET LOCKED\", a running timecode and range readouts appear, as if seen through a visor. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/hud-overlay
---

# How to add a sci-fi HUD overlay with Claude

At half a second mint-green corner brackets slide in, a ringed crosshair with two spinning arcs closes onto the center, and "TARGET LOCKED", a running timecode and range readouts appear, as if seen through a visor.

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 sci-fi HUD overlay to input.mp4
   Label: TARGET LOCKED
   ```

## 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 sci-fi hud overlay 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 HUD starts booting up.                          |
| `BOOT`    | `0.5`            | Seconds the brackets and reticle take to slide into place. |
| `COLOR`   | `(80, 255, 230)` | Line and text color.                                       |
| `RETICLE` | `0.20`           | Reticle ring radius as a share of the frame width.         |
| `SPIN`    | `90`             | Degrees per second the reticle's outer arcs turn.          |
| `LABEL`   | `TARGET LOCKED`  | Text under the reticle.                                    |

## Without the skill

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

```
Add a sci-fi HUD overlay to input.mp4, all lines and text in (80, 255, 230) with a soft 4-pixel dark (0, 20, 30) shadow behind them. Before 0.5 seconds the video is untouched. From 0.5s, over 0.5 seconds with an ease-out: four L-shaped corner brackets (arms 70 pixels long, 3 pixels thick, corners 28 pixels in from each frame edge) slide in from 40 pixels further out, and a targeting reticle centered on the frame shrinks from 1.6x into place. The reticle is a 2-pixel ring with a radius of 20% of the frame width, four crosshair ticks from 0.75 to 1.2 times the radius on each side, a small 16-pixel cross in the middle, and two 60-degree arcs 5 pixels thick at 1.12 times the radius, opposite each other, turning clockwise at 90 degrees per second. Once it's in place (1.0s) show readouts in a monospaced font: "TARGET LOCKED" at 5.5% of the frame width tall, centered 34 pixels below the bottom crosshair tick, in a 2-pixel outlined box with a dark (0, 20, 30) fill at 67% opacity; a running "REC mm:ss:ff" timecode in the top-left corner and "SYS ONLINE" in the top-right corner, 14 pixels inside the brackets; and "RANGE 2.30 M" over "AZ 134.0" in the bottom-left corner, both numbers changing slowly. Text other than the label is 4% of the frame width tall. Draw it with Python and Pillow (don't use ffmpeg's drawtext filter), composite it with ffmpeg, keep the audio, and save it as output.mp4 at 30fps.
```

## Run it without Claude

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

```
python3 hud-overlay.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""HUD overlay: thin corner brackets, a spinning targeting reticle and live readouts boot up over the video.

Usage: python3 hud-overlay.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, ImageFilter, ImageFont

START = 0.5                 # second the HUD starts booting up
BOOT = 0.5                  # seconds the brackets and reticle take to slide and shrink into place
COLOR = (80, 255, 230)      # HUD line and text color
LINE = 3                    # line thickness in pixels
INSET = 28                  # corner brackets' distance from the frame edge, in pixels
ARM = 70                    # length of each bracket arm, in pixels
RETICLE = 0.20              # reticle ring radius as a share of the frame width, centered on the frame
SPIN = 90                   # degrees per second the reticle's outer arcs turn
LABEL = "TARGET LOCKED"     # text under the reticle
FONTS = ["/System/Library/Fonts/Menlo.ttc",                         # macOS
         "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",     # Linux
         "C:/Windows/Fonts/consola.ttf"]                            # Windows
FONT_FILE = next((f for f in FONTS if Path(f).exists()), None) or sys.exit("No monospaced font found: add one to FONTS")

def brackets(draw, p):
    """L-shaped brackets in the four corners, sliding in from 40px further out as p goes 0 -> 1."""
    off = round(40 * (1 - p))
    for sx, sy in [(1, 1), (-1, 1), (1, -1), (-1, -1)]:
        x = (INSET - off) if sx > 0 else w - 1 - INSET + off
        y = (INSET - off) if sy > 0 else h - 1 - INSET + off
        draw.line([(x, y + sy * ARM), (x, y), (x + sx * ARM, y)], fill=COLOR, width=LINE, joint="curve")

def reticle(draw, t, p):
    """A ring with crosshair ticks and two spinning arcs, shrinking from 1.6x its size as p goes 0 -> 1."""
    cx, cy = w / 2, h / 2
    r = w * RETICLE * (1.6 - 0.6 * p)
    draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=COLOR, width=2)
    for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
        draw.line([(cx + dx * r * 0.75, cy + dy * r * 0.75), (cx + dx * r * 1.2, cy + dy * r * 1.2)], fill=COLOR, width=LINE)
    draw.line([(cx - 8, cy), (cx + 8, cy)], fill=COLOR, width=2)
    draw.line([(cx, cy - 8), (cx, cy + 8)], fill=COLOR, width=2)
    R = r * 1.12
    turn = SPIN * (t - START)
    for a in (turn, turn + 180):
        draw.arc([cx - R, cy - R, cx + R, cy + R], a, a + 60, fill=COLOR, width=LINE + 2)

def readouts(draw, t):
    """Timecode top left, status top right, the label under the reticle and changing numbers bottom left."""
    frames = round(t * 30)
    draw.text((INSET + 14, INSET + 14), f"REC {frames // 30 // 60:02d}:{frames // 30 % 60:02d}:{frames % 30:02d}",
              font=small, fill=COLOR)
    draw.text((w - INSET - 14, INSET + 14), "SYS ONLINE", font=small, fill=COLOR, anchor="ra")
    y = h / 2 + w * RETICLE * 1.2 + 34
    box = draw.textbbox((w / 2, y), LABEL, font=big, anchor="mm")
    draw.rectangle([box[0] - 12, box[1] - 8, box[2] + 12, box[3] + 8], fill=(0, 20, 30, 170), outline=COLOR, width=2)
    draw.text((w / 2, y), LABEL, font=big, fill=COLOR, anchor="mm")
    draw.text((INSET + 14, h - INSET - 14), f"RANGE {2.4 - 0.1 * t:.2f} M\nAZ {134 + 7 * math.sin(t * 2):6.1f}",
              font=small, fill=COLOR, anchor="ld", spacing=6)

def overlay(t):
    img = Image.new("RGBA", (w, h))
    if t < START:
        return img
    p = ease_out(min((t - START) / BOOT, 1))
    draw = ImageDraw.Draw(img)
    brackets(draw, p)
    reticle(draw, t, p)
    if p >= 1:
        readouts(draw, t)
    shadow = Image.new("RGBA", (w, h), (0, 20, 30))
    shadow.putalpha(img.getchannel("A").filter(ImageFilter.GaussianBlur(4)).point(lambda a: min(255, a * 2)))
    shadow.alpha_composite(img)
    return shadow

def ease_out(p):
    return 1 - (1 - p) ** 3

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)
small = ImageFont.truetype(FONT_FILE, round(w * 0.04))
big = ImageFont.truetype(FONT_FILE, round(w * 0.055))
render(src, out)
```

## More overlays and light

- [Anamorphic streak **Anamorphic streak**](https://www.recharm.com/effects/anamorphic-streak)
- [Aurora **Aurora**](https://www.recharm.com/effects/aurora)
- [Blood splatter **Blood splatter**](https://www.recharm.com/effects/blood-splatter)
- [Bokeh lights **Bokeh lights**](https://www.recharm.com/effects/bokeh-lights)
- [Camera flash **Camera flash**](https://www.recharm.com/effects/camera-flash)

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