# Scalable Multi-Monitor Kiosk A Docker Compose-based project designed to run multiple hardware-accelerated Chromium kiosk instances across multiple monitors (e.g., the dual HDMI ports of a Raspberry Pi 4). Each Chromium instance provides an HTTP API (inspired by the [balena browser block](https://github.com/balena-io-experimental/browser)) that allows you to dynamically control the browser's URL, refresh behavior, kiosk mode, and GPU settings on the fly. ## Architecture This project uses a robust **X11 Architecture** designed specifically to avoid the complexities of Virtual Terminals and Session Management inside headless Docker containers. 1. **`display-manager` Container**: Runs a central Xorg server with direct access to the GPU (`/dev/dri`). It exposes the X11 Unix socket to the other containers. 2. **`kiosk` Containers**: Individual, scalable containers running Node.js and Chromium. - **Resolution Independence**: On startup, each kiosk uses `xrandr` to dynamically detect the exact resolution and physical offset of its assigned monitor (e.g., `HDMI-1`). It then perfectly frames its Chromium window to that specific screen. ## Prerequisites - Docker and Docker Compose installed. - A host machine with KMS/DRM graphics capabilities (like a Raspberry Pi 4 running Debian/Raspberry Pi OS). - Multiple displays connected. ## Getting Started 1. **Clone the repository** and navigate to the root directory. 2. **Build and start the stack**: ```bash docker-compose up -d --build ``` 3. The display manager will initialize the X server, and the two kiosk instances will launch on their respective monitors. ## Scaling To add a third monitor (e.g., `HDMI-3`), simply add a new service to `docker-compose.yml`: ```yaml kiosk-3: build: ./kiosk restart: always depends_on: - display-manager ports: - "5013:5011" volumes: - x11-socket:/tmp/.X11-unix devices: - "/dev/dri:/dev/dri" environment: - DISPLAY=:0 - MONITOR=HDMI-3 - PORT=5011 - LAUNCH_URL=https://example.com ``` ## API Reference Each kiosk exposes an API on its mapped port (e.g., `5011` for HDMI-1, `5012` for HDMI-2). ### Status & Information * **`GET /ping`**: Returns `HTTP 200 OK` if the browser is ready. * **`GET /url`**: Returns the URL currently being displayed. * **`GET /flags`**: Returns the internal flags Chromium was started with. * **`GET /version`**: Returns the version of Chromium running. * **`GET /screenshot`**: Returns a `.png` screenshot of the X11 display. ### Navigation * **`POST /url`**: Sets the URL to be displayed (send as JSON or URL-encoded form data). * *Example*: `curl -X POST -d "url=https://wikipedia.org" http://localhost:5011/url` * *Note*: You can also pass `gpu=0|1` and `kiosk=0|1` alongside the URL. * **`POST /refresh`**: Reloads the current page. * **`POST /autorefresh/{interval}`**: Automatically refreshes the browser window every X seconds (send `0` to disable). ### Configuration * **`GET /kiosk`**: Returns `1` if kiosk mode is enabled, `0` if disabled. * **`PUT /kiosk/{value}`**: Enables (`1`) or disables (`0`) kiosk mode (restarts the browser). * **`GET /gpu`**: Returns `1` if GPU acceleration is enabled, `0` if disabled. * **`PUT /gpu/{value}`**: Enables (`1`) or disables (`0`) hardware acceleration (restarts the browser).