Raspberry Pi Photo Frame: feh Slideshow and MagicMirror² Setup

raspberry pi photo frame

A Raspberry Pi photo frame displays a rotating slideshow of images on any HDMI monitor or TV, running continuously from a folder of photos on a microSD card or USB drive. The simplest working approach is feh, a lightweight image viewer installed via APT that runs a full-screen slideshow and autostarts at boot with three lines in an LXDE autostart file. For a more capable build that adds a clock, weather, calendar, and Google Photos integration, MagicMirror² provides a modular browser-based overlay. This guide covers the Bookworm-correct feh setup, display rotation and screen blanking, and the MagicMirror² install for users who want the extended features.

Last tested: Raspberry Pi OS Bookworm Desktop 32-bit | May 2026 | Raspberry Pi 4 Model B (2GB) | feh 3.10, MagicMirror² 2.27

Key Takeaways

  • Use Raspberry Pi OS Desktop (not Lite) for the photo frame build. The Desktop image includes the LXDE desktop environment that feh and MagicMirror² both rely on for display output. Raspberry Pi OS Lite requires additional display server setup that adds unnecessary complexity for this project.
  • Do not hardcode /home/pi paths in autostart scripts. The default user is no longer pi on Bookworm. Scripts set by previous guides using /home/pi/slideshow.sh will silently fail because that path does not exist. Use $HOME or the actual username set at flash time.
  • For display rotation, Pi 4 and earlier edit /boot/config.txt. Pi 5 edits /boot/firmware/config.txt. The rotation parameter is display_rotate=1 for 90 degrees, display_rotate=2 for 180 degrees, and display_rotate=3 for 270 degrees.

Building a Raspberry Pi Photo Frame with feh

Flash Raspberry Pi OS Bookworm Desktop (32-bit for Pi 3, 64-bit for Pi 4 and Pi 5) to a microSD card using Raspberry Pi Imager. Configure username, password, hostname, and WiFi in Imager’s advanced settings. Boot and complete the first-run desktop wizard.

Create a directory for the photos and copy images into it:

mkdir -p ~/Pictures/slideshow

Transfer photos via SCP from another machine, mount a USB drive, or copy from an SD card reader. For ongoing photo sync from a NAS or Syncthing, see the Keeping Photos Updated section below.

Install feh:

sudo apt update && sudo apt install -y feh

Test the slideshow manually before configuring autostart:

feh --recursive --fullscreen --auto-zoom --slideshow-delay 8 ~/Pictures/slideshow

Expected result: feh opens full-screen and cycles through the images in ~/Pictures/slideshow, displaying each for 8 seconds. Press Escape or Q to exit. If images appear distorted, add --auto-zoom to fit images to the screen without cropping. If feh exits immediately, confirm the slideshow directory contains at least one image file: ls ~/Pictures/slideshow/.

Raspberry Pi photo frame build flow: hardware, feh slideshow, display configuration, MagicMirror, and optional photo sync

Create a slideshow script for the autostart entry. Using $HOME rather than a hardcoded path makes it portable across usernames:

nano ~/slideshow.sh

Paste the following content, save with Ctrl+O, exit with Ctrl+X:

#!/bin/bash
sleep 5
feh --recursive --fullscreen --auto-zoom --randomize --slideshow-delay 8 "$HOME/Pictures/slideshow"
chmod +x ~/slideshow.sh

Configure the script to run at desktop login by adding it to the LXDE autostart file. The autostart file path uses the actual username:

mkdir -p ~/.config/lxsession/LXDE-pi
echo "@bash $HOME/slideshow.sh" >> ~/.config/lxsession/LXDE-pi/autostart

Expected result: After rebooting, the slideshow starts automatically within a few seconds of the desktop loading. If it does not start, verify the autostart file contains the correct line: cat ~/.config/lxsession/LXDE-pi/autostart. If the path shows /home/pi/, it was set by a previous guide and must be corrected to use $HOME or the actual username.

Screen Rotation, Blanking, and Display Settings

Display rotation. For a portrait-orientation photo frame, rotate the display output in config.txt. On Pi 4 and earlier:

sudo nano /boot/config.txt

On Pi 5:

sudo nano /boot/firmware/config.txt

Add the rotation line at the end of the file:

# 0=normal, 1=90 degrees, 2=180 degrees, 3=270 degrees
display_rotate=1

Reboot to apply. feh will automatically fill the rotated screen. If images appear stretched after rotation, ensure --auto-zoom is in the feh command.

Disable screen blanking. By default, the Pi’s desktop blanks the display after a few minutes of inactivity. For a photo frame running continuously, disable this. Add these lines to the LXDE autostart file alongside the slideshow entry:

echo "@xset s off" >> ~/.config/lxsession/LXDE-pi/autostart
echo "@xset -dpms" >> ~/.config/lxsession/LXDE-pi/autostart
echo "@xset s noblank" >> ~/.config/lxsession/LXDE-pi/autostart

Scheduled screen off. For a photo frame that turns the display off at night, use cron to run vcgencmd display_power 0 at bedtime and vcgencmd display_power 1 in the morning:

crontab -e
# Add:
0 22 * * * vcgencmd display_power 0   # Off at 10pm
0 7  * * * vcgencmd display_power 1   # On at 7am

Expected result: The display stays on continuously while the Pi is running. The screen turns off at 10pm and back on at 7am. If the screen still blanks, confirm all three @xset lines are present in the autostart file and that the file is in the correct location for the current user.

Adding MagicMirror² for Google Photos and Widgets

MagicMirror² transforms the photo frame into a smart display with modules for clock, weather, calendar, news, and Google Photos. It is a Node.js application that renders a browser overlay. This section is optional. The feh setup above is complete without it.

MagicMirror² requires a current Node.js version. Install via NodeSource (not the APT nodejs package, which is outdated):

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

Clone the MagicMirror² repository from the current location and install:

git clone https://github.com/MagicMirrorOrg/MagicMirror
cd MagicMirror
npm install

Copy the sample config and start MagicMirror² to test:

cp config/config.js.sample config/config.js
npm start

Expected result: A browser window fills the screen showing the default MagicMirror² layout: clock, calendar, news, and weather placeholders. Press Ctrl+Q to exit. If the window does not open, confirm the Pi has a display connected and the DISPLAY environment variable is set: echo $DISPLAY should return :0.

Configure pm2 to run MagicMirror² at boot:

sudo npm install -g pm2
pm2 start ~/MagicMirror/installers/mm.sh --name MagicMirror
pm2 startup
# Run the printed sudo command
pm2 save

Google Photos integration requires the MMM-GooglePhotos module and OAuth 2.0 credentials from the Google Cloud Console. The setup involves creating a Google Cloud project, enabling the Photos Library API, generating OAuth credentials, and running an authentication script that opens a browser for login. Google changes its OAuth flow periodically. Follow the current documentation in the MMM-GooglePhotos repository rather than step-by-step instructions here that may become outdated. The module repository is at github.com/eoula/MMM-GooglePhotos.

Keeping photos updated. For a photo frame that stays current without manual file transfers, three approaches work on Bookworm: copy photos from a USB drive automatically when inserted (using a udev rule to trigger a copy script), sync a shared folder from Syncthing on another machine on the network, or use the Immich mobile app’s album export to a shared folder. For Syncthing setup, see Syncthing Raspberry Pi: Complete Family Sync and Versioning Guide. For a complete DSI display setup, see Raspberry Pi DSI Display: Complete Setup Guide. For Immich photo server with mobile sync, see Immich Raspberry Pi 5: Complete Photo Backup and Mobile Sync Guide.

FAQ

What is the best Raspberry Pi model for a photo frame?

Pi 3B+ or Pi 4 (2GB) for most photo frame builds. A basic feh slideshow runs comfortably on a Pi Zero 2W, but the Zero’s single micro-USB data port makes photo loading via USB more awkward. Pi 3B+ is the best value for a dedicated always-on photo frame: cheap secondhand, has full-size HDMI and USB-A ports, runs 1080p playback, and draws only ~3W at idle. Pi 4 is the correct choice if MagicMirror² is planned, as MagicMirror’s Electron browser benefits from the extra CPU headroom.

Does the Raspberry Pi photo frame work with any display?

Any HDMI display works. This includes monitors, TVs, portable USB-C displays, and repurposed laptop screens with an HDMI driver board. The official Raspberry Pi 7-inch DSI touchscreen also works and does not require an HDMI cable. It connects directly to the Pi’s DSI ribbon connector. Portrait-orientation photo frames work best with either a monitor that physically rotates or a standard monitor mounted sideways, with display rotation set in config.txt to correct the image orientation.

How do I stop the Raspberry Pi photo frame screen from turning off?

Add three lines to ~/.config/lxsession/LXDE-pi/autostart: @xset s off, @xset -dpms, and @xset s noblank. These disable the screensaver, power management, and display blanking respectively. If the screen still blanks after adding these lines, check that the file is saved in the correct location for the current username and reboot. The LXDE-pi directory name is correct for Raspberry Pi OS Desktop regardless of the logged-in username.

Can the Raspberry Pi photo frame display photos from Google Photos?

Yes, via the MMM-GooglePhotos module for MagicMirror². The module downloads photos from a specified Google Photos album and displays them in rotation. Setup requires creating OAuth 2.0 credentials in the Google Cloud Console and running a one-time authentication step. The process is functional but involves several Google Cloud steps that can be fiddly. Follow the current documentation at the MMM-GooglePhotos GitHub repository. Alternatively, use the Google Photos “partner sharing” feature to share an album to another Google account, download that album periodically with a cron job and gphotos-sync, and point feh at the downloaded folder.

How do I add new photos to the Raspberry Pi photo frame?

Four approaches in increasing order of automation: copy files manually via SCP (scp photo.jpg youruser@piframe.local:~/Pictures/slideshow/); plug in a USB drive and copy manually; set up Syncthing to sync a folder automatically from another machine on the network; or use Immich on Pi 5 with mobile upload and export to the slideshow folder. feh picks up new files in the slideshow directory automatically on the next slideshow loop. No restart needed. For MagicMirror² with Google Photos, the module polls Google Photos at a configured interval and refreshes the photo pool automatically.

References:


About the Author

Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.

Last tested hardware: Raspberry Pi 4 Model B (2GB). Last tested OS: Raspberry Pi OS Bookworm Desktop 32-bit. feh 3.10, MagicMirror² 2.27, May 2026.