Self-contained digital signage player. One Python process, one browser tab, zero cloud dependencies.
Upload images and videos through a web CMS, arrange them into playlists, and a fullscreen browser player loops through them with smooth fade transitions. Everything runs on a single machine -- a Raspberry Pi under a TV, an old PC behind a monitor, or a Docker container on whatever you have.
- Drag-and-drop media management -- upload images, videos, web URLs, and HTML snippets
- Visual Page Designer -- WYSIWYG slide builder with text, images, shapes, live widgets (clock, weather, countdown, scrolling text), and 5 starter templates -- no HTML required
- Playlist editor -- drag to reorder, per-item duration, shuffle, preview
- Smooth playback -- dual-layer CSS fade transitions, GPU-composited
- Offline resilient -- player caches everything locally and keeps playing if the network drops
- First-boot setup wizard -- browser-based, no config files to hand-edit
- Runs anywhere -- Raspberry Pi, Linux, macOS, Windows, Docker
- Multi-device management -- register devices, monitor health, group them, bulk-assign playlists
- Scheduling -- time windows, day-of-week, RRULE recurrence, priority system
- Multi-zone layouts -- split screens into positioned zones with independent playlists
- Interactive triggers -- keyboard, touch zones, GPIO buttons, webhooks, timeouts -- link playlists with trigger-driven transitions for kiosks, wayfinding, and emergency alerts
- Emergency overrides -- instant message or playlist push to all/group/device, auto-expiry
- Role-based access -- admin, editor, viewer roles with API tokens
- Optional HTTPS -- one-click enable in the setup wizard, auto-generates a self-signed certificate, or bring your own PEM files
- Structured logging -- backend error log, player persistent log with remote retrieval, audit trail
- Backup and restore -- one-click ZIP export of database and media
- No cloud account required. No sign-up. No telemetry. No phoning home.
- No online activation, no license key, no "connect to our servers to unlock your device." It works on a network with no internet at all.
- No feature gates or crippled "free tier" -- this is the complete application.
git clone https://github.com/freewarefocus/tinysignage.git
cd tinysignage
docker compose up -dOpen http://localhost:8080/setup to create your admin account, then /cms to manage content and /player for the display.
New here? Follow the Getting Started guide for a full walkthrough.
| Method | Guide |
|---|---|
| Docker | Install with Docker |
| Raspberry Pi | Install on Raspberry Pi |
| Windows | Install on Windows |
| macOS | Install on macOS |
Multiple screens? The installer lets you choose what to install: CMS + Player together on one device, CMS only (central server), or Player only (display screen pointing at a remote CMS). See the Raspberry Pi guide for a walkthrough of multi-screen setups.
Browser (player) -- poll every 30s --> FastAPI backend --> SQLite
Browser (CMS) -- REST API --------> |
Device watchdog (marks offline devices)
watchdog_process.py -- GET /health ---> Monitors CMS + browser
Restarts crashed processes
Memory limit enforcement
A single FastAPI process serves the API, CMS, player, and media files. SQLite is the only database. The player polls for changes, caches everything locally, and manages its own playback timer -- no WebSocket, no server-push. An independent process watchdog monitors the CMS and browser from the outside, restarting them if they crash or exceed memory limits. See Architecture for details.
TinySignage uses a 3-tier cascade for display settings: global defaults (Settings page) can be overridden per playlist (playlist settings panel), which can be overridden per item (inline controls on each playlist row). Leaving a value as "Default" at any level inherits from the tier above.
| Setting | Options | Controls |
|---|---|---|
| Effect (transition) | Fade, Slide, Cut | How items transition in |
| Fade time (transition duration) | 0 -- 30 seconds | How long the transition takes |
| Show for (display duration) | 1 -- 3600 seconds | How long an image/HTML item stays on screen (videos play to end) |
| Scaling (object-fit) | Fit inside, Fill & crop, Stretch, Original size | How images and videos fill the display area |
The cascade for scaling (object_fit): per-item value wins, then playlist-level default, then global default. The global default is contain (fit inside, may show black bars).
TinySignage never fails silently. Every error surfaces through two channels: user-facing notifications and a persistent debug log.
| What you need | Where to look |
|---|---|
| Server errors (500s, crashes) | CMS > System Log, or logs/errors.jsonl, or GET /api/logs/errors |
| Player issues (poll failures, asset load errors) | Press Ctrl+Shift+D on the player for the debug overlay, or GET /api/devices/{id}/player-log for remote access |
| Who changed what (asset deleted, playlist modified) | CMS > Audit Log, or GET /api/audit |
| Watchdog activity (restarts, memory snapshots) | logs/watchdog.log, or journalctl -u signage-watchdog (Pi/Linux) |
| Failed login attempts | Audit Log (action: auth_failed) |
Player logs are especially useful for headless devices (Raspberry Pi, kiosk). The player stores a 200-entry ring buffer locally and uploads it to the server on each heartbeat, so you can debug remotely without physical access.
TinySignage runs on anything with a browser and a screen. No proprietary hardware required.
Tested reference hardware (Raspberry Pi 5):
| Component | Approx. cost |
|---|---|
| Raspberry Pi 5 (4GB) | ~$85 |
| Power supply | ~$15 |
| Case with heatsink | ~$15 |
| Micro HDMI cable | ~$10 |
| 16GB SD card | ~$10 |
| Total | ~$135 |
Also works on: any x86 mini PC, retired office PC, Mac, Linux server, or Docker host.
| Guide | Description |
|---|---|
| Getting Started | Zero-to-content in 10 minutes |
| Install with Docker | Docker-specific setup and management |
| Install on Raspberry Pi | Dedicated kiosk display |
| Install on Windows | Local application on Windows |
| Install on macOS | Local application on macOS |
| Managing Media | Uploads, tags, HTML snippets, widgets |
| Page Designer | Visual builder for custom slides with text, images, shapes, and live widgets |
| Playlists | Creation, editing, per-playlist settings |
| Devices | Registration, health monitoring, groups |
| Scheduling | Time windows, recurrence, priority |
| Multi-Zone Layouts | Split-screen zone positioning |
| Interactive Triggers | Keyboard, touch, GPIO, webhook triggers |
| BrightSign Support | BrightSign player integration and MRSS feeds (experimental) |
| GPIO Bridge | Physical buttons on Raspberry Pi |
| Emergency Overrides | Instant override push and auto-expiry |
| Users and Permissions | RBAC roles, API tokens, sessions |
| Backup and Restore | ZIP export and import |
| Configuration | config.yaml reference |
| Player Behavior | Polling, caching, offline mode, transitions, persistent logging |
| Troubleshooting | Log locations, common issues, and fixes |
| Architecture | System design, project layout, dependencies |
| API Reference | All endpoints with examples |
| Contributing | Dev setup, conventions, PR guidelines |
TinySignage is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
You are free to use, modify, and distribute TinySignage. If you run a modified version as a network service, the AGPL requires you to make your source code available to users of that service. Self-hosting for your own displays -- modified or not -- requires no special action beyond retaining the license notice.