Skip to content

Loadscreen NodeCG Bundle - System Overview

High-level architecture and system design documentation for the Loadscreen broadcast graphics system.

Purpose

The Loadscreen NodeCG Bundle is a broadcast graphics system for esports productions. It provides:

  • Real-time overlays for live broadcasts (scoreboards, rosters, maps, etc.)
  • Web-based dashboard for production control
  • Integration with external services (Challonge, Rainbow Six API, etc.)
  • Asset management for logos, images, videos
  • Animation state management for smooth transitions

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         NodeCG Server                            │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │              Loadscreen Bundle Extension                    │ │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │ │
│  │  │ Team Mgmt    │  │ Caster Mgmt  │  │ Graphics Mgmt│    │ │
│  │  └──────────────┘  └──────────────┘  └──────────────┘    │ │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │ │
│  │  │ Map Set      │  │ Standings    │  │ Schedule     │    │ │
│  │  └──────────────┘  └──────────────┘  └──────────────┘    │ │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │ │
│  │  │ Scene Switch │  │ Lottie Player│  │ API Routes   │    │ │
│  │  └──────────────┘  └──────────────┘  └──────────────┘    │ │
│  └────────────────────────────────────────────────────────────┘ │
│                                                                   │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │                    Replicant Database                       │ │
│  │   (Real-time synchronized state stored in SQLite)          │ │
│  └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              ▲  │
                WebSocket     │  │  HTTP
                              │  ▼
        ┌─────────────────────────────────────────────┐
        │         Clients (Web Browsers)              │
        │  ┌───────────┐  ┌──────────────────────┐  │
        │  │ Dashboard │  │ Graphics (in OBS)    │  │
        │  └───────────┘  └──────────────────────┘  │
        └─────────────────────────────────────────────┘

Core Concepts

1. NodeCG Framework

NodeCG provides the foundation:

  • Replicants - Synchronized state between server, dashboard, and graphics
  • Messages - Event-driven communication
  • WebSocket - Real-time data updates
  • Express Routes - HTTP API endpoints
  • Asset Management - File upload/storage system

2. Bundle Structure

loadscreen-nodecg-bundle/
├── extension/          # Server-side code (Node.js)
│   ├── index.js        # Entry point
│   └── modules/        # Feature modules

├── dashboard/          # Control panels (Web UI)
│   ├── current-match/  # Match control
│   ├── team-general/   # Team management
│   ├── scene-switcher/ # Graphics switcher
│   └── ...

├── graphics/           # Broadcast overlays (for OBS)
│   ├── scoreboard/     # Match scoreboard
│   ├── roster/         # Team rosters
│   ├── map-set/        # Map selection
│   └── ...

└── schemas/            # Data validation schemas
    ├── teams.json
    ├── currentMatch.json
    └── ...

3. Data Flow

Producer → Dashboard Panel → Replicant → Graphics → OBS → Broadcast

Example: Updating team score
1. Producer clicks +1 on dashboard
2. Dashboard sends HTTP request to API
3. API updates "currentMatch" replicant
4. Replicant syncs to all connected clients
5. Graphics (in OBS) receive update via WebSocket
6. Scoreboard animates to show new score
7. Viewers see updated score on stream

Key Components

Extension Modules (Server-Side)

Located in extension/modules/, these handle business logic:

team-management.js (35KB - Largest module)

  • Team CRUD operations
  • Roster management
  • Player database
  • Team data formatting for graphics
  • Swap sides/colors functionality
  • Provides team selection dropdowns

Key functions:

  • initTeamMgmt() - Initialize listeners
  • handleTeamMgmt() - Process team form submissions
  • formatRosterForGraphic() - Format team data for overlays
  • getTeam(), getPlayer() - Database queries

casters-management.js

  • Caster database
  • Current casters selection
  • Format caster data for graphics

graphics-management.js

  • List all available graphics
  • Combine bundle graphics + custom lottie animations
  • Populate graphics dropdown menus

map-set.js

  • Map selection and ordering
  • Map pool management
  • Format map data for graphics

scene-switcher.js

  • Control which graphics are visible
  • Transition management
  • Preview/Program functionality

lottie-player.js

  • Manage custom Lottie animations
  • Upload and parse .json animation files
  • Dynamic animation state inference

global-style.js

  • Inject CSS variables for team colors
  • Apply to all dashboard panels
  • Real-time color synchronization

api.js

  • HTTP API endpoints
  • Read/update replicants via REST
  • Trigger messages from external tools
  • Integration endpoints

Key APIs:

  • /api/readReplicant?replicant=NAME - Get data
  • /api/updateReplicant?replicant=NAME&value=VALUE - Set data
  • /api/currentMatch.json - Current match JSON export
  • /api/swap-sides - Swap team positions
  • /api/updateScore?team=1&value=1 - Update scores

standings.js

  • Tournament standings management
  • Win/loss records
  • Leaderboard formatting

schedule.js

  • Match schedule
  • Upcoming matches
  • Schedule display formatting

predictions.js

  • Match predictions/betting
  • Win probability calculations

migrations.js

  • Database initialization
  • Schema migrations
  • Default data seeding

file-management.js

  • File operations
  • Asset handling

challongeAPI.js

  • Challonge tournament integration
  • Import brackets/matches

r6api.js

  • Rainbow Six Siege API integration
  • Player stats
  • Match predictions based on stats

Dashboard Panels (Control Interface)

Web-based control panels for production staff:

current-match - Match Control

  • Select teams
  • Update scores
  • Set series text (event name)
  • Quick score increment/decrement buttons
  • Swap sides/colors
  • Animation controls (show/hide scoreboard)

team-general - Team Management

  • Create/edit teams
  • Manage team info (name, logo, colors)
  • Roster management (add/remove players)
  • Player profiles (name, role, headshot, etc.)

casters-management - Talent Control

  • Manage caster database
  • Add/edit caster info
  • Select current casters for broadcast

current-casters - Active Casters

  • Display current talent on stream
  • Quick selection interface

scene-switcher - Graphics Control

  • Visual preview of all graphics
  • Drag-and-drop scene ordering
  • Show/hide graphics
  • Transition controls

map-set - Map Selection

  • Select maps for match
  • Set map order
  • Map pool management
  • Show map bans

standings - Tournament Standings

  • Team rankings
  • Win/loss records
  • Playoff brackets

schedule - Match Schedule

  • Upcoming matches
  • Match times
  • Series information

graphics-groups - Graphic Bundles

  • Group multiple graphics together
  • Play sequences of overlays
  • Custom animation playlists

lottie-player - Animation Manager

  • Upload Lottie animation files
  • Preview animations
  • Configure animation states

predictions - Match Predictions

  • Display betting odds
  • Win probabilities

global-style - Color Theme

  • Set team colors globally
  • CSS injection for consistent theming

Graphics (Broadcast Overlays)

HTML pages loaded in OBS as browser sources:

scoreboard/ - Match Scoreboard

  • Team names and logos
  • Current score
  • Series text (event name)
  • Best-of indicator
  • Animated transitions

Variants:

  • scoreboard.html - Standard version
  • scoreboard-ow.html - Overwatch-specific

roster/ - Team Rosters

  • Player names and roles
  • Player headshots
  • Team branding
  • Multiple layouts

Variants:

  • leftTeam.html - Single team (left side)
  • rightTeam.html - Single team (right side)
  • roster-vs-roster.html - Both teams head-to-head
  • roster-fixed-layout.html - Pre-defined positions

map-set/ - Map Selection

  • Map pool display
  • Selected maps
  • Map bans
  • Next map animations

Variants:

  • map-set.html - Basic version
  • map-set-fixed-layout.html - Fixed positions
  • map-set-footer-bans.html - With ban indicators
  • next-map-video.html - Video transition
  • next-map-lottie.html - Lottie animation

casters/ - Talent Display

  • Caster names and photos
  • Social media handles
  • Animated introductions

standings/ - Tournament Standings

  • Team rankings table
  • Win/loss records
  • Animated reveals

schedule/ - Match Schedule

  • Upcoming matches
  • Match times
  • Best-of indicators

predictions/ - Betting/Predictions

  • Win probabilities
  • Betting odds display

brackets/ - Tournament Brackets

  • Bracket visualization
  • Match progression
  • Playoff tree

lower-thirds/ - Information Bars

  • Interview lower thirds
  • Generic lower thirds
  • Scoreboard lower thirds

graphics-groups/ - Grouped Animations

  • Play multiple graphics in sequence
  • Custom animation playlists

scene-switcher/ - Program Output

  • preview.html - Preview feed
  • program.html - Program feed (live)

lottie-player/ - Custom Animations

  • Play user-uploaded Lottie animations
  • Dynamic state control

Data Model

Replicants (State)

Replicants are real-time synchronized data stored in SQLite:

teams (Object)

javascript
{
    "team-id-123": {
        id: "team-id-123",
        name: "Team Name",
        initials: "TN",
        logo: "/path/to/logo.png",
        primaryColor: "#2986ff",
        secondaryColor: "#18548b",
        roster: {
            "player-id-1": {
                id: "player-id-1",
                name: "Player Name",
                role: "DPS",
                headshot: "/path/to/photo.jpg",
                // ... more fields
            }
        }
    }
}

currentMatch (Object)

javascript
{
    team1: "team-id-123",
    team2: "team-id-456",
    team1score: "2",
    team2score: "1",
    team1primaryColor: "#2986ff",
    team1secondaryColor: "#18548b",
    team2primaryColor: "#c72929",
    team2secondaryColor: "#781717",
    seriesText: "Grand Finals",
    bestOf: "5"
}

graphicsGroups (Array)

javascript
[
    {
        groupName: "Match Intro",
        graphics: [
            { url: "/path/to/graphic1.html", suffix: "" },
            { url: "/path/to/graphic2.html", suffix: "" }
        ],
        hideInSceneSwitcher: false
    }
]

casters (Object)

javascript
{
    "caster-id-1": {
        id: "caster-id-1",
        name: "Caster Name",
        twitter: "@handle",
        photo: "/path/to/photo.jpg"
    }
}

lottiePlayer (Object)

javascript
{
    "animation-name": {
        lottieData: { /* Lottie JSON */ },
        states: { /* Animation states */ }
    }
}

Animation States

javascript
// Per-graphic animation control
"animState-scoreboard": "hidden" | "default" | "custom-state"
"animState-roster": "hidden" | "in" | "out"

Assets (Files)

Managed through NodeCG's asset system:

  • team-logos/ - Team branding
  • player-headshots/ - Player photos
  • map-images/ - Map icons/banners
  • animations/ - Lottie animation files
  • other-images/ - General images
  • other-videos/ - Video files
  • fonts/ - Custom fonts
  • transitions/ - Transition videos

Data Flow Examples

Example 1: Updating Match Score

1. Producer clicks "+1" button in dashboard

2. Dashboard sends request:
   GET /api/updateScore?team=1&value=1

3. API endpoint (api.js) receives request

4. Updates "currentMatch" replicant:
   currentMatch.team1score = "3"

5. Replicant broadcasts change via WebSocket

6. Scoreboard graphic (in OBS) receives update

7. JavaScript updates DOM: document.querySelector('.team1score').textContent = "3"

8. Lottie animation plays score change transition

9. Viewers see updated score on stream

Example 2: Changing Graphics

1. Producer drags graphic in Scene Switcher

2. Dashboard updates "scene-switcher" replicant

3. Scene Switcher extension receives change event

4. Updates animation states:
   - Old graphic: animState → "hidden"
   - New graphic: animState → "default"

5. Graphics receive state changes

6. Old graphic animates out (transition)
   New graphic animates in (transition)

7. Smooth transition visible in OBS/stream

Example 3: Team Color Update

1. Producer changes team color in Team Management

2. Dashboard updates "teams" replicant

3. Global Style module detects change

4. Generates CSS variables:
   --team1-primary: #2986ff
   --team1-secondary: #18548b

5. Injects CSS into all dashboard panels

6. currentMatch replicant updated with colors

7. Graphics receive color updates

8. All overlays update colors in real-time

Communication Patterns

1. Replicant Changes (Real-time Sync)

javascript
// Server-side (Extension)
nodecg.Replicant('teams').on('change', (newValue, oldValue) => {
    // React to changes
    processTeamUpdate(newValue);
});

// Client-side (Dashboard/Graphics)
const teamsRep = nodecg.Replicant('teams');
teamsRep.on('change', (newValue) => {
    // Update UI
    renderTeams(newValue);
});

2. Messages (Request/Response)

javascript
// Server listens
nodecg.listenFor('requestTeam', (teamId, ack) => {
    const team = getTeam(teamId);
    ack(null, team);  // Send response
});

// Client sends
nodecg.sendMessage('requestTeam', 'team-id-123', (error, team) => {
    if (team) {
        renderTeam(team);
    }
});

3. HTTP API (External Integration)

bash
# Read data
GET /bundles/loadscreen-nodecg-bundle/api/readReplicant?replicant=teams

# Update data
GET /bundles/loadscreen-nodecg-bundle/api/updateReplicant?replicant=currentMatch&key=seriesText&value=Finals

# JSON export
GET /bundles/loadscreen-nodecg-bundle/api/currentMatch.json

Feature Modules Deep Dive

Team Management System

Purpose: Manage teams, players, and match setup

Components:

  • Extension: team-management.js (35KB)
  • Dashboard: team-general/ panel
  • Graphics: roster/* overlays
  • Schema: teams.json

Features:

  • Team database (name, logo, colors, etc.)
  • Player roster (name, role, photo, stats)
  • Current match team selection
  • Color synchronization
  • Swap sides/colors

Data managed:

  • Team profiles (50+ properties per team)
  • Player profiles (30+ properties per player)
  • Current match state
  • Formatted data for graphics

Graphics Management System

Purpose: Organize and list all available graphics

Components:

  • Extension: graphics-management.js
  • Used by: Scene Switcher, various dropdowns

Features:

  • Scans all bundles for graphics
  • Lists custom Lottie animations
  • Lists uploaded assets (images/videos)
  • Provides formatted dropdown options
  • Groups graphics by bundle

Output: graphicsSelect replicant with structured list

Scene Switcher

Purpose: Control which graphics are visible in broadcast

Components:

  • Extension: scene-switcher.js
  • Dashboard: scene-switcher/ (fullbleed panel)
  • Graphics: scene-switcher/preview.html, program.html

Features:

  • Visual preview of all graphics
  • Drag-and-drop ordering
  • Show/hide graphics
  • Transition control
  • Preview/Program feeds

How it works:

  1. Maintains list of graphic states (shown/hidden)
  2. Updates animation state replicants
  3. Graphics react to state changes
  4. Smooth transitions via Lottie animations

Animation State Machine

Purpose: Smooth transitions between graphic states

Key file: graphics/common/modules/animationStateMachine.js

Concept:

  • Graphics have states: "hidden", "default", "variant1", etc.
  • Each state has a frame position or range
  • Transitions between states play animations
  • Lottie JSON defines animation frames

Example states:

javascript
{
    states: {
        hidden: { frame: 1 },      // Completely off
        default: { frame: [76, 92], loop: true },  // Showing
        team1Win: { frame: 150 }   // Special state
    },
    animations: {
        in: [1, 76],      // Animate from hidden to default
        out: [76, 1]      // Animate from default to hidden
    }
}

Usage in graphics:

javascript
nodecg.Replicant('animState-scoreboard').on('change', (newState, oldState) => {
    animationStateMachine(lottieAnim, animStates, oldState, newState);
});

Lottie Integration

Purpose: Use After Effects animations in web graphics

Workflow:

  1. Designer creates animation in After Effects
  2. Export as Lottie JSON (Bodymovin plugin)
  3. Upload to bundle
  4. Bundle parses layer names (.primaryColor, .teamName, etc.)
  5. Dynamically updates text/colors in animation
  6. Real-time data binding to team/player data

Special layer names:

  • .primaryColor - Team primary color
  • .secondaryColor - Team secondary color
  • .teamName - Team name text
  • .seriesText - Event/series text
  • .dynamicScale - Auto-scale text to fit
  • .maxW-800 - Max width 800px

Asset Management

Purpose: Upload and manage media files

Categories:

  • Team logos
  • Player headshots
  • Map images
  • Character portraits
  • Lottie animation files
  • Fonts
  • Generic images/videos
  • Transitions

Access in graphics:

javascript
const assets = nodecg.readReplicant('assets:team-logos');
const logoUrl = assets[0].url;

External Integrations

Challonge API

  • Import tournament brackets
  • Fetch match schedules
  • Sync results

Rainbow Six Siege API

  • Fetch player stats
  • Calculate win rates
  • Generate predictions

Custom API Endpoints

  • Allow external tools to read/write data
  • Integration with streaming software
  • Third-party overlays

Technology Stack

Server-Side

  • Node.js v20.x - Runtime
  • Express - Web server
  • Socket.IO - Real-time communication
  • SQLite - Database (via NodeCG's replicant system)
  • Axios - HTTP requests (external APIs)
  • Dotenv - Environment variables

Client-Side (Dashboard)

  • Bootstrap 5 - UI framework
  • Bootstrap Icons - Icons
  • EJS - Templating (optional)
  • Draggable (@shopify/draggable) - Drag-and-drop
  • Chart.js - Data visualization
  • Vanilla JavaScript - No heavy frameworks

Graphics

  • Lottie-web - Animation playback
  • CountUp.js - Number animations
  • Vanilla JavaScript - Lightweight for OBS

Performance Considerations

Design Principles

  1. Lightweight Graphics

    • Minimal JavaScript
    • No heavy frameworks in OBS
    • Efficient animation playback
  2. Real-time Updates

    • WebSocket for instant sync
    • Optimistic UI updates
    • Debounced save operations
  3. Asset Optimization

    • Compress images
    • Use WebP format
    • Lazy-load assets
    • CDN for static files (optional)

Resource Usage

Typical resource usage:

  • Memory: ~150-300 MB
  • CPU: 5-15% idle, 20-40% during active use
  • Network: Minimal (WebSocket keepalive)
  • Disk: ~100MB bundle + assets

OBS Browser Sources:

  • Each graphic = separate browser instance
  • ~50-100MB RAM per graphic
  • GPU-accelerated rendering
  • Set "Shutdown when not visible" to save resources

Security Model

Authentication

  • Discord OAuth for user login
  • Role-based access control
  • Session management via express-session

Authorization

  • All dashboard panels require auth (if enabled)
  • Graphics are public (displayed in OBS/stream)
  • API endpoints can require auth

Data Protection

  • No sensitive data in client-side code
  • Secrets in .env file (server-only)
  • Session secrets for cookie signing

Deployment Architecture

Production Setup:

User Browser → Nginx (port 80) → NodeCG (port 9090) → SQLite DB

                                  Replicants

                              ┌─────────┴─────────┐
                              ▼                   ▼
                         Dashboard            Graphics
                      (Control Room)        (OBS Studio)

                                            Live Stream

Components:

  • Nginx - Reverse proxy, SSL termination, port 80/443
  • NodeCG - Application server, port 9090
  • PM2 - Process manager, auto-restart, monitoring
  • SQLite - Persistent storage for replicants
  • OBS Studio - Broadcast software loading graphics

Scalability

Current Limitations

  • Single instance - Not designed for horizontal scaling
  • SQLite - Single file database (not distributed)
  • In-memory state - Replicants cached in RAM

Suitable For

  • Single production setup
  • Up to ~10 concurrent dashboard users
  • Unlimited graphics (read-only viewers)
  • Multiple OBS instances on network

Not Suitable For

  • Multiple redundant servers
  • High-availability requirements
  • Geographically distributed production

Extension Points

Adding New Features

  1. New Dashboard Panel:

    • Create HTML in dashboard/panel-name/
    • Add to package.json dashboardPanels array
    • Restart NodeCG
  2. New Graphic:

    • Create HTML in graphics/graphic-name/
    • Add to package.json graphics array
    • Add to OBS as browser source
  3. New Extension Module:

    • Create .js file in extension/modules/
    • Import and initialize in extension/index.js
    • Restart NodeCG
  4. New API Endpoint:

    • Add route in extension/modules/api.js
    • Use nodecg.Router()
    • Access at /bundles/loadscreen-nodecg-bundle/api/your-endpoint
  5. New Replicant:

    • Create schema in schemas/your-replicant.json
    • Add default value in db-default-data.json
    • Use in extension/dashboard/graphics

Development Workflow

Local Development

bash
# Start NodeCG
npm start

# Access dashboard
http://localhost:9090/dashboard/

# Test graphic in browser
http://localhost:9090/bundles/loadscreen-nodecg-bundle/graphics/scoreboard/scoreboard.html

Making Changes

  1. Edit code (extension, dashboard, or graphics)
  2. Restart NodeCG (changes take effect)
  3. Refresh browser (for dashboard/graphics changes)
  4. Test in OBS (add as browser source)

No Build Required

  • Bundle code runs directly (no compilation)
  • Instant reload on changes
  • Fast iteration

Integration with OBS

Adding Graphics to OBS

  1. Add Browser Source

    • Click "+" in Sources
    • Choose "Browser"
  2. Configure:

    • URL: http://localhost:9090/bundles/loadscreen-nodecg-bundle/graphics/scoreboard/scoreboard.html
    • Width: 1920
    • Height: 1080
    • FPS: 60 (for smooth animations)
    • Custom CSS: (if needed)
  3. Settings:

    • ✅ Shutdown source when not visible
    • ✅ Refresh browser when scene becomes active

Managing Multiple Graphics

Best practices:

  • One OBS scene per graphic
  • Use Scene Switcher to control visibility
  • Group related graphics in OBS scenes
  • Use OBS Studio Mode (Preview/Program)

Key Design Patterns

1. Global API Context

All extension modules access NodeCG via shared context:

javascript
// nodecg-api-context.js provides global access
const nodecgApiContext = require('./nodecg-api-context');
const nodecg = nodecgApiContext.get();

Benefit: No need to pass nodecg to every function

2. Replicant-Driven UI

Dashboard and graphics listen to replicant changes:

javascript
const currentMatch = nodecg.Replicant('currentMatch');
currentMatch.on('change', (newValue) => {
    updateUI(newValue);
});

Benefit: Automatic synchronization, no manual polling

3. Form Helper Pattern

Custom HTML attribute for easy forms:

html
<form ls-simple-form="currentMatch">
    <input name="team1score" />
    <button type="submit">Submit</button>
</form>

Benefit: Automatic binding to replicants

4. Animation State Management

Declarative animation definitions:

javascript
const animStates = {
    states: { /* destinations */ },
    animations: { /* transitions */ }
};

Benefit: Consistent animation behavior, easy to modify

System Boundaries

What This Bundle Does

✅ Broadcast overlay graphics ✅ Production control dashboard ✅ Team/player data management ✅ Asset management ✅ Animation control ✅ Match data tracking ✅ External API integration

What This Bundle Does NOT Do

❌ Video encoding/streaming (OBS handles this) ❌ Match gameplay capture (OBS handles this) ❌ Tournament bracket generation (imports from Challonge) ❌ Video replay system ❌ Chat moderation ❌ Donation/sub alerts (use different tool)

Future Considerations

Potential Enhancements

  • Database migrations for schema changes
  • More flexible animation system
  • Better asset organization
  • GraphQL API
  • TypeScript migration
  • Automated testing
  • Hot reload for graphics
  • Multi-language support

Scaling Options

  • Read-only replicas for graphics
  • Redis for distributed state
  • PostgreSQL for relational data
  • Microservices architecture
  • Kubernetes deployment

Summary

The Loadscreen NodeCG Bundle is a comprehensive broadcast graphics system that:

  1. Manages tournament data (teams, players, matches)
  2. Controls what graphics appear on stream
  3. Animates transitions between states
  4. Integrates with external services
  5. Provides web-based production control
  6. Delivers real-time overlays for OBS

Architecture: Extension (server logic) → Replicants (data) → Dashboard (control) + Graphics (display)

Key strength: Real-time synchronization via NodeCG's replicant system

Target users: Esports production teams, broadcast graphics operators, tournament organizers