# Frambe
A lightweight, self-contained Docker web application that connects to your [Immich](https://immich.app/) server and displays photos in a beautiful full-screen slideshow — perfect for turning old tablets, spare screens, and Raspberry Pis into digital photo frames.
## ✨ Features
- **Immich API Integration** — Connects securely via API key (kept server-side)
- **Album Browser** — Select any album (owned or shared), random photos, or favorites only
- **Person / Face Support** — Display photos of a specific person via Immich's face recognition
- **Admin Dashboard** — Real-time WebSocket-based control panel for all connected frames
- **Admin Authentication** — Optional username/password login to protect the admin dashboard
- **REST API with Token Auth** — Control frames from Home Assistant, scripts, or external tools
- **URL-Based Zero-Touch Launch** — Skip the setup screen entirely with query parameters
- **Auto-Refresh** — Periodically checks for new photos added to the source album/person
- **Smooth Crossfade** — Double-buffered image transitions with configurable duration
- **Background Blur** — Blurred backdrop fills the space behind non-covering images
- **Clock & Date Overlay** — Always know the time at a glance
- **EXIF Info** — Shows photo location, date, and camera info
- **Progress Bar** — Subtle indicator of time until next photo
- **Touch Controls** — Tap left/right edges to navigate, centre to toggle overlay
- **Keyboard Controls** — Arrow keys, Space, F (fullscreen), I (info), Esc (exit)
- **Screen Wake Lock** — Prevents screen sleep on supported devices
- **Responsive** — Works on any screen size from phone to TV
- **Older Device Friendly** — Vanilla HTML/CSS/JS, no heavy frameworks
- **Docker Containerised** — Single container, minimal footprint
---
## 🚀 Deployment
### Prerequisites
- A running [Immich](https://immich.app/) server
- An Immich API key (see below)
- Docker and Docker Compose (recommended) — or Node.js 18+ for running from source
### 1. Get your Immich API Key
1. Open your Immich web interface
2. Click your profile picture → **Account Settings** → **API Keys**
3. Create a new key with `asset.read` and `album.read` permissions
4. Copy the key — you'll need it for the next step
### 2. Deploy with Docker Compose (recommended)
```bash
git clone https://gitea.hideawaygaming.com.au/jessikitty/frambe.git
cd frambe
```
Edit `docker-compose.yml` and set your `IMMICH_URL` and `IMMICH_API_KEY`:
```yaml
environment:
- IMMICH_URL=http://your-immich-server:2283
- IMMICH_API_KEY=your-api-key-here
```
Then start the container:
```bash
docker compose up -d
```
Frambe is now running at `http://your-server:3030`.
### 3. Deploy with Docker Run
If you prefer not to use Compose:
```bash
docker build -t frambe .
docker run -d \
--name frambe \
-p 3030:3000 \
-e IMMICH_URL=http://your-immich-server:2283 \
-e IMMICH_API_KEY=your-api-key \
--restart unless-stopped \
frambe
```
### 4. Run from Source (development)
```bash
git clone https://gitea.hideawaygaming.com.au/jessikitty/frambe.git
cd frambe
npm install
```
Create a `.env` file from the example:
```bash
cp .env.example .env
```
Edit `.env` with your Immich URL and API key, then start the server:
```bash
npm start
```
Frambe will be available at `http://localhost:3000`.
### Port Mapping
The internal server runs on port **3000**. The Docker Compose config maps this to external port **3030** by default. You can change this in `docker-compose.yml`:
```yaml
ports:
- "8080:3000" # Access Frambe on port 8080 instead
```
---
## 🔄 Upgrading
### Docker Compose (recommended)
```bash
cd frambe
git pull
docker compose build
docker compose up -d
```
Your configuration in `docker-compose.yml` is preserved — only the application code is rebuilt.
### Docker Run
```bash
cd frambe
git pull
docker stop frambe
docker rm frambe
docker build -t frambe .
docker run -d \
--name frambe \
-p 3030:3000 \
-e IMMICH_URL=http://your-immich-server:2283 \
-e IMMICH_API_KEY=your-api-key \
--restart unless-stopped \
frambe
```
### From Source
```bash
cd frambe
git pull
npm install
npm start
```
### Switching to a Specific Version
Frambe uses git tags for releases. To pin to a specific version:
```bash
git fetch --tags
git checkout v1.4.1 # Replace with desired version
docker compose build && docker compose up -d
```
To switch back to the latest:
```bash
git checkout main
git pull
docker compose build && docker compose up -d
```
### Upgrade Notes
- **All upgrades are non-destructive** — Frambe stores no persistent data on disk. All configuration is via environment variables.
- **No database migrations** — there is no database. Session tokens are in-memory and will reset on restart (users simply log in again).
- **Check the changelog below** before upgrading major versions for any new required environment variables.
---
## 🔑 Authentication
### Admin Dashboard Login
Protect the admin dashboard with a username and password:
```yaml
environment:
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=your-secure-password
```
When `ADMIN_PASSWORD` is set, accessing `/admin` requires signing in. When not set, the dashboard is open (useful for trusted local networks).
### API Token for External Access
Enable token-authenticated REST API access for Home Assistant, scripts, or other external tools:
```yaml
environment:
- FRAMBE_API_TOKEN=your-secret-token-here
```
---
## 🔌 REST API
When `FRAMBE_API_TOKEN` is configured, the following endpoints are available:
### List Connected Frames
```
GET /api/clients
Authorization: Bearer your-secret-token-here
```
Returns all connected frame clients with their status, IP, name, and config.
### Send Command to a Frame
```
POST /api/clients/:id/command
Authorization: Bearer your-secret-token-here
Content-Type: application/json
{
"action": "next",
"payload": {}
}
```
Available actions: `start`, `stop`, `next`, `prev`, `sleep`, `wake`, `refresh`, `setSource`, `setConfig`
### Home Assistant Example
```yaml
rest_command:
frambe_next_photo:
url: "http://frambe-server:3030/api/clients/{{ client_id }}/command"
method: POST
headers:
Authorization: "Bearer your-secret-token-here"
Content-Type: "application/json"
payload: '{"action": "next"}'
```
Authentication can also be provided via `x-api-token` header or `?token=` query parameter.
---
## 🔗 Zero-Touch URL Parameters
Skip the setup screen entirely by passing query parameters. This is ideal for dedicated frames — just bookmark the URL on each tablet:
| URL | What it shows |
|---|---|
| `http://server:3030/?album=ALBUM_UUID` | Photos from a specific album |
| `http://server:3030/?person=PERSON_UUID` | Photos of a specific person (face recognition) |
| `http://server:3030/?favorites` | Favorite photos only |
| `http://server:3030/?random` | Random photos from the library |
You can find album and person UUIDs in Immich's web interface URL bar when viewing an album or person.
---
## ⚙️ Configuration
All settings are via environment variables. Set them in `docker-compose.yml`, pass with `docker run -e`, or put them in a `.env` file when running from source.
| Variable | Default | Description |
|---|---|---|
| `IMMICH_URL` | *(required)* | Your Immich server URL |
| `IMMICH_API_KEY` | *(required)* | Immich API key |
| `SLIDESHOW_INTERVAL` | `30` | Seconds between photos |
| `TRANSITION_DURATION` | `2` | Crossfade duration in seconds |
| `IMAGE_FIT` | `contain` | `contain` or `cover` |
| `SHUFFLE` | `true` | Randomise photo order |
| `BACKGROUND_BLUR` | `true` | Show blurred backdrop |
| `SHOW_CLOCK` | `true` | Display clock overlay |
| `SHOW_DATE` | `true` | Display date overlay |
| `SHOW_EXIF` | `true` | Display photo metadata |
| `SHOW_PROGRESS` | `true` | Display progress bar |
| `INCLUDE_VIDEOS` | `true` | Include video assets in slideshow |
| `REFRESH_INTERVAL` | `300` | Seconds between source refresh checks (new photos) |
| `ALBUM_ID` | *(empty)* | Auto-start with specific album (env-based) |
| `SHOW_FAVORITES_ONLY` | `false` | Auto-start with favorites (env-based) |
| `ADMIN_USERNAME` | `admin` | Admin dashboard login username |
| `ADMIN_PASSWORD` | *(empty)* | Admin dashboard password (leave empty to disable auth) |
| `FRAMBE_API_TOKEN` | *(empty)* | API token for REST endpoint access (leave empty for open access) |
| `PORT` | `3000` | Internal server port (Docker maps externally via compose) |
---
## 🎮 Controls
### Touch / Mouse
- **Left 20%** of screen — Previous photo
- **Centre 60%** — Toggle overlay (clock, info, close button)
- **Right 20%** — Next photo
### Keyboard
- `←` / `→` — Previous / Next photo
- `Space` — Next photo
- `F` — Toggle fullscreen
- `I` — Toggle info overlay
- `Esc` — Exit to album selection
---
## 📱 Tablet Setup Tips
1. Open the frame URL in your tablet's browser (use a `?album=` or `?person=` URL for zero-touch)
2. Add to Home Screen for a full-screen app experience
3. Enable kiosk mode or guided access to lock to the app
4. Disable screen timeout in your device settings
---
## 🏗️ Architecture
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Browser │ HTTP │ Frambe │ API │ Immich │
│ (Tablet) │◄────────►│ (Node.js) │◄────────►│ Server │
└──────────────┘ :3030 └──────────────┘ :2283 └──────────────┘
▲
┌─────────┴─────────┐
│ REST API / WS │
│ (Home Assistant) │
└───────────────────┘
```
The Node.js backend acts as a secure proxy — your Immich API key never reaches the browser. The frontend periodically polls the backend for new photos so albums stay up to date without restarting.
---
## 🏷️ Versioning
Frambe follows [Semantic Versioning](https://semver.org/):
- **Major** (`X.0.0`) — Large feature overhauls, breaking changes, or major UI redesigns
- **Minor** (`0.X.0`) — New features, functionality additions, or significant improvements
- **Patch** (`0.0.X`) — Bug fixes, small tweaks, and minor corrections
### Branches
| Branch | Purpose |
|---|---|
| `main` | Stable releases — production-ready code |
| `dev` | Development — latest features, may be unstable |
### Changelog
#### v1.4.1 — Shared Albums
- ✅ Album picker now shows **shared albums** alongside owned albums
- ✅ Shared albums are visually marked with a "Shared" badge in the picker
- ✅ Shared album icons use 🔗 to distinguish from owned 📁 albums
- ✅ Deduplication ensures albums shared with yourself don't appear twice
#### v1.4.0 — Admin Auth & REST API
- ✅ Admin dashboard login with username/password authentication (env-based)
- ✅ Session management with HttpOnly cookies (24-hour expiry, automatic cleanup)
- ✅ API token authentication for external access (Home Assistant, scripts, curl)
- ✅ REST endpoint: `GET /api/clients` — list all connected frames
- ✅ REST endpoint: `POST /api/clients/:id/command` — send commands to frames
- ✅ Multiple auth methods: Bearer token, `x-api-token` header, `?token=` query param
- ✅ Auth status endpoint: `GET /api/auth/status`
- ✅ Backwards compatible — auth is opt-in, disabled by default
- 🆕 New env vars: `ADMIN_USERNAME`, `ADMIN_PASSWORD`, `FRAMBE_API_TOKEN`
#### v1.3.0 — Admin Dashboard & Video Support
- ✅ Real-time admin dashboard at `/admin` with WebSocket communication
- ✅ Live frame management: start, stop, next, prev, sleep, wake, refresh
- ✅ Remote source switching: change album, person, random, or favorites per frame
- ✅ Remote config: adjust slideshow interval, toggle clock/date/EXIF/progress per frame
- ✅ Frame naming and rename support (persists by IP)
- ✅ Video playback support in slideshow (with `INCLUDE_VIDEOS` toggle)
- ✅ Person / face recognition photo source via Immich's people API
- ✅ Connection status indicators and auto-reconnect
#### v1.2.1 — Bug Fixes
- 🐛 Fixed port mapping (3030:3000 external:internal)
- 🐛 Fixed URL parameter auto-launch not starting the slideshow
#### v1.2.0 — Zero-Touch Launch & Auto-Refresh
- ✅ URL query parameters for zero-touch launch (`?album=`, `?person=`, `?favorites`, `?random`)
- ✅ Person / face support — display photos of a specific person
- ✅ Periodic auto-refresh — new photos appear without restarting
- ✅ App icon for home screen bookmarks
- ✅ Default external port changed to 3030
- 🆕 New env var: `REFRESH_INTERVAL`
#### v1.1.0 — Rebrand
- ✅ Rebranded to Frambe
#### v1.0.0 — Initial Release
- ✅ Album browser with Immich API integration
- ✅ Full-screen slideshow with smooth crossfade transitions
- ✅ Double-buffered image loading for seamless display
- ✅ Background blur behind non-covering images
- ✅ Clock, date, and EXIF metadata overlays
- ✅ Progress bar showing time until next photo
- ✅ Touch controls (left/centre/right tap zones)
- ✅ Keyboard controls (arrows, space, F, I, Esc)
- ✅ Screen wake lock to prevent display sleep
- ✅ Configurable via environment variables
- ✅ Docker containerised deployment
- ✅ Vanilla HTML/CSS/JS frontend — no frameworks, works on older devices
---
## 📄 License
MIT