Skip to main content

Developer & Open Source Guide

MediaSignage provides extensive open-source tools and SDKs for developers who want to customize, extend, or white-label digital signage solutions. This guide covers the development ecosystem, from simple component creation to full platform customization.

Development Ecosystem Overview

Available Open Source Projects

ProjectLicensePurposeTechnologies
StudioLiteGPL V3Lightweight CMSAngular, TypeScript, RxJS
Pepper SDKGPL V3Core signage libraryJavaScript, HTML5
msGetStartedGPL V3Account creation wizardBootstrap, Backbone.js
Component SDKGPL V3Custom component devHTML5, CSS, JavaScript
StudioDashboardGPL V3Enterprise dashboardAngular, Redux

Development Stack

┌─────────────────────────────────────────────────────────────────────────────┐
│ Digital Signage Development Stack │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Frontend (Studio/CMS) │
│ ├── Angular Framework │
│ ├── TypeScript │
│ ├── RxJS (Reactive Programming) │
│ ├── NgRx (State Management) │
│ └── Bootstrap / Custom CSS │
│ │
│ Player Runtime │
│ ├── HTML5 / CSS3 │
│ ├── JavaScript (ES6+) │
│ ├── Web Components │
│ └── Canvas / WebGL (animations) │
│ │
│ Backend Services │
│ ├── Node.js │
│ ├── RESTful APIs │
│ ├── WebSocket (real-time) │
│ └── SignService (local player API) │
│ │
│ SDKs & Libraries │
│ ├── RedPepper SDK (authentication) │
│ ├── YellowPepper SDK (server communication) │
│ └── Component SDK (custom widgets) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

StudioLite - Open Source CMS

Overview

StudioLite is a fully open-source digital signage content management system built with Angular. It provides approximately 90% of SignStudio's functionality and can be freely forked, customized, and rebranded.

Repository: github.com/born2net/studioDashboard

Getting Started

# Clone the repository
git clone https://github.com/born2net/studioDashboard.git
cd studioDashboard

# Install dependencies
npm install

# Start development server
npm start

# Build for production
npm run build

# Run tests
npm test

Project Structure

studioDashboard/
├── src/
│ ├── app/
│ │ ├── campaigns/ # Campaign management
│ │ ├── scenes/ # Scene editor
│ │ ├── resources/ # Media library
│ │ ├── timeline/ # Timeline editor
│ │ ├── stations/ # Player management
│ │ ├── settings/ # User settings
│ │ └── shared/ # Shared components
│ ├── assets/
│ │ ├── images/
│ │ └── styles/
│ └── environments/
│ ├── environment.ts
│ └── environment.prod.ts
├── angular.json
├── package.json
└── README.md

Customization Examples

Branding / White-Label:

// src/environments/environment.ts
export const environment = {
production: false,
appName: 'Your Signage Platform',
companyName: 'Your Company',
logo: 'assets/images/your-logo.png',
primaryColor: '#3498db',
secondaryColor: '#2ecc71',
apiBaseUrl: 'https://your-api-server.com',
supportEmail: 'support@yourcompany.com'
};

Custom Theme:

// src/styles/custom-theme.scss
$primary-color: #3498db;
$secondary-color: #2ecc71;
$background-color: #1a1a2e;
$text-color: #ffffff;

// Override default styles
.navbar {
background-color: $primary-color;
}

.btn-primary {
background-color: $secondary-color;

&:hover {
background-color: darken($secondary-color, 10%);
}
}

Adding Custom Features:

// src/app/custom-feature/custom-feature.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomFeatureComponent } from './custom-feature.component';
import { CustomFeatureRoutingModule } from './custom-feature-routing.module';

@NgModule({
declarations: [CustomFeatureComponent],
imports: [
CommonModule,
CustomFeatureRoutingModule
]
})
export class CustomFeatureModule { }

Component SDK

Overview

The Component SDK enables creation of custom widgets and components for both SignStudio (configuration UI) and SignPlayer (runtime display).

Component Architecture

custom-component/
├── player/ # SignPlayer runtime
│ ├── index.html # Component HTML
│ ├── styles.css # Component styles
│ └── script.js # Component logic
├── studio/ # SignStudio configuration
│ ├── properties.html # Properties panel
│ └── properties.js # Properties logic
├── icon.png # Component icon (64x64)
├── manifest.json # Component metadata
└── README.md

Creating a Custom Component

Step 1: Create manifest.json

{
"name": "Weather Widget",
"id": "com.yourcompany.weather",
"version": "1.0.0",
"description": "Displays current weather conditions",
"author": "Your Company",
"website": "https://yourcompany.com",
"icon": "icon.png",
"category": "data",
"tags": ["weather", "data", "api"],
"properties": {
"apiKey": {
"type": "string",
"label": "API Key",
"required": true
},
"location": {
"type": "string",
"label": "Location",
"default": "New York, US"
},
"units": {
"type": "select",
"label": "Units",
"options": ["imperial", "metric"],
"default": "imperial"
},
"refreshInterval": {
"type": "number",
"label": "Refresh (minutes)",
"default": 15,
"min": 5,
"max": 60
}
}
}

Step 2: Create Player Runtime (player/index.html)

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="weather-widget">
<div class="weather-icon">
<img id="condition-icon" src="" alt="">
</div>
<div class="weather-info">
<div id="temperature" class="temp"></div>
<div id="condition" class="condition"></div>
<div id="location" class="location"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

Step 3: Create Player Logic (player/script.js)

(function() {
// Get component properties from SignPlayer
const props = window.componentProperties || {};

const API_KEY = props.apiKey;
const LOCATION = props.location || 'New York, US';
const UNITS = props.units || 'imperial';
const REFRESH_INTERVAL = (props.refreshInterval || 15) * 60 * 1000;

async function fetchWeather() {
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(LOCATION)}&appid=${API_KEY}&units=${UNITS}`
);

if (!response.ok) throw new Error('Weather API error');

const data = await response.json();
updateDisplay(data);
} catch (error) {
console.error('Weather fetch failed:', error);
showError();
}
}

function updateDisplay(data) {
const tempUnit = UNITS === 'imperial' ? '°F' : '°C';

document.getElementById('temperature').textContent =
Math.round(data.main.temp) + tempUnit;

document.getElementById('condition').textContent =
data.weather[0].description;

document.getElementById('location').textContent =
data.name;

document.getElementById('condition-icon').src =
`https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`;
}

function showError() {
document.getElementById('temperature').textContent = '--';
document.getElementById('condition').textContent = 'Unable to load';
}

// Initial fetch
fetchWeather();

// Refresh on interval
setInterval(fetchWeather, REFRESH_INTERVAL);

// Listen for SignPlayer events
window.addEventListener('signplayer:refresh', fetchWeather);
window.addEventListener('signplayer:pause', () => {
// Handle pause if needed
});
})();

Step 4: Create Properties Panel (studio/properties.html)

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
<style>
body { padding: 15px; }
.form-group { margin-bottom: 15px; }
</style>
</head>
<body>
<form id="properties-form">
<div class="form-group">
<label for="apiKey">OpenWeatherMap API Key</label>
<input type="text" class="form-control" id="apiKey" required>
<small class="text-muted">Get free API key at openweathermap.org</small>
</div>

<div class="form-group">
<label for="location">Location</label>
<input type="text" class="form-control" id="location" value="New York, US">
</div>

<div class="form-group">
<label for="units">Temperature Units</label>
<select class="form-control" id="units">
<option value="imperial">Fahrenheit (°F)</option>
<option value="metric">Celsius (°C)</option>
</select>
</div>

<div class="form-group">
<label for="refreshInterval">Refresh Interval (minutes)</label>
<input type="number" class="form-control" id="refreshInterval"
value="15" min="5" max="60">
</div>
</form>

<script src="properties.js"></script>
</body>
</html>

Step 5: Create Properties Logic (studio/properties.js)

(function() {
// Communication with SignStudio
const StudioBridge = {
// Get current property values
getProperties: function() {
return window.parent.postMessage({
type: 'getProperties'
}, '*');
},

// Save property values
setProperties: function(props) {
window.parent.postMessage({
type: 'setProperties',
properties: props
}, '*');
}
};

// Listen for property updates from SignStudio
window.addEventListener('message', function(event) {
if (event.data.type === 'propertiesLoaded') {
loadProperties(event.data.properties);
}
});

// Load properties into form
function loadProperties(props) {
if (props.apiKey) document.getElementById('apiKey').value = props.apiKey;
if (props.location) document.getElementById('location').value = props.location;
if (props.units) document.getElementById('units').value = props.units;
if (props.refreshInterval) document.getElementById('refreshInterval').value = props.refreshInterval;
}

// Save on input change
document.getElementById('properties-form').addEventListener('change', function() {
StudioBridge.setProperties({
apiKey: document.getElementById('apiKey').value,
location: document.getElementById('location').value,
units: document.getElementById('units').value,
refreshInterval: parseInt(document.getElementById('refreshInterval').value)
});
});

// Request initial properties
StudioBridge.getProperties();
})();

Testing Components Locally

# Start local development server
npx http-server ./custom-component -p 8080

# Access component directly
open http://localhost:8080/player/index.html

# Simulate SignPlayer properties
# In browser console:
window.componentProperties = {
apiKey: 'your_test_api_key',
location: 'London, UK',
units: 'metric',
refreshInterval: 5
};
location.reload();

Packaging and Distribution

# Create component package
cd custom-component
zip -r weather-widget-1.0.0.zip *

# Package structure:
# weather-widget-1.0.0.zip
# ├── manifest.json
# ├── icon.png
# ├── player/
# │ ├── index.html
# │ ├── styles.css
# │ └── script.js
# └── studio/
# ├── properties.html
# └── properties.js

Pepper SDK

RedPepper SDK (Authentication)

// Authentication with RedPepper SDK
import { RedPepper } from '@mediasignage/pepper-sdk';

const pepper = new RedPepper({
serverUrl: 'https://your-server.com',
domain: 'your-domain'
});

// Login
async function login(username, password) {
try {
const session = await pepper.authenticate(username, password);
console.log('Logged in:', session.user);
return session;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
}

// Logout
async function logout() {
await pepper.logout();
}

// Check session
function isAuthenticated() {
return pepper.isAuthenticated();
}

YellowPepper SDK (Server Communication)

// Server communication with YellowPepper SDK
import { YellowPepper } from '@mediasignage/pepper-sdk';

const yellow = new YellowPepper({
serverUrl: 'https://your-server.com',
authToken: sessionToken
});

// Get campaigns
async function getCampaigns() {
const campaigns = await yellow.campaigns.list();
return campaigns;
}

// Create resource
async function uploadMedia(file) {
const resource = await yellow.resources.upload(file, {
name: file.name,
type: 'image'
});
return resource;
}

// Update scene
async function updateScene(sceneId, sceneData) {
const scene = await yellow.scenes.update(sceneId, sceneData);
return scene;
}

// Subscribe to real-time updates
yellow.subscribe('campaign.updated', (data) => {
console.log('Campaign updated:', data);
});

msGetStarted - Account Creation Wizard

Overview

msGetStarted is an open-source account creation wizard that can be customized for white-label deployments.

Repository: github.com/born2net/msgetstarted

Features

  • Studio selection (Lite/Pro)
  • Player installer selection
  • Branded account creation
  • Password management
  • Bootstrap responsive layout

Customization

// config.js - Branding configuration
const config = {
branding: {
companyName: 'Your Company',
logo: 'assets/your-logo.png',
primaryColor: '#3498db',
supportEmail: 'support@yourcompany.com',
termsUrl: 'https://yourcompany.com/terms',
privacyUrl: 'https://yourcompany.com/privacy'
},

features: {
showStudioSelection: true,
defaultStudio: 'lite',
showPlayerOptions: true,
allowSocialLogin: false,
requireEmailVerification: true
},

plans: {
showFreePlan: true,
showProPlan: true,
showEnterprisePlan: true,
defaultPlan: 'free'
}
};

Enterprise Dashboard

Features

The Enterprise StudioDashboard provides centralized management for large signage networks:

  • User Control: Manage users, roles, permissions
  • Server Status: Live monitoring of all servers
  • Network Health: Reports and alerts
  • Application Management: Deploy and manage apps
  • User Activity: Audit logs and analytics
  • Geo Maps: Visualize screen locations
  • Remote Management: Control players remotely

Deployment

# Clone dashboard repository
git clone https://github.com/born2net/studioDashboard.git
cd studioDashboard

# Configure environment
cp .env.example .env
# Edit .env with your settings

# Build and deploy
npm install
npm run build

# Serve with nginx or similar
cp -r dist/* /var/www/dashboard/

API Integration

REST API Overview

// Base API endpoints
const API_BASE = 'https://your-server.com/api/v1';

const endpoints = {
// Authentication
auth: {
login: `${API_BASE}/auth/login`,
logout: `${API_BASE}/auth/logout`,
refresh: `${API_BASE}/auth/refresh`
},

// Campaigns
campaigns: {
list: `${API_BASE}/campaigns`,
create: `${API_BASE}/campaigns`,
get: (id) => `${API_BASE}/campaigns/${id}`,
update: (id) => `${API_BASE}/campaigns/${id}`,
delete: (id) => `${API_BASE}/campaigns/${id}`
},

// Resources
resources: {
list: `${API_BASE}/resources`,
upload: `${API_BASE}/resources/upload`,
get: (id) => `${API_BASE}/resources/${id}`,
delete: (id) => `${API_BASE}/resources/${id}`
},

// Stations (Players)
stations: {
list: `${API_BASE}/stations`,
get: (id) => `${API_BASE}/stations/${id}`,
update: (id) => `${API_BASE}/stations/${id}`,
command: (id) => `${API_BASE}/stations/${id}/command`
}
};

WebSocket Real-Time Updates

// Real-time connection for live updates
const ws = new WebSocket('wss://your-server.com/ws');

ws.onopen = () => {
// Authenticate WebSocket connection
ws.send(JSON.stringify({
type: 'auth',
token: authToken
}));

// Subscribe to updates
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['stations', 'campaigns']
}));
};

ws.onmessage = (event) => {
const data = JSON.parse(event.data);

switch (data.type) {
case 'station.status':
handleStationUpdate(data.payload);
break;
case 'campaign.updated':
handleCampaignUpdate(data.payload);
break;
case 'resource.uploaded':
handleResourceUpdate(data.payload);
break;
}
};

Frequently Asked Questions


The open-source ecosystem enables developers to build customized digital signage solutions. For enterprise development support and additional resources, contact MediaSignage or visit the API documentation.