---
title: "How to add a swipe up arrow with Claude (tested prompt and script)"
description: "A yellow double chevron hops up and drops back over white \"SWIPE UP\" text at the bottom of the frame, over and over, like a story ad nudging you to swipe. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/swipe-up-arrow
---

# How to add a swipe up arrow with Claude

A yellow double chevron hops up and drops back over white "SWIPE UP" text at the bottom of the frame, over and over, like a story ad nudging you to swipe.

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 swipe up arrow to input.mp4
   Label: SWIPE 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 swipe up arrow 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                                             |
| ------------- | --------------- | -------------------------------------------------------- |
| `LABEL`       | `SWIPE UP`      | The text under the arrow.                                |
| `START`       | `0.5`           | Second the arrow and text fade in.                       |
| `PERIOD`      | `0.8`           | Seconds per bounce.                                      |
| `BOUNCE`      | `0.045`         | How far the arrow jumps, as a share of the frame height. |
| `ARROW_COLOR` | `(255, 214, 0)` | Chevron color.                                           |

## Without the skill

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

```
Add a swipe up arrow to input.mp4: two stacked yellow (255, 214, 0) chevrons pointing up, each 18% of the frame width across and 5.5% of the width tall, drawn with lines 2.4% of the width thick and set 6% of the width apart, centered across with their resting center 80% of the way down. Under them, "SWIPE UP" in white bold text 8.5% of the frame width tall, centered across at 89.5% down. Give both a soft dark blurred shadow. They fade in over 0.25 seconds starting at 0.5 seconds, then the chevrons bounce until the end: they lift 4.5% of the frame height and drop back every 0.8 seconds (lift = |sin(pi * (t - 0.5) / 0.8)| x 4.5% of the height), while the text stays still. Nothing shows before 0.5 seconds. 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 `swipe-up-arrow.py` and run:

```
python3 swipe-up-arrow.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Swipe up arrow: a yellow double chevron bounces upward over "SWIPE UP" text near the bottom of the frame.

Usage: python3 swipe-up-arrow.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

LABEL = "SWIPE UP"           # the text under the arrow
START = 0.5                  # second the arrow and text fade in
FADE = 0.25                  # seconds the fade-in takes
PERIOD = 0.8                 # seconds per bounce
BOUNCE = 0.045               # how far the arrow jumps, as a share of the frame height
ARROW_Y = 0.80               # arrow's resting center (0 = top, 1 = bottom)
TEXT_Y = 0.895               # text's center
ARROW_COLOR = (255, 214, 0)  # chevron color
TEXT_SIZE = 0.085            # text height as a share of the frame width
FONTS = ["/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 with_shadow(img):
    """Put a soft dark shadow under img so it reads on any footage."""
    shadow = Image.new("RGBA", img.size, (0, 0, 0, 0))
    shadow.putalpha(img.getchannel("A").point(lambda a: a * 0.6))
    shadow = shadow.filter(ImageFilter.GaussianBlur(w * 0.012))
    shadow.alpha_composite(img)
    return shadow

def arrow():
    """Two stacked chevrons pointing up, centered in a w x w/3 image."""
    img = Image.new("RGBA", (w, w // 3))
    draw = ImageDraw.Draw(img)
    half, rise, gap, stroke = w * 0.09, w * 0.055, w * 0.06, round(w * 0.024)
    cx, cy = w / 2, w / 6
    for tip in (cy - gap * 0.9, cy + gap * 0.1):
        draw.line([(cx - half, tip + rise), (cx, tip), (cx + half, tip + rise)], fill=ARROW_COLOR, width=stroke, joint="curve")
    return with_shadow(img)

def label():
    img = Image.new("RGBA", (w, w // 5))
    font = ImageFont.truetype(FONT_FILE, round(w * TEXT_SIZE))
    ImageDraw.Draw(img).text((w / 2, w / 10), LABEL, font=font, anchor="mm", fill="white")
    return with_shadow(img)

def overlay(t):
    img = Image.new("RGBA", (w, h))
    if t < START:
        return img
    alpha = min((t - START) / FADE, 1)
    lift = abs(math.sin(math.pi * (t - START) / PERIOD)) * BOUNCE * h  # up and back down once per PERIOD
    img.alpha_composite(text_img, (0, round(h * TEXT_Y - text_img.height / 2)))
    img.alpha_composite(arrow_img, (0, round(h * ARROW_Y - arrow_img.height / 2 - lift)))
    if alpha < 1:
        img.putalpha(img.getchannel("A").point(lambda a: a * alpha))
    return img

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)
arrow_img, text_img = arrow(), label()
render(src, out)
```

## More captions and text

- [AI chat reply animation **AI chat reply animation**](https://www.recharm.com/effects/ai-chat-typing)
- [Animated bar chart **Animated bar chart**](https://www.recharm.com/effects/animated-bar-chart)
- [Animated checklist **Animated checklist**](https://www.recharm.com/effects/animated-checklist)
- [Animated line graph **Animated line graph**](https://www.recharm.com/effects/animated-line-graph)
- [Animated pie chart **Animated pie chart**](https://www.recharm.com/effects/animated-pie-chart)

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