Offline-first digital picture frame software for Raspberry Pi.
FastAPI backend, React + TypeScript admin UI, DecentDB metadata, filesystem-backed images, and a polished fullscreen /display experience.
Quick start · Pi deployment · Development · Documentation
SPF5000 is a self-hosted, LAN-manageable digital picture frame stack for Raspberry Pi-class hardware. It is built for households that want an appliance-like frame without subscriptions, vendor lock-in, fragile mobile apps, cloud dependency, or visible black flashes between slides.
Table of contents
SPF5000 exists to make a digital picture frame feel like a dependable home appliance instead of a subscription service. Here are the highlights of what's inside:
- 🔌 Offline-First by Design — Playback stays local and cached instead of depending on live cloud responses.
- 🖼️ Appliance-Oriented UX — The Pi boots directly into a dedicated, gorgeous fullscreen slideshow at
/display. - 🚀 Smooth Transitions — Say goodbye to jarring black flashes! The display route uses a dual-layer renderer that preloads the next image before animating.
- 🎨 Expanded Background Presentation — Portrait and mixed-aspect slides support
black,dominant_color,gradient,soft_vignette,palette_wash,blurred_backdrop,mirrored_edges, andadaptive_auto; cached display-variant metadata drives color-based modes while image-based treatments can reuse the display variant at render time. - 📱 LAN-Managed Admin — Setup, login, settings, import, and diagnostics are seamlessly available from a browser on your local network.
- 🧳 Backup & Export Ready — Download ZIP backups of the DecentDB state, restore validated database backups, and export original collection media without dropping to SSH.
- ☁️ Google Photos Integration — First-class Google Photos provider using the Ambient API for offline-cached local playback.
- 🌦️ Weather & Alerts — Built-in National Weather Service integration for real-time widget overlays and fullscreen alerts.
- 🛠️ Modern Stack — Built with a snappy FastAPI backend and a polished React 19 + TypeScript + Vite frontend.
- 🔒 Secure & Private — Single-admin session auth, clear storage boundaries with DecentDB, and locally managed files with SHA-256 duplicate detection.
- 🌙 Smart Scheduling — App-managed sleep schedule stored in DecentDB, evaluated in a configurable display timezone with Pi-local fallback, and surfaced in the admin UI with both Pi-local and display-time clocks.
- 🍓 Pi-Ready — Includes appliance scripts for install, uninstall, and health checks on Raspberry Pi OS Desktop.
- 🔍 Doctor Page — Built-in health diagnostics page that aggregates subsystem status, identifies problems, and provides actionable remediation guidance.
- 📜 Admin Log Viewer — Inspect recent SPF5000 log files and rotated backups from the admin UI without dropping to SSH.
- Backend: FastAPI
- Frontend: React 19 + TypeScript + Vite
- Structured state: DecentDB
- Binary storage: local filesystem
- Display runtime: Chromium kiosk mode on Raspberry Pi
Local import folder Google Photos Ambient API
│ │
▼ ▼
LocalFilesProvider GooglePhotosProvider
│ │
└──────────────► import / sync services
│
┌─────────────────┴─────────────────┐
▼ ▼
DecentDB metadata/settings/bootstrap Filesystem originals +
state display/thumbnail variants
│
▼
/api/display/playlist
│
▼
Chromium kiosk at /display
The fullscreen /display route is intentionally separate from the admin shell. It stays public, runs without admin chrome, uses a hidden cursor, preloads the next slide before transitioning, supports configurable black, dominant_color, gradient, soft_vignette, palette_wash, blurred_backdrop, mirrored_edges, and adaptive_auto background presentation, keeps cached display-variant metadata as the source for color-based modes, may reuse the display variant directly for image-based modes, and can render an intentional black fullscreen sleep state during configured quiet hours evaluated in the configured display timezone or the Pi-local timezone when none is set.
Weather and alert overlays are also display concerns, but they still follow the same appliance rules: weather and alerts are fetched and cached by the backend, /display consumes only cached data, and sleep mode remains the highest-precedence fullscreen state.
- Python
3.11+ - Node.js and npm
- for source-based local development, a local DecentDB checkout so you can install the Python binding and build the native library
SPF5000 does not install DecentDB from backend/requirements.txt. You must make both the editable Python binding and the native library available separately.
For local development in this repo, the expected DecentDB path is still a source checkout. The Raspberry Pi appliance installer uses a different path: it downloads the latest matching release bundle for the native library and the matching source archive for the Python binding.
cd backend
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m pip install -e /path/to/decentdb/bindings/python
cd /path/to/decentdb
nimble build_lib
export DECENTDB_NATIVE_LIB=$PWD/build/libc_api.so
cd /path/to/spf5000/backend
python -m appIf you cloned decentdb next to this repository, a typical install looks like this:
cd backend
source .venv/bin/activate
python -m pip install -e ../../decentdb/bindings/python
cd ../../decentdb
nimble build_lib
export DECENTDB_NATIVE_LIB=$PWD/build/libc_api.so
cd ../spf5000/backend
python -m appWhen running from a DecentDB source checkout, build/libc_api.so is the normal Linux output. Release bundles may instead ship libdecentdb.so; the Python binding accepts either filename, and DECENTDB_NATIVE_LIB can point to either one.
The backend reads spf5000.toml from the repo root by default. Override the config path with SPF5000_CONFIG=/path/to/spf5000.toml.
cd frontend
npm install
npm run devThe Vite dev server runs on http://127.0.0.1:5173 and proxies /api to the backend on port 8000.
- Dev UI:
http://127.0.0.1:5173 - Backend API docs:
http://127.0.0.1:8000/api/docs - Production-style UI after a frontend build:
http://127.0.0.1:8000
On a fresh install, the root app flow sends you through /setup first so you can create the single local admin account.
cd backend
.venv/bin/python -m pytest
cd ../frontend
npm run buildSPF5000 keeps startup and runtime wiring in spf5000.toml. Application settings such as slideshow timing, selected collection, bootstrap completion, admin credentials, the display sleep schedule, and the optional display timezone live in DecentDB.
Example config:
[server]
host = "0.0.0.0"
port = 8000
debug = false
[logging]
level = "INFO"
[security]
# session_secret = "replace-with-a-long-random-string"
[paths]
data_dir = "./backend/data"
cache_dir = "./backend/cache"
database_path = "./backend/data/spf5000.ddb"
[providers.google_photos]
# Google Photos Ambient API OAuth client for TVs and limited-input devices.
client_id = "your-google-client-id"
client_secret = "your-google-client-secret"
provider_display_name = "Google Photos"
sync_cadence_seconds = 3600Important notes:
- Set
SPF5000_CONFIGto use a different runtime config path. - Set
security.session_secretif you want admin sessions to survive backend restarts. - If
security.session_secretis omitted, SPF5000 generates an ephemeral secret and admin sessions are invalidated on restart. - Sleep scheduling and the optional display timezone are managed in-app and stored in DecentDB, not in
cron,systemd, Chromium flags, orspf5000.toml. - Google Photos credentials live in
spf5000.toml, while linked-account state, selected media sources, sync runs, and provider asset mappings live in DecentDB. - Google Photos playback stays offline-first: the frame syncs media into managed local storage and
/displayplays from the local cache. - Weather settings, cached conditions, active alerts, and refresh history live in DecentDB-backed application state rather than the runtime config file.
By default, the backend manages these paths:
backend/data/spf5000.ddbbackend/data/fallback/empty-display.jpgbackend/data/sources/local-files/import/backend/data/sources/google-photos/import/backend/data/staging/imports/backend/data/staging/google-photos/backend/data/storage/originals/backend/data/storage/variants/display/backend/data/storage/variants/thumbnails/
The backend currently recognizes:
.jpg.jpeg.png.webp.gif
SPF5000 is designed around a browser-kiosk runtime on Raspberry Pi OS Desktop.
Recommended hardware and OS:
- Raspberry Pi 3 or Raspberry Pi 4
- Raspberry Pi OS with Desktop
The supported appliance flow is:
- the Pi boots into the desktop automatically
- the backend starts as a
systemdservice - Chromium launches in kiosk mode against local
/display - administrators manage the frame from another device on the LAN
sudo raspi-config
sudo raspi-config nonint do_blanking 1
sudo mkdir -p /opt
cd /opt
sudo git clone https://github.com/sphildreth/spf5000.git
sudo chown -R pi:pi /opt/spf5000
cd /opt/spf5000
sudo ./scripts/install-pi.sh --user pi
sudo ./scripts/doctor.sh --user pi
sudo rebootThen from another device on the LAN:
http://<pi-hostname-or-ip>:8000/setup
scripts/install-pi.sh automates:
- apt package installation for the supported Pi runtime
backend/.venvcreation and backend dependency installation- DecentDB binding installation from the matching source archive plus native-library download from the matching release bundle
frontend/distcreation- runtime
spf5000.tomlgeneration systemdservice installation and startup- Chromium autostart kiosk wiring
Useful companion scripts:
scripts/doctor.sh— validates service state, local health checks, display playlist/sleep state, Chromium availability, and kiosk wiringscripts/uninstall-pi.sh— removes appliance wiring while preserving data by default
For full appliance details, read:
Backend commands assume backend/.venv exists; activate it or use .venv/bin/python explicitly. Frontend commands assume cd frontend && npm install has already been run.
| Task | Command |
|---|---|
| Start backend | make backend |
| Start frontend dev server | make frontend |
| Run backend tests | make test |
| Run one backend test | cd backend && .venv/bin/python -m pytest tests/test_health.py::test_health |
| Build the frontend | cd frontend && npm run build |
| Preview the frontend build | cd frontend && npm run preview |
| Start backend via helper script | ./scripts/dev-backend.sh |
| Start frontend via helper script | ./scripts/dev-frontend.sh |
| Build frontend via helper script | ./scripts/build-frontend.sh |
When frontend/dist exists, FastAPI serves the built frontend directly. That lets a single backend process expose:
- the admin UI
- the setup/login flow
- the public
/displayroute - the API under
/api
/setup— first-run bootstrap flow/login— returning admin sign-in/admin— protected admin shell/display— public fullscreen slideshow route
- health and system status
- setup and auth/session
- doctor diagnostics and admin log viewing
- settings, display timezone, and sleep schedule
- weather settings, cached conditions, and alert status
- collections and assets
- backup, restore, and collection export workflows
- sources and local import
- display config, public playlist, and public weather/alert overlays
Browse the generated OpenAPI docs at:
http://127.0.0.1:8000/api/docs
backend/ FastAPI app, services, repositories, providers, tests
frontend/ React + TypeScript + Vite admin and display UI
design/ PRD, SPEC, ADR index, accepted architecture decisions
docs/ Raspberry Pi setup, backup/export, Google Photos, and installer guides
deploy/ systemd, autostart, and config templates
graphics/ project artwork and logos
scripts/ development and Pi appliance helper scripts
Design and operational docs live in-repo:
design/PRD.md— product goals and scopedesign/SPEC.md— technical specificationdesign/ADR.md— architecture decision record indexdesign/adr/— accepted and proposed ADRsCHANGELOG.md— release notes following Keep a Changelogdocs/PI_SETUP_GUIDE.md— end-to-end Pi setupdocs/BACKUP_AND_EXPORT_GUIDE.md— database backup/restore and collection media export workflowsdocs/GOOGLE_PHOTOS_GUIDE.md— Google Photos credential setup, connection flow, and sync behaviordocs/INSTALLER.md— installer, doctor, and uninstall details
SPF5000 1.0.0 is intentionally focused:
- Google Photos selection happens through Google's Ambient settings UI instead of an in-app full-library browser
- weather currently supports one configured location and the U.S. National Weather Service provider
- browser uploads support batch image ingestion into local collections
- database backup and restore cover DecentDB state only; original media still requires collection export or other filesystem migration
- destructive library management remains intentionally limited
- authentication is single-admin and local-account only
- the product is image-focused; video-first playback is not a goal for this version
Issues and pull requests are welcome.
Before making large changes:
- read
design/PRD.md,design/SPEC.md, anddesign/ADR.md - preserve the existing backend layering of routes, services, repositories, and providers
- keep frontend API access under
frontend/src/api/ - do not silently diverge from accepted ADRs
If your change affects runtime behavior, storage boundaries, provider behavior, display rendering, or other accepted architectural decisions, add a new ADR under design/adr/ instead of rewriting history away.
SPF5000 is licensed under the Apache License 2.0.
