nodecg-venue-gfx
Broadcast graphics for a venue screen, as a NodeCG 2 bundle: an always-on scrolling ticker with operator-added TTL messages, corner-alternating ads that shrink the live program into a corner, a break announcement slide, and automated + manual OBS scene choreography over obs-websocket. Built for festivals and concerts; generic enough for any single-screen show.
- All on-screen text is edited live from the dashboard
- Support for multiple locales
- OBS setup is generated. One script emits an importable scene collection, and a dashboard panel verifies a live OBS against the exact geometry the graphics render from.
- If OBS dies mid-show, the scheduler keeps running and re-applies the right scene the moment OBS comes back. If NodeCG dies, restart it and it comes back in live mode with all content intact.
Quick start
Fresh venue machine (installs NodeCG + this bundle + config + OBS scene collection in one go):
curl -fsSLO https://git.dvdrw.dev/kckljajicevo/nodecg-venue-gfx/raw/branch/master/scripts/install.sh
bash install.sh
Into an existing NodeCG install:
cd <nodecg>/bundles
git clone https://git.dvdrw.dev/kckljajicevo/nodecg-venue-gfx.git
cd nodecg-venue-gfx
npm ci && npm run build
Then:
- Write
cfg/nodecg-venue-gfx.json(see docs/examples/cfg.example.json). - Set up OBS:
npm run obs:scenes-> import -> done. Details in docs/OBS-SETUP.md. - Start NodeCG (
node index.jsfrom the install root) and open the dashboard at http://localhost:9090.
To run as a service that survives reboots: scripts/install-service.sh
(systemd user unit). To update later: scripts/update.sh.
Configuration
cfg/nodecg-venue-gfx.json in the NodeCG install, validated against
configschema.json. Everything is optional:
| Key | Default | Purpose |
|---|---|---|
locale |
"en" |
Dashboard language ("en" or "sr") |
branding.tickerBlurb |
"Welcome ♪" |
First-run seed for the recurring ticker blurb |
branding.breakHeading |
"Stay tuned — there's more!" |
First-run seed for the break-slide heading |
branding.sampleQrUrl |
"https://example.com" |
First-run seed for the sample ad's QR URL |
obs.enabled |
false |
Connect to OBS and drive scenes |
obs.url |
"ws://127.0.0.1:4455" |
obs-websocket URL |
obs.password |
"" |
obs-websocket password |
obs.scenes.* |
MAIN, MAIN_AD_TR, … |
Names of the six driven scenes |
obs.transitions.* |
Move, Stinger |
Names of the two transitions |
obs.sources.* |
TickerOverlay, … |
Names of the shared sources/nested scenes (for the generator + verifier) |
branding.* values only seed the very first run — after that, everything is
edited from the dashboard and persists in NodeCG's database. Set
obs.enabled: false to develop without OBS; everything else keeps working.
Dashboard panels
- Ticker messages — ticker settings (loop blurb, separator, default TTL, speed) and message management; each active message shows a depleting fill for its remaining time, with renew/remove/inline-edit.
- Ads — CRUD for ads (strip text, column text, column image from the Assets dialog, QR URL, enabled), with a live QR preview per row. Each row has a "Trigger now" button to force that specific ad onto air immediately (enabled or not), bypassing the round-robin — disabled while an ad slot is already showing.
- Choreography / OBS — connection status, mode/phase badges, countdown to the next automatic transition, break-slide content, and manual controls: show ad now (round-robin), skip ad, break toggle.
- Timing — the choreography timing settings: ad interval, ad duration, break gap, break ad duration, ads per burst.
- OBS setup check — read-only verification of the live OBS against the expected scenes/sources/transforms/transitions, with concrete fixes.
Images (ad backgrounds, column images, break background, act logos) are uploaded through NodeCG's standard Assets dialog.
Graphics (1920×1080)
| URL | Default OBS source | Purpose |
|---|---|---|
graphics/ticker.html |
TickerOverlay |
Bottom 96 px scrolling ticker |
graphics/ad.html |
AdOverlay |
QR + headline in the gamma-shaped cutout |
graphics/break.html |
BreakSlide |
Break announcement slide (ambient motion) |
The ticker uses a segment-queue marquee: loop content is snapshotted per revolution, so adding/removing messages mid-scroll never causes visual jumps.
Theming
- Colors: edit the
THEME LABblock insrc/graphics/index.css— every brand color the graphics use is a token there. - Fonts: the
FONT LABblock right above it (Margarine is bundled as the default; drop a.ttf/.woff2intosrc/graphics/fonts/to swap). - Geometry (canvas size, the 70% shrink, ad cutout, OBS transforms):
src/shared/obs-geometry.mjs— one module shared by the graphics, the scene-collection generator, and the verify panel, so they can never disagree. v1 is fixed at 1920×1080.
Adding a language
- Copy
src/i18n/en.tstosrc/i18n/<code>.tsand translate (theStringstype keeps you honest). - Register it in
src/i18n/index.ts(locales). - Add the code to the
localeenum inconfigschema.json.
Development
npm run dev # parcel watch + nodemon restarting NodeCG
npm run build # one-shot production build
npm run obs:scenes # regenerate the OBS scene collection
Dev mode assumes the bundle lives inside a NodeCG install
(bundles/nodecg-venue-gfx); nodemon runs node ../../index.js.
Rules of thumb:
- Graphics/dashboard changes: rebuild (or let the watcher do it), then refresh the page / OBS browser source. No server restart.
- Extension (
src/extension/), types, or package.json changes: rebuild and restart NodeCG — plainnode index.jsnever rebuilds anything. - Graphics are fixed 1920×1080 canvases — judge them in OBS or a fullscreen 1080p tab; smaller windows show a crop.
Architecture notes
- The scheduler is a state machine over
(mode: normal|break, phase: live|ad)with exactly one pending timer; OBS calls are fire-and-forget, so an unreachable OBS never stalls the choreography. - Corner alternation and ad round-robin persist across restarts
(
schedulerStatereplicant). On startup the bundle always returns to (normal, live) + the main scene. - Replicant defaults are only applied when nothing is persisted, so operator edits survive restarts.