Building a Low-Cost Digital Signage System with Modern Web Technologies

Digital signage has evolved from static posters to dynamic, centrally managed displays. The idea is simple: use a network of screens to show targeted ads in places where people wait—lobbies, elevators, checkout lines. Back in the day, folks used old PCs and stripped-down Windows to run a browser in kiosk mode. Today, we can do it better, cheaper, and with way less power using a Raspberry Pi and modern web technologies.

The Core Concept

A small computer runs a web browser that points to a central server. The server delivers a webpage with rotating ads. Update the server, and all displays update instantly. No need to touch each machine.

Modern Hardware

Forget the Pentium III and compact flash. A Raspberry Pi 4 (or even a Zero 2 W) with a microSD card and a simple HDMI monitor is more than enough. It sips power and fits behind the screen. Add a cheap USB Wi-Fi or Ethernet dongle—done.

Modern Software

Raspberry Pi OS Lite (no desktop overhead) + Chromium in kiosk mode. Just add a systemd service to start the browser at boot and force fullscreen. Here’s a quick startup script:

#!/bin/bash
chromium-browser --kiosk --noerrdialogs --disable-session-crashed-bubble --disable-infobars --check-for-update-interval=604800 http://yoursite.xisto.com/signage/

Centralized Content Server

Host the signage page on Xisto (free cPanel hosting with 1GB space). The page uses HTML5, CSS Grid, and a bit of JavaScript to rotate ads. You can divide the screen into sections: a large main area, smaller quarter ads, half-width banners, etc. Update the images on the server, and every display gets them instantly.

Modular Layout with CSS Grid

Here’s a modern, responsive layout for a 4-zone signage page (top-left, top-right, bottom-left, bottom-right):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Digital Signage</title>
<style>
  body, html { margin:0; height:100%; overflow:hidden; }
  .grid-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    height: 100vh;
    gap: 0;
  }
  .grid-item {
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  .grid-item img { width:100%; height:100%; object-fit:contain; }
</style>
</head>
<body>
<div class="grid-container">
  <div class="grid-item" id="tl"><img src="https://yoursite.xisto.com/ads/tl.jpg" alt="Top Left"></div>
  <div class="grid-item" id="tr"><img src="https://yoursite.xisto.com/ads/tr.jpg" alt="Top Right"></div>
  <div class="grid-item" id="bl"><img src="https://yoursite.xisto.com/ads/bl.jpg" alt="Bottom Left"></div>
  <div class="grid-item" id="br"><img src="https://yoursite.xisto.com/ads/br.jpg" alt="Bottom Right"></div>
</div>
<script>
// Simple rotation: change image source every 10 seconds
const ads = {
  tl: ["ad1.jpg", "ad2.jpg"],
  tr: ["ad3.jpg"],
  bl: ["ad4.jpg", "ad5.jpg", "ad6.jpg"],
  br: ["ad7.jpg"]
};
const zones = ['tl','tr','bl','br'];
let idx = {tl:0, tr:0, bl:0, br:0};
setInterval(() => {
  zones.forEach(zone => {
    const arr = ads[zone];
    if(arr.length > 1) {
      idx[zone] = (idx[zone] + 1) % arr.length;
      document.getElementById(zone).querySelector('img').src = "https://yoursite.xisto.com/ads/" + arr[idx[zone]];
    }
  });
}, 10000);
</script>
</body>
</html>

Why This Works

  • Low cost: A Pi and a monitor can run 24/7 on ~$50 total hardware.
  • Centralized management: Update ads via Xisto’s file manager or FTP.
  • Scalable: Add as many Pi clients as you want; all pull from the same URL.
  • No licensing fees: Open-source OS and browser.

I’m testing this with a Pi 4 and a 22" monitor. The boot-to-kiosk takes about 30 seconds. For emergency recovery, the systemd service reboots the browser if it crashes. Planning to add a watchdog timer.

Next Steps

I’ll build a demo page here on Xisto and share the link. Also experimenting with dynamic content (weather, news feeds) via JavaScript. Any thoughts on using WebSockets for real-time updates? Or should simple polling suffice?

All feedback is welcome. Let’s discuss the pros and cons of different hardware (e.g., Pi vs. thin clients) and content management strategies.

Topic Summary: Build a low-cost digital signage system using Raspberry Pi, Chromium kiosk mode, and modern web tech like PWAs and SSE. Centralized content management via a headless CMS; real-time updates via SSE; hardware options include Pi 5 or thin clients. Scalable, secure, and open-source.

:hammer_and_wrench: Featured GitHub Resource:

  • Screenly/Anthias - The world’s most popular open source digital signage project. (★ 3622)

:open_book: Topic Overview (Wikipedia):

Digital signage is a segment of electronic signage that uses digital display technologies to present multimedia content in both public and private environments. Content may include video, images, text, or interactive media and is typically displayed for purposes such as advertising, information dissemination, branding, or entertainment.
Read more on Wikipedia

:movie_camera: YouTube Video:

There’s a lot of solid groundwork laid out here for a DIY digital signage solution. The approach of using a Raspberry Pi with Chromium kiosk mode is tried and true, but modern web technologies can take it further without complicating the core simplicity.

Progressive Web App Enhancements

Instead of a plain HTML page, you can wrap the signage interface as a Progressive Web App (PWA). This gives you offline capabilities via a service worker, so if the network drops, the last loaded ads still play. It also allows for background sync when connectivity returns. PWAs can be ‘installed’ on the Pi’s Chromium, which will remove the URL bar even without kiosk flags, simplifying the startup script.

Real-time vs Polling Updates

The thread mentions WebSockets vs polling. For a signage system, polling every 10-30 seconds is perfectly fine and simpler to implement. WebSockets add overhead for potentially zero gain if content only changes once per day. That said, if you want to push urgent messages (e.g., emergency alerts) instantly, WebSockets or Server-Sent Events (SSE) are more appropriate. SSE is easier to implement on the server side in PHP (which Xisto supports) and works with standard HTTP.

Content Management Approaches

You’re not limited to static files. A lightweight headless CMS like Strapi or even a simple JSON file on the server can drive what displays. For instance, store ad metadata in a JSON array, and the signage page fetches that JSON every cycle. This way you can change sequences, durations, and zones without touching HTML. Pair this with a cron job on the server to rotate or expire ads automatically.

Modern Hardware Considerations

While Pi 4 is adequate, the Pi 5 offers significantly faster GPU and memory bandwidth. For 4K displays or many zones, the Pi 5 handles video playback smoothly. Alternatively, for sheer reliability, used enterprise thin clients (e.g., Dell Wyse) with x86 processors and SSDs can be found for similar cost – they boot faster and have better driver support for some displays.

Security and Management

Put the Pi on a dedicated VLAN if possible. Use iptables or ufw to block all inbound traffic except HTTP/HTTPS outbound. Add a watchdog timer (a hardware watchdog or systemd WatchdogSec) to reboot the Pi if the browser freezes. For fleet management, tools like balena or Ansible can update multiple Pis simultaneously.

Broader Implications

Modern digital signage is no longer just static images. With WebGL and Canvas, you can render dynamic data visualizations, live social media feeds, or interactive touchscreen polls. The same low-cost hardware can run a full-screen WebGL dashboard showing real-time metrics from an API. This opens up opportunities beyond advertising – think information kiosks, factory floor displays, or restaurant menu boards that update with inventory levels. The barrier to entry is now essentially zero, which democratizes digital signage for small businesses and community organizations.