Zoneminder - Setup a simple and fast Camera Wall


dropcap zoneminder

Two years back, I had setup a ZoneMinder installation for some office building with almost 40 IP cameras.

This setup is has been running quite stable since installation time. But, once the system was up and running, I was asked to setup a big camera wall including thumbnails of all the cameras.

After reading some forum posts, It appeared that ZoneMinder montage functionnality is not optimised for these camera walls in term of performance. All posts where asking to create a simple web page to handle the camera wall.

As I did not find any ZoneMinder based web camera wall project, I decided to write one.

This article explains how to setup a very simple yet flexible web camera wall based on a ZoneMinder camera setup :

  • it is served by a simple Apache/PHP server
  • it uses ZoneMinder database and API to extract the camera list & generate thumbnails
  • it handles images display and refresh with some simple CSS and Javascript code
  • it allows to zoom on a specific image on the wall

It has been designed to work on any modern web browser and to use minimum network bandwith to be compatible with a internet usage.

It has been tested on a Ubuntu 16.04 LTS server with ZoneMinder 1.30.4. It has been used with Firefox 56 & Chromium 3x. It should be usable on many other server and client environments.

1. Functionnalities

This web camera wall main principles are :

  • camera wall is a grid where you can adjust the number of rows and columns
  • camera wall setup is managed thru URL parameters
  • you can setup wall resolution
  • Zoneminder label is displayed as a tag for every camera
  • camera alert status is retrieved from ZoneMinder and displayed thru label background color code
  • wall can display a subset of cameras determined by ZoneMinder configuration
  • it can also display a specific list of cameras
  • any camera disabled in ZoneMinder will be ignored by the camera wall
  • to minimise data traffic, camera thumbnailing is handled by ZoneMinder

On the camera wall, thumbnails are refreshed one by one sequentially. Refresh rate will vary according to your network speed.

zoneminder camera wall

In case of movement detection, label background becomes red.

zoneminder camera wall movement

Every camera on the wall can be zoomed with a simple click on its thumbnail. Zoom feature is handled by FancyZoom library. Any zoomed image is also refreshed automatically according to your network speed.

zoneminder camera wall zoom

The camera wall scripts should be hosted if possible on the same server as ZoneMinder, if not on a server on the same LAN.

Scripts are using ZoneMinder APIs and get credentials thru a standard ZoneMinder account.

2. Usage

Camera wall is called thru camera-wall.php

Here are the parameters available :

Parameter   Description    Default
 row   Number of rows on the wall  6
 column   Number columns on the wall  7
 width   Maximum witdh of the camera wall (in pixels)  1920
 height   Maximum height of the camera wall (in pixels)  1080
 zoom   Height of the zoomed image (in pixels)  900
 index  Index of first camera to display (according to zm sequence)  1
 cams  List of cameras to display (indexes separated with -)  


You should set row and column according to the number of cameras you want to display on the camera wall.

If your wall is accessible under http://your.server.url/wall, here are some calling examples :

3. Camera wall scripts


This file is the camera wall configuration file.

It contains informations about :

  • your zoneminder server
  • the credentials you will use for the wall
  • the wall name as it will be displayed in your browser

Please note that it is strongly suggested to create a specific user for the camera wall in your zoneminder setup.

This user will be used to provide credentials to the camera wall. It should have read access to all the cameras of the wall.

// -------------------------------------------------------
//        Credentials for ZoneMinder camera wall
// -------------------------------------------------------

// zoneminder server URL
$zmURL  = "";

// zoneminder credentials
$zmUser = "youruser";
$zmPass = "yourpassword";

// Wall name
$strWallName = "Your wall title";

3.2. cam-image.jpeg.php

This script is called by the main script to display individual camera on the wall.

It is in charge of retrieving and displaying a camera thumbnail with its zoneminder index and a display scale factor.

Image is retrieved thru ZoneMinder API with the camera wall credentials and served as a image/jpeg stream.

This script is used by FancyZoom library, which imposes to have .jpeg in the picture name. Only files with .jpeg are detected as pictures by FancyZoom. That's why the script name ends with .jpeg.php

// -------------------------------------------------------
//      Webcam image display from ZoneMinder
// Revision history :
//   10/11/2017 - V1.0 - Creation from N. Bernaerts
// -------------------------------------------------------

// zoneminder configuration
require_once ("");

// parameters : image index
$monitorId = -1;
if (isset($_GET["id"])) $monitorId = $_GET["id"];

// parameter : image scale (in % between 1 and 100)
$monitorScale = 0;
if (isset($_GET["scale"])) $monitorScale = $_GET["scale"];

// calculate hash login ($zmSecret . $zmUser . $zmPass . $arrTime['tm_hour'] . $arrTime['tm_mday'] . $arrTime['tm_mon'] . $arrTime['tm_year'])
$arrTime = localtime();
$authKey = $zmSecret . $zmUser . $zmPass . $arrTime[2] . $arrTime[3] . $arrTime[4] . $arrTime[5];
$authHash = md5 ($authKey);

// generate zoneminder URL
$imgURL = $zmURL . "/cgi-bin/zms?mode=single&monitor=" . $monitorId . "&scale=" . $monitorScale . "&user=" . $zmUser . "&pass=" . $zmPass;

// set jpeg header
header("Content-type: image/jpeg");

// get image
$imgData = file_get_contents($imgURL);
echo $imgData; 

3.3. cam-status.php

This script is in charge of retrieving camera status (idle, alert or post-alert) using zoneminder API.

According to the camera status, it sends an HTML color code :

  • white : no alert
  • red : alert detected
  • orange : post-alert

This color is used as the camera tag background color.

// -------------------------------------------------------
// Webcam detection status
// Parameters :
//   index - Index of cam on the wall
// Revision history :
//   15/05/2018 - V1.0 - Creation by N. Bernaerts
// -------------------------------------------------------

// zoneminder configuration
require_once ("");

// Parameters
$camIndex = $_GET["index"];

// recover cam array thru saved cookie
$arrCamCookie = unserialize($_COOKIE['cams']);

// retrieve zoneminder cam id from cookie array
$camID = $arrCamCookie['cam'][$camIndex];

// retrieve zoneminder session id from cookie array
$sessionID = $arrCamCookie['session'];

// get cam status thru zoneminder
$ch = curl_init ();
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_URL, $zmURL . "/api/monitors/alarm/id:" . $camID . "/command:status.json");
curl_setopt ($ch, CURLOPT_HTTPHEADER, array("Cookie: " . $sessionID));
$json = curl_exec($ch);
curl_close ($ch);

// convert json to array
$arrResult = json_decode ($json, true);

// display status
switch ($arrResult["status"]) {
    case 0: echo "#FFFFFF"; break;
    case 2: echo "#FF0000"; break;
    case 3: echo "#FFA500"; break;



This zip file contains the complete FancyZoom version 1.1 library files.

It should be unzipped in the web folder of the other camera wall files, under js-global sub-directory.

Do not hesitate to go on FancyZoom site to check how this fantastic library works.

3.5. cam-wall.php

This is the main script in charge of displaying the camera wall.

When called, this script runs with 2 stages :

  1. an initialisation phase
  2. an infinite display refresh loop

Initialisation phase is sequenced according to these steps :

  1. get camera list with their specs from ZoneMinder server
  2. sort cameras according to ZoneMinder defined sequence
  3. determine which camera should be displayed according to the wall setup (number of cameras according to the number of rows and columns, first camera index, list of cameras …)
  4. calculate for each camera the thumbnail scaling ratio according to its original size
  5. start display infinite loop

Infinite loop retrieves camera image and status sequentially to avoid any stress on ZomeMinder server. Every time a camera image is fully downloaded, next camera image refresh starts.

With this sequential approach, you get a very fast refresh rate on a fast LAN and the refresh rate adjust to your network speed when you are connected over an internet connexion.

To avoid any browser cache problem, a timestamp is added to every camera image URL. This way, during the refresh cycle, your browser won't try to use a previously cached image.

In case a camera is zoomed, another refresh loop is started to only update the zoomed image.

4. Installation

All camera wall scripts should be placed in a directory, directly accessible.

An installation script is available from my GitHub repository.

Here are the commands to install the complete camera wall environment under existing /var/www/html/wall directory :

# wget
# chmod +x
# ./ /var/www/html/wall
# rm

Once installation is done, you need to configure according to your ZoneMinder server setup.

Your Zoneminder camera wall is now ready.


 Hope it helps !

Signature Technoblog

This article is published "as is", without any warranty that it will work for your specific need.
If you think this article needs some complement, or simply if you think it saved you lots of time & trouble,
just let me know at This email address is being protected from spambots. You need JavaScript enabled to view it.. Cheers !

icon linux icon debian icon apache icon mysql icon php icon piwik icon googleplus