---
title: "How to add window and leaf shadows with Claude (tested prompt and script)"
description: "Soft diagonal stripes of shade from window blinds fall across the bottle and backdrop in warm sunlight, with the silhouette of a leafy branch swaying in the top-right corner. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/window-and-leaf-shadows
---

# How to add window and leaf shadows with Claude

Soft diagonal stripes of shade from window blinds fall across the bottle and backdrop in warm sunlight, with the silhouette of a leafy branch swaying in the top-right corner.

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 window and leaf shadows to input.mp4
   Second the shadows fade in: 0.5
   Tilt of the blind stripes in degrees: 30
   ```

## 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 window and leaf shadows 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 shadows fade in.                                                                   |
| `ANGLE`      | `30`                                     | Tilt of the blind stripes in degrees.                                                         |
| `PITCH`      | `0.26`                                   | Spacing of the slat shadows as a share of the frame width. Lower gives more, thinner stripes. |
| `SLAT`       | `0.45`                                   | Share of each stripe that is shadow.                                                          |
| `LEAVES`     | `9`                                      | Pairs of leaves along the branch.                                                             |
| `SWAY`       | `4`                                      | Degrees the leafy branch swings either way.                                                   |
| `SOFTNESS`   | `0.012`                                  | Blur on the shadow edges as a share of the frame width.                                       |
| `SHADE, SUN` | `(0.45, 0.48, 0.58), (1.10, 1.03, 0.92)` | What shadows and sunlit areas multiply each color channel by: cool shade, warm sun.           |

## Without the skill

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

```
Add window blind and leaf shadows to input.mp4 with Python, numpy and Pillow, reading and writing frames with ffmpeg (no stock overlay). Build a light mask from 0 (shadow) to 1 (sun). Blinds: parallel shadow stripes rotated 30 degrees counterclockwise (rising to the right), one every 26% of the frame width (140 px on a 540 px frame), each shadow taking 45% of that spacing, edges blurred with a Gaussian of radius 1.2% of the frame width; draw them on an oversized canvas so they can slide 4% of the frame width to the left over 6 seconds. Leaves: a branch silhouette hanging in from the top-right corner, a stem 5 px wide along the curve x = (1.05 - 0.75 s) x width, y = (-0.02 + 0.42 s + 0.08 sin(3 s)) x height for s from 0 to 1, with a pair of pointed leaves at s = 0.1, 0.2 ... 0.9, one on each side angled 50 degrees off the stem's direction, each (22% - 7% x s) of the frame width long and about half as wide (an outline whose half-width is 0.24 x length x sin(pi u) at fraction u along it); sway the whole branch about its top-right anchor by 4 degrees x sin(2 pi t / 2.4) plus 1.2 degrees x sin(2 pi t / 0.9), and blur it like the stripes. The light mask is the stripes' light times (1 - leaf). Multiply each pixel's RGB by SHADE + (SUN - SHADE) x light, with SHADE = (0.45, 0.48, 0.58) for cool shadows and SUN = (1.10, 1.03, 0.92) for warm sunlight. Leave the frame untouched before 0.5 seconds and fade the shadows in from 0.5 to 1.1 seconds, then keep them to the end. This effect has no text (any text would be drawn with Pillow, not ffmpeg's drawtext filter). Keep the size, 30fps and the audio, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `window-and-leaf-shadows.py` and run:

```
python3 window-and-leaf-shadows.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Window and leaf shadows: slanted blind stripes and a swaying leafy branch cast soft shadows across the shot.

Usage: python3 window-and-leaf-shadows.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys

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

START = 0.5                    # second the shadows fade in
FADE = 0.6                     # seconds the fade-in takes
ANGLE = 30                     # tilt of the blind stripes, degrees (they rise to the right)
PITCH = 0.26                   # distance from one slat shadow to the next, as a share of the frame width
SLAT = 0.45                    # share of each stripe that is shadow
DRIFT = 0.04                   # how far the stripes slide over 6 seconds, as a share of the frame width
LEAVES = 9                     # pairs of leaves along the branch
SWAY = 4                       # degrees the branch swings either way
SWAY_PERIOD = 2.4              # seconds per swing
SOFTNESS = 0.012               # shadow edge blur, as a share of the frame width
SHADE = (0.45, 0.48, 0.58)     # what the shadows multiply each channel by: dark and a little cool
SUN = (1.10, 1.03, 0.92)       # what the lit areas multiply each channel by: a warm sunlight tint

def blinds():
    """Stripes of slat shadow (0) and light (1), drawn larger than the frame and tilted, so they can slide."""
    side = round(math.hypot(w, h) * 1.3)
    img = Image.new("L", (side, side), 255)
    draw = ImageDraw.Draw(img)
    pitch = w * PITCH
    for k in range(int(side / pitch) + 1):
        draw.rectangle([0, k * pitch, side, k * pitch + pitch * SLAT], fill=0)
    return img.rotate(ANGLE, resample=Image.BICUBIC).filter(ImageFilter.GaussianBlur(w * SOFTNESS))

def branch():
    """A leafy branch hanging in from the top-right corner, as a shadow mask (255 = leaf)."""
    img = Image.new("L", (w, h), 0)
    draw = ImageDraw.Draw(img)
    draw.line([stem(s) for s in np.linspace(0, 1, 40)], fill=255, width=max(3, w // 110))
    for k in range(1, LEAVES + 1):
        s = k / (LEAVES + 1)
        (x0, y0), (x1, y1) = stem(s), stem(s + 0.01)
        along = math.atan2(y1 - y0, x1 - x0)
        for side in (-1, 1):
            draw.polygon(leaf(x0, y0, along + side * math.radians(50), w * (0.22 - 0.07 * s)), fill=255)
    return img

def stem(s):
    """Point s (0 at the corner, 1 at the tip) along the branch's curved stem."""
    return w * (1.05 - 0.75 * s), h * (-0.02 + 0.42 * s + 0.08 * math.sin(3 * s))

def leaf(x, y, a, length):
    """A pointed leaf outline, about half as wide as it is long, from (x, y) along angle a."""
    edge = [(length * u, length * 0.24 * math.sin(math.pi * u)) for u in np.linspace(0, 1, 12)]
    outline = edge + [(px, -py) for px, py in reversed(edge)]
    c, s = math.cos(a), math.sin(a)
    return [(x + px * c - py * s, y + px * s + py * c) for px, py in outline]

def light(t):
    """How lit each pixel is at time t, 0 (full shadow) to 1 (full sun), shape (h, w, 1)."""
    shift = w * DRIFT * t / 6
    ox, oy = (stripes.width - w) / 2 + shift, (stripes.height - h) / 2
    window = stripes.transform((w, h), Image.AFFINE, (1, 0, ox, 0, 1, oy), resample=Image.BILINEAR)
    swing = SWAY * math.sin(2 * math.pi * t / SWAY_PERIOD) + SWAY * 0.3 * math.sin(2 * math.pi * t / 0.9)
    leaves = leaf_mask.rotate(swing, resample=Image.BICUBIC, center=(w * 1.05, -h * 0.02))
    leaves = leaves.filter(ImageFilter.GaussianBlur(w * SOFTNESS))
    lit = np.asarray(window, np.float32) / 255 * (1 - np.asarray(leaves, np.float32) / 255)
    return lit[..., None]

def cast(frame, t):
    amount = np.clip((t - START) / FADE, 0, 1)
    if amount == 0:
        return frame
    lit = light(t)
    gain = np.array(SHADE) + (np.array(SUN) - np.array(SHADE)) * lit
    gain = 1 + (gain - 1) * amount
    return (frame * gain).round().clip(0, 255).astype(np.uint8)

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

src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
stripes = blinds()
leaf_mask = branch()

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)
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)
    writer.stdin.write(cast(frame, i / fps).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)
