---
title: "How to add light rays with Claude (tested prompt and script)"
description: "From half a second in, a warm white glow lights up the top-left corner and shafts of golden light fan out from it across the holographic backdrop, slowly turning as the camera orbits and gilding the bottle's metal collar. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/light-rays
---

# How to add light rays with Claude

From half a second in, a warm white glow lights up the top-left corner and shafts of golden light fan out from it across the holographic backdrop, slowly turning as the camera orbits and gilding the bottle's metal collar.

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 light rays to input.mp4
   Where the light comes from (0-1 across and down the frame): (0.12, 0.06)
   Color of the light: (255, 205, 140)
   ```

## 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 light rays 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                                                                            |
| ----------- | ----------------- | --------------------------------------------------------------------------------------- |
| `SOURCE`    | `(0.12, 0.06)`    | Where the light comes from (0-1 across and down the frame).                             |
| `COLOR`     | `(255, 205, 140)` | Color of the light. Warm sunlight by default.                                           |
| `BEAMS`     | `(4, 7, 11)`      | Beams per full turn in three overlapping fans. Higher numbers give more, thinner beams. |
| `SWAY`      | `0.15`            | How fast the beams drift, in radians per second. 0 holds them still.                    |
| `REACH`     | `1.8`             | How far the beams carry before fading out, as a share of the frame height.              |
| `HAZE`      | `0.12`            | Soft light between the beams, as a share of a beam's brightness.                        |
| `THRESHOLD` | `0.7`             | Parts of the picture darker than this (0-1) block the light and cast shadow shafts.     |
| `STRENGTH`  | `1.0`             | How bright the light is (0-1).                                                          |
| `GLOW`      | `0.14`            | Radius of the bright spot at the source, as a share of the frame width.                 |
| `START`     | `0.5`             | Second the light starts fading up.                                                      |
| `RAMP`      | `1.0`             | Seconds it takes to reach full strength. 0 turns it on at once.                         |

## Without the skill

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

```
Add light rays (god rays) to input.mp4 with Python, OpenCV and numpy, piping frames through ffmpeg. The light comes from a point 12% across and 6% down the frame. Beams: for each pixel take its angle a around that point and add up 0.5 + 0.5*cos(n*a + phase) for n = 4, 7 and 11, with the phases drifting at +0.15, -0.30 and +0.45 radians per second; divide by the frame's maximum, cube it, add 0.12 of soft haze, and fade it with distance d as (1 - d / (1.8 x frame height))^1.2. Shade the beams with the picture: on a half-size copy, take brightness (0-1) mapped from 0.70-0.85 to 0-1, radial-blur it toward the light point (7 passes, each averaging the image with a copy scaled toward the point, scales from sqrt(0.1) up to 0.1^(1/128)), divide by its mean, square it, clip to 0-1.5 and scale back up; multiply the beams by that so dark parts like the bottle leave a shadow shaft. Add a bright spot at the point, (1 - d / (14% of the frame width))^2. Clip the light to 0-1, tint it warm (255, 205, 140) and screen-blend it over the frame. The light is off for the first 0.5 seconds, then fades up linearly to full strength over 1 second and stays on to the end. Keep the size, the 30fps frame rate, the 6-second length and the audio, encode H.264 yuv420p, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 light-rays.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Light rays: warm beams of light fan out from a glowing spot in the top-left corner through haze, shaped by the shot.

Usage: python3 light-rays.py input.mp4 output.mp4
Needs ffmpeg, OpenCV and numpy (pip install opencv-python-headless numpy).
"""
import json
import math
import subprocess
import sys

import cv2
import numpy as np

SOURCE = (0.12, 0.06)       # where the light comes from (0-1 across and down the frame)
COLOR = (255, 205, 140)     # warm sunlight
BEAMS = (4, 7, 11)          # beams per full turn in three overlapping fans, which together leave uneven gaps
SWAY = 0.15                 # how fast the beams drift, in radians per second
REACH = 1.8                 # how far the beams carry before fading out, as a share of the frame height
HAZE = 0.12                 # soft light between the beams, as a share of a beam's brightness
THRESHOLD = 0.7             # parts darker than this (0-1) block the light and cast shadow shafts
STRENGTH = 1.0              # how bright the beams are (0-1)
GLOW = 0.14                 # radius of the bright spot at the source, as a share of the frame width
START = 0.5                 # second the light starts fading up
RAMP = 1.0                  # seconds it takes to reach full strength
PASSES = 7                  # radial blur passes (2^7 = 128 samples along each beam)

def beams(t):
    """Light leaving the source: three fans of soft-edged beams that slowly turn, fading with distance."""
    fan = sum(0.5 + 0.5 * np.cos(n * angle + (i + 1) * SWAY * t * (-1) ** i) for i, n in enumerate(BEAMS))
    fan = (fan / fan.max()) ** 3  # cubed for crisp edges, the brightest beam at 1
    return (HAZE + fan) * np.clip(1 - dist / (REACH * h), 0, 1) ** 1.2

def shading(frame):
    """How much light gets through to each pixel: the share of bright, open picture on the way from the source,
    found with a radial blur toward it, so the dark bottle leaves a shadow shaft behind it."""
    small = cv2.resize(frame, (w // 2, h // 2), interpolation=cv2.INTER_AREA)
    luma = cv2.cvtColor(small, cv2.COLOR_RGB2GRAY).astype(np.float32) / 255
    open_ = np.clip((luma - THRESHOLD) / 0.15, 0, 1)  # fully open from 0.15 above the threshold
    cx, cy = SOURCE[0] * w / 2, SOURCE[1] * h / 2
    scale = 0.1  # samples run from each pixel 90% of the way back to the source
    for _ in range(PASSES):
        # each pass averages with a copy pulled toward the source, doubling the samples along each beam
        scale = math.sqrt(scale)
        m = np.float32([[scale, 0, cx * (1 - scale)], [0, scale, cy * (1 - scale)]])
        open_ = (open_ + cv2.warpAffine(open_, m, (w // 2, h // 2), flags=cv2.INTER_LINEAR | cv2.WARP_INVERSE_MAP)) / 2
    open_ = np.clip((open_ / open_.mean()) ** 2, 0, 1.5)  # streaks and shadows relative to the frame's average
    return cv2.resize(open_, (w, h), interpolation=cv2.INTER_LINEAR)

def strength(t):
    return STRENGTH * min(max((t - START) / RAMP, 0), 1) if RAMP else STRENGTH * (t >= START)

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"], float(num) / float(den)

src, out = sys.argv[1:3]
w, h, fps = probe(src)
yy, xx = np.mgrid[0:h, 0:w].astype(np.float32)
dist = np.hypot(xx - SOURCE[0] * w, yy - SOURCE[1] * h)
angle = np.arctan2(yy - SOURCE[1] * h, xx - SOURCE[0] * w)
spot = np.clip(1 - dist / (GLOW * w), 0, 1) ** 2
color = np.float32(COLOR) / 255
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", str(fps), "-i", "-", "-i", src, "-map", "0:v", "-map", "1:a?", "-r", "30",
                           "-c:v", "libx264", "-crf", "18", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
                          stdin=subprocess.PIPE)
i = 0
while True:
    raw = reader.stdout.read(w * h * 3)
    if len(raw) < w * h * 3:
        break
    frame = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    t = i / fps
    k = strength(t)
    if k > 0:
        light = np.clip(beams(t) * shading(frame) + spot, 0, 1)
        # screen blend: the light brightens the picture without ever pushing past white
        beam = light[..., None] * color * k
        frame = (255 * (1 - (1 - frame / 255) * (1 - beam))).astype(np.uint8)
    writer.stdin.write(frame.tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

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