🎬 Star Wars Media Looper for Raspberry Pi 4
Project Overview: This comprehensive guide will help you create a Docker-based media
looper that continuously displays Star Wars pictures and video clips on your Raspberry Pi 4 with a
5-inch screen. The application runs in fullscreen mode and automatically cycles through all your media
files.
📋 Table of Contents
🔧 System Requirements
- Hardware: Raspberry Pi 4 (2GB RAM minimum recommended)
- Display: 5-inch touchscreen (800x480 or similar resolution)
- Storage: MicroSD card (16GB minimum, 32GB+ recommended)
- OS: Raspberry Pi OS (Bullseye or later)
- Software: Docker and Docker Compose
📁 Project Structure
Directory Layout
starwars-looper/
├── Dockerfile
├── docker-compose.yml
├── app/
│ ├── looper.py
│ └── requirements.txt
└── media/
├── images/
│ ├── image1.jpg
│ ├── image2.png
│ └── ...
└── videos/
├── clip1.mp4
├── clip2.mov
└── ...
⚙️ Initial Setup - Raspberry Pi Configuration
Step 1: Update Your System
First, ensure your Raspberry Pi is up to date:
sudo apt-get update
sudo apt-get upgrade -y
Step 2: Install Docker
Download and install Docker using the official installation script:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
Note: After adding your user to the Docker group, you need to log out and log back in for
the changes to take effect.
Step 3: Install Docker Compose
sudo apt-get install -y docker-compose
Step 4: Enable X Server Access for Docker
Allow Docker containers to access the display:
xhost +local:docker
echo "xhost +local:docker" >> ~/.bashrc
echo "export DISPLAY=:0" >> ~/.bashrc
source ~/.bashrc
Step 5: Create Project Directory
mkdir -p ~/starwars-looper/app
mkdir -p ~/starwars-looper/media
cd ~/starwars-looper
📄 Project Files
File 1: Dockerfile
FROM balenalib/raspberry-pi-debian:bullseye
# Install dependencies
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-opencv \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender-dev \
libgomp1 \
libatlas-base-dev \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
libv4l-dev \
libgtk-3-0 \
libqt5gui5 \
libqt5test5 \
x11-apps \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy requirements and install Python packages
COPY app/requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
# Copy application
COPY app/ .
# Run the looper
CMD ["python3", "looper.py"]
File 2: docker-compose.yml
version: '3.8'
services:
starwars-looper:
build: .
container_name: starwars-looper
privileged: true
restart: unless-stopped
environment:
- DISPLAY=:0
- MEDIA_PATH=/media
- IMAGE_DURATION=5
- SHUFFLE=true
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- ./media:/media:ro
devices:
- /dev/vchiq:/dev/vchiq
network_mode: host
File 3: app/requirements.txt
opencv-python==4.8.1.78
numpy==1.24.3
pillow==10.0.0
File 4: app/looper.py
#!/usr/bin/env python3
import cv2
import os
import time
import random
from pathlib import Path
# Configuration
MEDIA_PATH = os.getenv('MEDIA_PATH', '/media')
IMAGE_DURATION = int(os.getenv('IMAGE_DURATION', '5'))
SHUFFLE = os.getenv('SHUFFLE', 'true').lower() == 'true'
SUPPORTED_IMAGES = {'.jpg', '.jpeg', '.png', '.bmp', '.gif'}
SUPPORTED_VIDEOS = {'.mp4', '.avi', '.mov', '.mkv', '.m4v'}
class MediaLooper:
def __init__(self):
self.media_files = []
self.load_media_files()
def load_media_files(self):
"""Load all media files from the media directory"""
print(f"Loading media files from {MEDIA_PATH}...")
if not os.path.exists(MEDIA_PATH):
print(f"Error: Media path {MEDIA_PATH} does not exist!")
return
for root, dirs, files in os.walk(MEDIA_PATH):
for file in files:
ext = Path(file).suffix.lower()
if ext in SUPPORTED_IMAGES or ext in SUPPORTED_VIDEOS:
full_path = os.path.join(root, file)
self.media_files.append({
'path': full_path,
'type': 'image' if ext in SUPPORTED_IMAGES else 'video',
'name': file
})
if SHUFFLE:
random.shuffle(self.media_files)
print(f"Found {len(self.media_files)} media files")
def display_image(self, image_path):
"""Display an image for a specified duration"""
print(f"Displaying image: {os.path.basename(image_path)}")
img = cv2.imread(image_path)
if img is None:
print(f"Error loading image: {image_path}")
return
cv2.namedWindow('Star Wars Looper', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('Star Wars Looper', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
screen_height, screen_width = 480, 800
img_height, img_width = img.shape[:2]
scale = min(screen_width / img_width, screen_height / img_height)
new_width = int(img_width * scale)
new_height = int(img_height * scale)
resized_img = cv2.resize(img, (new_width, new_height))
canvas = cv2.copyMakeBorder(
resized_img,
(screen_height - new_height) // 2,
(screen_height - new_height + 1) // 2,
(screen_width - new_width) // 2,
(screen_width - new_width + 1) // 2,
cv2.BORDER_CONSTANT,
value=[0, 0, 0]
)
cv2.imshow('Star Wars Looper', canvas)
cv2.waitKey(IMAGE_DURATION * 1000)
def display_video(self, video_path):
"""Display a video clip"""
print(f"Playing video: {os.path.basename(video_path)}")
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"Error opening video: {video_path}")
return
cv2.namedWindow('Star Wars Looper', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('Star Wars Looper', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
fps = cap.get(cv2.CAP_PROP_FPS)
delay = int(1000 / fps) if fps > 0 else 30
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
screen_height, screen_width = 480, 800
frame_height, frame_width = frame.shape[:2]
scale = min(screen_width / frame_width, screen_height / frame_height)
new_width = int(frame_width * scale)
new_height = int(frame_height * scale)
resized_frame = cv2.resize(frame, (new_width, new_height))
canvas = cv2.copyMakeBorder(
resized_frame,
(screen_height - new_height) // 2,
(screen_height - new_height + 1) // 2,
(screen_width - new_width) // 2,
(screen_width - new_width + 1) // 2,
cv2.BORDER_CONSTANT,
value=[0, 0, 0]
)
cv2.imshow('Star Wars Looper', canvas)
if cv2.waitKey(delay) & 0xFF == ord('q'):
break
cap.release()
def run(self):
"""Main loop to display media files"""
if not self.media_files:
print("No media files found. Exiting...")
return
print("Starting Star Wars media looper...")
print("Press 'q' to quit")
try:
while True:
for media in self.media_files:
if media['type'] == 'image':
self.display_image(media['path'])
else:
self.display_video(media['path'])
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
raise KeyboardInterrupt
if SHUFFLE:
random.shuffle(self.media_files)
except KeyboardInterrupt:
print("\nStopping looper...")
finally:
cv2.destroyAllWindows()
if __name__ == "__main__":
looper = MediaLooper()
looper.run()
🚀 Installation Steps
Installation Process Flow
1. Create Project Files
↓
2. Add Media Files
↓
3. Build Docker Image
↓
4. Run Container
↓
5. Verify Display
Step 1: Create All Required Files
Create the Dockerfile in the project root:
nano ~/starwars-looper/Dockerfile
Copy the Dockerfile content from above, save (Ctrl+O, Enter), and exit (Ctrl+X).
Create the docker-compose.yml file:
nano ~/starwars-looper/docker-compose.yml
Copy the docker-compose.yml content from above, save and exit.
Create the requirements.txt file:
nano ~/starwars-looper/app/requirements.txt
Copy the requirements.txt content from above, save and exit.
Create the looper.py file:
nano ~/starwars-looper/app/looper.py
Copy the looper.py content from above, save and exit.
Make the Python script executable:
chmod +x ~/starwars-looper/app/looper.py
Step 2: Add Your Media Files
Copy your Star Wars images and videos to the media folder. You can organize them in subfolders if desired:
cp /path/to/your/starwars/images/*.jpg ~/starwars-looper/media/
cp /path/to/your/starwars/videos/*.mp4 ~/starwars-looper/media/
💡 TIP: You can use USB drives or SCP to transfer media files to your Raspberry Pi.
Example using USB drive:
sudo mount /dev/sda1 /mnt/usb
cp /mnt/usb/starwars/* ~/starwars-looper/media/
sudo umount /mnt/usb
Step 3: Build the Docker Image
Navigate to the project directory and build the image:
cd ~/starwars-looper
docker-compose build
⚠️ Important: The initial build may take 10-20 minutes on Raspberry Pi 4 as it downloads
and compiles all dependencies. Be patient!
Step 4: Run the Container
Start the media looper in detached mode:
docker-compose up -d
Step 5: Verify Operation
Check the logs to ensure everything is working:
docker-compose logs -f
You should see output like:
Loading media files from /media...
Found 15 media files
Starting Star Wars media looper...
Displaying image: millennium-falcon.jpg
Playing video: death-star-trench.mp4
Press Ctrl+C to exit the log view (this won't stop the container).
⚙️ Configuration Options
| Environment Variable |
Default Value |
Description |
MEDIA_PATH |
/media |
Path to the media folder inside the container |
IMAGE_DURATION |
5 |
Number of seconds to display each image |
SHUFFLE |
true |
Randomly shuffle media files (true/false) |
Customizing Settings
To change configuration, edit the docker-compose.yml file:
nano ~/starwars-looper/docker-compose.yml
Example: Change image duration to 10 seconds and disable shuffle:
environment:
- DISPLAY=:0
- MEDIA_PATH=/media
- IMAGE_DURATION=10
- SHUFFLE=false
After making changes, restart the container:
docker-compose down
docker-compose up -d
Screen Resolution Adjustment
If your 5-inch screen has a different resolution than 800x480, edit the looper.py file:
nano ~/starwars-looper/app/looper.py
Find these lines and adjust the values:
screen_height, screen_width = 480, 800 # Adjust to your screen resolution
Common 5-inch screen resolutions:
- 800x480 (default)
- 640x480
- 720x480
🎮 Usage Commands
Starting the Looper
cd ~/starwars-looper
docker-compose up -d
Stopping the Looper
docker-compose down
Viewing Logs
docker-compose logs -f
Restarting the Looper
docker-compose restart
Checking Container Status
docker ps
Adding New Media Files
Copy new files to the media folder:
cp /path/to/new/files/* ~/starwars-looper/media/
Then restart the container:
docker-compose restart
Removing the Project
docker-compose down
docker rmi starwars-looper_starwars-looper
rm -rf ~/starwars-looper
🔄 Auto-Start on Boot
To make the looper start automatically when Raspberry Pi boots:
Method 1: Using systemd (Recommended)
Create a systemd service file:
sudo nano /etc/systemd/system/starwars-looper.service
Add the following content:
[Unit]
Description=Star Wars Media Looper
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/home/pi/starwars-looper
ExecStart=/usr/bin/docker-compose up -d
ExecStop=/usr/bin/docker-compose down
User=pi
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable starwars-looper.service
sudo systemctl start starwars-looper.service
Method 2: Using crontab
crontab -e
Add this line at the end:
@reboot sleep 30 && cd /home/pi/starwars-looper && /usr/bin/docker-compose up -d
🔧 Troubleshooting
Problem: "No media files found" Error
Symptoms: Container starts but shows "Found 0 media files"
Solution:
1. Verify media files exist in the correct location:
ls -la ~/starwars-looper/media/
2. Check file permissions:
chmod -R 755 ~/starwars-looper/media/
3. Verify supported file formats (jpg, jpeg, png, bmp, gif, mp4, avi, mov, mkv, m4v)
4. Restart the container:
docker-compose restart
Problem: Display Not Showing
Symptoms: Container runs but nothing appears on screen
Solution:
1. Check DISPLAY environment variable:
echo $DISPLAY
2. Grant X server access:
xhost +local:docker
3. Verify X11 socket permissions:
ls -la /tmp/.X11-unix/
4. Test X server access from container:
docker exec -it starwars-looper env DISPLAY=:0 xeyes
Problem: Video Stuttering or Lag
Symptoms: Videos play but with poor performance
Solution:
1. Reduce video resolution. Convert videos using ffmpeg:
sudo apt-get install ffmpeg
ffmpeg -i input.mp4 -vf scale=800:480 -c:v libx264 -crf 23 output.mp4
2. Increase GPU memory allocation:
sudo nano /boot/config.txt
Add or modify:
gpu_mem=256
sudo reboot
Problem: Container Won't Start
Symptoms: Docker-compose fails to start container
Solution:
1. Check Docker service status:
sudo systemctl status docker
2. View detailed error logs:
docker-compose logs
3. Verify Docker Compose file syntax:
docker-compose config
4. Rebuild the image:
docker-compose build --no-cache
Problem: Image/Video Not Filling Screen
Symptoms: Content appears too small or has large borders
Solution:
1. Check your actual screen resolution:
xrandr | grep '*'
2. Update screen dimensions in looper.py to match your display
3. Rebuild and restart:
docker-compose down
docker-compose build
docker-compose up -d
Problem: Permission Denied Errors
Symptoms: Docker commands require sudo
Solution:
1. Add user to docker group:
sudo usermod -aG docker $USER
2. Log out and log back in, or run:
newgrp docker
3. Verify group membership:
groups
📊 Performance Optimization Tips
1. Optimize Media Files
- Resize images to match screen resolution (800x480)
- Convert videos to H.264 format with appropriate bitrate
- Use progressive JPEGs for faster loading
2. System Configuration
- Disable unnecessary services to free up resources
- Increase GPU memory allocation to 256MB
- Use a high-speed microSD card (Class 10 or UHS-I)
- Enable hardware acceleration in Raspberry Pi config
3. Docker Optimization
- Regularly clean unused Docker images:
docker system prune
- Monitor container resource usage:
docker stats
- Limit container memory if needed in docker-compose.yml
🎯 Advanced Customization
Adding Background Music
You can extend the looper to play Star Wars music in the background by installing pygame and modifying
looper.py:
Add 'pygame' to requirements.txt
Adding Transition Effects
Implement fade-in/fade-out effects between images by modifying the display_image function in looper.py to use
OpenCV's blending functions.
Remote Control via Web Interface
You could add a Flask web server to control playback remotely from your phone or computer.
Multiple Display Support
Run multiple instances with different DISPLAY values to show on multiple screens simultaneously.
📝 Supported File Formats
| Type |
Supported Formats |
Recommended Format |
| Images |
.jpg, .jpeg, .png, .bmp, .gif |
JPG (best compression/quality ratio) |
| Videos |
.mp4, .avi, .mov, .mkv, .m4v |
MP4 with H.264 codec |
🆘 Getting Help
Useful Commands for Diagnostics
Check Docker version:
docker --version
Check Docker Compose version:
docker-compose --version
List all containers:
docker ps -a
View container resource usage:
docker stats starwars-looper
Access container shell for debugging:
docker exec -it starwars-looper /bin/bash
✅ Final Checklist
Before running your project, ensure:
- ✓ Docker and Docker Compose are installed
- ✓ All project files are created correctly
- ✓ Media files are copied to the media folder
- ✓ X server access is granted (xhost +local:docker)
- ✓ Screen resolution is configured correctly in looper.py
- ✓ User is added to docker group
- ✓ DISPLAY environment variable is set
🎬 Expected Output
When everything is working correctly, you should see:
- Fullscreen display of Star Wars images and videos
- Smooth transitions between media files
- Continuous looping through all content
- Black borders maintaining aspect ratio
- No terminal window visible (fullscreen mode)
⚠️ Important Notes
- The looper runs in fullscreen mode and takes over the entire display
- To exit the looper, press 'q' or stop the container with docker-compose down
- Large video files may take time to load initially
- Ensure adequate power supply (official 15W USB-C adapter recommended)
May the Force be with your Raspberry Pi! 🚀
Created for Raspberry Pi 4 with Docker • Last updated: March 2026