Appearance
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 streamKey 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 listenershandleTeamMgmt()- Process team form submissionsformatRosterForGraphic()- Format team data for overlaysgetTeam(),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 versionscoreboard-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-headroster-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 versionmap-set-fixed-layout.html- Fixed positionsmap-set-footer-bans.html- With ban indicatorsnext-map-video.html- Video transitionnext-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 feedprogram.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 streamExample 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/streamExample 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-timeCommunication 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.jsonFeature 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:
- Maintains list of graphic states (shown/hidden)
- Updates animation state replicants
- Graphics react to state changes
- 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:
- Designer creates animation in After Effects
- Export as Lottie JSON (Bodymovin plugin)
- Upload to bundle
- Bundle parses layer names (
.primaryColor,.teamName, etc.) - Dynamically updates text/colors in animation
- 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
Lightweight Graphics
- Minimal JavaScript
- No heavy frameworks in OBS
- Efficient animation playback
Real-time Updates
- WebSocket for instant sync
- Optimistic UI updates
- Debounced save operations
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
.envfile (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 StreamComponents:
- 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
New Dashboard Panel:
- Create HTML in
dashboard/panel-name/ - Add to
package.jsondashboardPanels array - Restart NodeCG
- Create HTML in
New Graphic:
- Create HTML in
graphics/graphic-name/ - Add to
package.jsongraphics array - Add to OBS as browser source
- Create HTML in
New Extension Module:
- Create
.jsfile inextension/modules/ - Import and initialize in
extension/index.js - Restart NodeCG
- Create
New API Endpoint:
- Add route in
extension/modules/api.js - Use
nodecg.Router() - Access at
/bundles/loadscreen-nodecg-bundle/api/your-endpoint
- Add route in
New Replicant:
- Create schema in
schemas/your-replicant.json - Add default value in
db-default-data.json - Use in extension/dashboard/graphics
- Create schema in
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.htmlMaking Changes
- Edit code (extension, dashboard, or graphics)
- Restart NodeCG (changes take effect)
- Refresh browser (for dashboard/graphics changes)
- 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
Add Browser Source
- Click "+" in Sources
- Choose "Browser"
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)
- URL:
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:
- Manages tournament data (teams, players, matches)
- Controls what graphics appear on stream
- Animates transitions between states
- Integrates with external services
- Provides web-based production control
- 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