Skip to content

viticci/frames-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Frame device screenshots and screen recordings with Apple product bezels from the command line. Auto-detects devices, supports colors, merging, and batch processing. Based on the Apple Frames shortcut by me for MacStories.net, not affiliated with Apple.


Installation

Requirements

  • Python 3.8+
  • Pillow (Python imaging library)
  • ffmpeg 5.1+ and ffprobe 5.1+ for video framing (frames setup checks this and can install ffmpeg with Homebrew on macOS)

Option A: Clone the repo (recommended)

git clone https://github.com/viticci/frames-cli.git
cd frames-cli
pip3 install Pillow

Then symlink the script into a directory that's already in your PATH:

# Check which bin directory is in your PATH (use the first one that exists)
# Common locations: ~/.local/bin, ~/bin, /usr/local/bin

# Create the directory if needed, then symlink
mkdir -p ~/.local/bin
ln -s "$(pwd)/frames" ~/.local/bin/frames

If ~/.local/bin isn't in your PATH yet, add it to ~/.zshrc (or ~/.bashrc):

export PATH="$HOME/.local/bin:$PATH"

Then restart your terminal or run source ~/.zshrc.

Verify it works:

frames --version

Option B: Direct download

pip3 install Pillow
mkdir -p ~/.local/bin
curl -o ~/.local/bin/frames https://raw.githubusercontent.com/viticci/frames-cli/main/frames
chmod +x ~/.local/bin/frames

If ~/.local/bin isn't in your PATH yet, add it to ~/.zshrc (or ~/.bashrc):

export PATH="$HOME/.local/bin:$PATH"

Then restart your terminal or run source ~/.zshrc.

Setup

The CLI will automatically detect and download Apple Frames 4 assets on first run. Setup also checks video requirements and, on macOS, can install ffmpeg for you with Homebrew if it is missing. You can also set up manually:

# Guided download (interactive — downloads ~40 MB from cdn.macstories.net)
frames setup

# Or point to an existing assets folder
frames setup /path/to/Frames

The guided setup downloads the asset pack, extracts it, and saves the path to ~/.config/frames/config.json. If assets get corrupted or you need a fresh copy, run frames setup again to re-download.

You can also set the FRAMES_ASSETS environment variable instead of using the config file.


Quick Start

# Frame a screenshot — auto-detects device, saves as name_framed.png
frames screenshot.png

# Frame all PNGs in a directory
frames *.png

# Frame with a specific color
frames -c "Cosmic Orange" screenshot.png

# Frame with random colors
frames -c random *.png

# Assign colors per input
frames --colors "Silver,Space Black,random" one.png two.png three.png

# Tip: frame a screen recording with the same auto-detected device bezel
frames video recording.mp4

# Tip: tune MP4 export size/quality; best is the default
frames video --preset compact recording.mp4
frames video --preset balanced recording.mp4
frames video --preset best recording.mp4

# Tip: inspect the video match before spending time rendering
frames --json video-info recording.mp4

# Tip: merge framed videos and play them left to right
frames video -m --playback-offset 1.mp4 2.mp4

# Frame and merge side by side
frames -m screenshot1.png screenshot2.png

# Merge in batches of 3 (15 files → 5 merged images)
frames -b 3 *.png

# Copy framed result to clipboard (macOS)
frames --copy screenshot.png

# Save to /framed/ subfolder
frames -f screenshot.png

# Save to custom subfolder
frames --subfolder mockups *.png

# Show device info without framing
frames info screenshot.png

# List all supported devices
frames list

Commands

Default framing

Frame one or more screenshots. The frame keyword is optional — passing files directly uses it automatically.

frames screenshot.png
frames frame screenshot.png   # same thing

# With flags
frames -c "Desert Titanium" -o ~/output/ *.png
frames -m -s 80 screenshot1.png screenshot2.png

Output naming: originalname_framed.png in the same directory as the source. Merged output is merged_framed.png.

Device detection: Automatic from screenshot pixel width. When multiple devices share a width, height disambiguates. The newest device frame is used when multiple generations share a resolution — override with --device.

Color resolution: --colors per-input value > --color flag > user default (set via colors command) > first color in device's list.


--color / -c and --colors

Specify a frame color by exact name, 1-based index, default, or random.

frames -c "Cosmic Orange" screenshot.png
frames -c 2 screenshot.png
frames -c random *.png
frames --colors "Silver,Space Black,random" one.png two.png three.png

--color random randomizes independently per input. --colors maps comma-separated values to expanded inputs by order, and it cannot be combined with --color. Use list-colors to see what's available for a device.


video

Frame screen recordings or videos with the same Apple Frames assets used for screenshots.

frames video recording.mp4
frames video -c Silver recording.mp4
frames video --colors "Silver,random" 1.mp4 2.mp4
frames video --strip-audio recording.mp4
frames video --preset compact recording.mp4
frames video --preset balanced recording.mp4
frames video --preset best recording.mp4
frames video -m --alpha 1.mp4 2.mp4
frames video -m --background transparent 1.mp4 2.mp4

Video support requires ffmpeg 5.1+ and ffprobe 5.1+. frames setup checks for both and can install ffmpeg with Homebrew on macOS. Supported input extensions are .mp4, .mov, and .m4v.

Single-video output is originalname_framed.mp4 by default, or .mov for --alpha, --codec prores, or --background transparent. Audio is preserved unless --strip-audio is passed.

Use --alpha or --background transparent to create transparent ProRes 4444 .mov output. This works for single videos and merged videos. --alpha defaults the canvas to transparent unless you explicitly pass another --background; MP4/H.264 and MP4/HEVC outputs do not support alpha. If you pass an explicit output file for transparent output, it must use a .mov extension.

Interactive video renders show a live progress bar. JSON and non-interactive runs stay quiet for scripting. Completed video exports report output size and source-vs-output savings in both human output and JSON.

Video presets tune MP4 export size and quality. best is the default. balanced and compact lower H.264/HEVC bitrate for hardware encoding and use higher CRF values for software encoding. --quality N remains an expert CRF override for software encoders only; lower is higher quality.

Common video recipes:

Goal Command
Check the match before rendering frames --json video-info recording.mp4
Frame one video frames video recording.mp4
Use compact MP4 export frames video --preset compact recording.mp4
Use balanced MP4 export frames video --preset balanced recording.mp4
Use best MP4 export frames video --preset best recording.mp4
Frame silently frames video --strip-audio recording.mp4
Merge videos simultaneously frames video -m 1.mp4 2.mp4
Play merged videos left to right frames video -m --playback-offset 1.mp4 2.mp4
Assign per-input colors frames video --colors "Silver,random" 1.mp4 2.mp4
Transparent ProRes MOV frames video --alpha recording.mp4
Transparent merged ProRes MOV frames video -m --alpha 1.mp4 2.mp4
Transparent merged canvas frames video -m --background transparent 1.mp4 2.mp4
# Transparent ProRes MOV
frames video --alpha recording.mp4

# Transparent merged ProRes MOV
frames video -m --alpha 1.mp4 2.mp4
frames video -m --background transparent 1.mp4 2.mp4

# HEVC output
frames video --codec hevc recording.mp4

# Compact HEVC output
frames video --codec hevc --preset compact recording.mp4

# Custom background
frames video --background "#f5f5f5" recording.mp4

Video merging

Merge multiple framed videos into a horizontal canvas. By default, videos play simultaneously and the output duration is the longest input.

frames video -m 1.mp4 2.mp4
frames video -m --no-scale 1.mp4 2.mp4
frames video -m --alpha 1.mp4 2.mp4
frames video -m --background transparent 1.mp4 2.mp4

When merging different devices, videos are proportionally scaled using the same physical-height model as image merges and bottom-aligned.

Use --playback-offset to play videos one at a time from left to right. Inactive videos hold on their first frame before playback and their final frame after playback.

frames video -m --playback-offset 1.mp4 2.mp4

With --playback-offset, audio is concatenated sequentially and videos without audio contribute silence. Simultaneous video merges omit mixed audio in this version.

Transparent merges output .mov using ProRes 4444 with yuva444p10le pixels. Use this when you want the merged devices floating over transparency for Final Cut Pro, Keynote, or another compositor.


video-info

Probe videos and report matching Apple frame metadata without rendering.

frames video-info recording.mp4
frames --json video-info recording.mp4
frames --json video-info --colors "Silver,random" 1.mp4 2.mp4

video-info uses the same device, variant, color, ffmpeg, and ffprobe checks as frames video. It reports dimensions, duration, fps, codec, audio state, matched device, selected color, frame size, mask state, and resize metadata.

frames video --preset compact|balanced|best controls MP4 export size/quality. best is the default. Presets affect H.264 and HEVC bitrate for hardware encoders and CRF for software encoders; ProRes/alpha output keeps ProRes settings. --quality N is still available as a software CRF override.

frames video --alpha ... and frames video --background transparent ... return transparent ProRes .mov output. In JSON, alpha is true and background is transparent unless an explicit opaque background is provided.

frames --json video ... returns the selected preset plus output_size_bytes, output_size, source_size_bytes, source_size, savings_bytes, savings, and savings_percent after export. Merged JSON output includes the same top-level size and savings fields.


--merge / -m and --spacing / -s

Merge all framed images into a single horizontal strip. Default spacing between frames is 60px.

When merging different devices, frames are automatically scaled to reflect real-world physical proportions and bottom-aligned. An iPhone next to an iPad will be proportionally shorter, just like in real life. Same-device merges are unaffected.

frames -m screenshot1.png screenshot2.png screenshot3.png
frames -m -s 120 screenshot1.png screenshot2.png

The merged output is saved as merged_framed.png in the output directory.


--no-scale

Disable proportional scaling when merging. All frames render at native pixel size and are center-aligned (pre-v1.2 behavior).

frames -m --no-scale iphone.png ipad.png

--batch / -b

Merge screenshots in sequential batches of N. Produces multiple merged images instead of one.

# 15 screenshots → 5 merged images of 3
frames -b 3 *.png

# Batch merge with custom spacing and output directory
frames -b 4 -s 80 -o /output/ *.png

# Batch merge with random colors
frames -b 3 -c random *.png

Batch size must be at least 2. If the total isn't evenly divisible, the last batch contains the remainder. Output files are named merged_1_framed.png, merged_2_framed.png, etc.

--batch implies --merge — no need to pass both. JSON output includes a batches array with per-batch counts and paths.


--subfolder / -f

Save framed images to a subfolder relative to the source file's directory, instead of next to the originals. Two modes:

  • -f (shorthand) saves to a /framed/ subfolder
  • --subfolder NAME saves to a custom-named subfolder
frames -f screenshot.png
# saves to ./framed/screenshot_framed.png

frames -f *.png
# saves all to ./framed/

frames --subfolder mockups *.png
# saves all to ./mockups/

To make subfolder mode the default, run frames setup --subfolder. To revert, run frames setup --no-subfolder.


--output / -o

Save framed images to a specific output directory.

frames -o ~/Desktop/framed/ screenshot.png
frames -o /tmp/output/ *.png

--copy

Copy the framed image directly to the macOS clipboard. Works with a single image only. The success/failure message prints to stderr, so it won't corrupt --json output.

frames --copy screenshot.png
frames --json --copy screenshot.png   # valid JSON on stdout

--device / -d

Force a specific device frame instead of auto-detecting from the screenshot dimensions. Skips automatic variant resolution, so the exact device you specify is used. Useful when multiple devices share a resolution.

frames -d "iPhone 17 Pro Portrait" screenshot.png
frames -d "MacBook Pro M5 14" screenshot.png

Use frames list to see exact device names.


--json

Output machine-readable JSON instead of human-readable text. Designed for AI agent pipelines and scripting.

frames --json screenshot.png
# → {"source": "screenshot.png", "device": "iPhone 17 Pro Portrait", "color": "Cosmic Orange", "output": "/path/to/screenshot_framed.png", ...}

frames --json -m screenshot1.png screenshot2.png
# → {"merged": "/path/to/merged_framed.png", "count": 2, "frames": [...]}

frames --json info screenshot.png
# → {"file": "screenshot.png", "device": "iPhone 17 Pro Portrait", "width": 1290, "height": 2796, ...}

list

List all supported devices grouped by category. Shows pixel dimensions and available color counts.

frames list

colors

Interactive TUI color picker (curses). Navigate with arrow keys, select with space, confirm with enter. Sets per-device default colors stored in ~/.config/frames/config.json.

frames colors

list-colors

Show all available colors for a specific device. Supports partial name matching.

frames list-colors "17 Pro"
frames list-colors "MacBook"
frames list-colors "Watch Ultra"

info

Detect the device for a screenshot without framing it. Shows device name, pixel dimensions, available colors, mask and resize info.

frames info screenshot.png
frames --json info screenshot.png

setup

Download assets or configure the assets folder path. Without arguments, starts an interactive download from cdn.macstories.net (~40 MB). With a path, points the CLI at an existing assets folder.

# Download assets interactively (first-time setup or re-download)
frames setup

# Point to an existing assets folder
frames setup /path/to/Frames

# With subfolder mode
frames setup --subfolder /path/to/Frames     # enable subfolder mode by default
frames setup --no-subfolder /path/to/Frames  # disable subfolder mode (default)

If assets are missing or outdated when you run any command, the CLI will automatically offer to download them.


Configuration

The setup and colors commands write to ~/.config/frames/config.json:

{
  "assets_path": "/path/to/Frames",
  "use_subfolder": false,
  "default_colors": {
    "iPhone 17 Pro": "Deep Blue",
    "MacBook Pro M5 14": "Space Black"
  }
}

Assets priority order: --assets flag > FRAMES_ASSETS env var > config file > default iCloud Shortcuts path.

Color priority order: --color flag > config default (set via colors command) > first color in device list.

The FRAMES_ASSETS environment variable takes precedence over the config file and is useful for CI or non-standard setups:

FRAMES_ASSETS=/path/to/assets frames screenshot.png

For AI Agents

The --json flag makes frames pipeline-friendly. All output goes to stdout; errors go to stderr.

# Frame a screenshot, capture the output path
OUTPUT=$(frames --json screenshot.png | python3 -c "import sys,json; print(json.load(sys.stdin)['output'])")

# Get device info as JSON
frames --json info screenshot.png

# Frame and merge, get merged path
frames --json -m *.png

Batch processing patterns:

# Frame everything into a separate directory
frames -o ~/framed/ ~/screenshots/*.png

# Frame and merge all into one image
frames -m -o ~/framed/ ~/screenshots/*.png

# Random colors for visual variety
frames -c random -o ~/framed/ ~/screenshots/*.png

# Subfolder mode — outputs land in ./framed/ next to sources
frames -f ~/screenshots/*.png

Claude Code skill: A skill file is included in skill/SKILL.md. Install it to ~/.claude/skills/frames-cli/SKILL.md to give Claude Code native awareness of the CLI, its flags, and batch patterns.


Supported Devices

Category Devices Notes
iPhone 17 17, 17 Pro, 17 Pro Max Portrait + landscape
iPhone Air Air Portrait + landscape
iPhone 16 16, 16 Plus, 16 Pro, 16 Pro Max Portrait + landscape
iPhone 12-13 12/13 mini, 12/13, 12/13 Pro, 12/13 Pro Max Portrait + landscape
iPhone 8 / SE iPhone 8, SE Portrait
iPad Pro 11" / 13" (2018-2024), Air, mini Portrait + landscape
MacBook Neo, Air M5 13"/15", Pro M5 14"/16", Pro 2021, Air 2020-2022, Pro 13 Front-facing
iMac iMac M4 7 colors (Silver default)
Studio Display Studio Display, Studio Display XDR 2 colors each (Light default); XDR is a variant
Apple Watch Ultra 3, Ultra 2024, Series 11, Series 10, Series 7 Including band combinations

Watch Ultra 3 supports 13 case + band combinations. Watch Series 11 supports 22 case + band combinations per size. All devices that have landscape variants support both orientations.


Credits

by Federico Viticci, MacStories.net

About

Frame screenshots and screen recordings taken on Apple devices with official product bezels.

Resources

License

Stars

Watchers

Forks

Contributors

Languages