How to Create a Live Sports Data Billboard¶
This guide walks through creating a digital billboard that displays live sports data — betting odds, live game scores, or game status. These creatives update in real time as the data changes, making them highly relevant during game-day windows.
Prerequisites¶
- A Lucit account with access to the Template Designer
- A live sports data source (odds API, scores API, or a regularly updated spreadsheet)
- Familiarity with the Template Designer canvas
- For sports betting creatives: geo rules configured to target only screens in legal sports betting states
Compliance Note: Sports betting advertising is regulated at the state level. Configure geo rules to suppress betting-specific creative (odds, "Bet Now" CTAs, spread messaging) in states where sports betting is not legal. A general brand awareness creative should serve in non-permitted states.
What You'll Build¶
This guide covers three variations:
- Live Betting Odds Display — Shows current spread, moneyline, and over/under for a game
- Live Score Ticker — Shows current score and game status (quarter, period, half)
- Game Phase Creative — Switches creative content based on whether the game is pre-game, in-game, halftime, or post-game
Architecture Overview¶
Live sports data billboards require an external data feed connected to Lucit. The creative template reads from this feed every time it renders.
Two supported feed approaches:
-
Google Sheets (recommended for simpler setups): Manually or automatically update a Google Sheet with current odds/scores. The template reads from the sheet. Refresh interval depends on how often the sheet is updated.
-
API-Connected Feed (advanced): A server-side integration pulls from a live sports data API (e.g., The Odds API, Sportradar, StatMuse) and writes to a format readable by Lucit templates. Enables true real-time data.
Variation 1: Betting Odds Display¶
Step 1: Set Up Your Data Source¶
Create a Google Sheet with the following columns:
| Column | Description | Example |
|---|---|---|
home_team |
Home team name or abbreviation | Chiefs |
away_team |
Away team name or abbreviation | Eagles |
spread_home |
Home team spread | -3.5 |
spread_away |
Away team spread | +3.5 |
over_under |
Game total | 49.5 |
moneyline_home |
Home moneyline | -175 |
moneyline_away |
Away moneyline | +145 |
game_time |
Kickoff time | 6:30 PM ET |
is_active |
Whether to show this game | true |
Connect this sheet to your template using the Google Sheets app in Lucit: 1. Go to Apps & Data → ADD NEW 2. Select Google Sheets 3. Authorize and paste your Sheet ID 4. Map the column names to data variables in the template
Step 2: Build the Creative Template¶
Design a template with text elements for each data point. Suggested layout:
[GAME TIME] [SPONSOR LOGO]
[AWAY TEAM] vs. [HOME TEAM]
[AWAY SPREAD] [HOME SPREAD]
O/U: [OVER_UNDER]
Give each text element an ID matching its data variable:
- #game-time
- #home-team, #away-team
- #spread-home, #spread-away
- #over-under
Step 3: Add Odds Formatting JavaScript¶
Use a registerDesignerFunction to format the spread value with a + prefix where appropriate:
registerDesignerFunction('formatSpread', function(params, data) {
// params[0] = the raw spread value (e.g., "-3.5" or "3.5")
var spread = params[0];
if (!spread || spread === '') {
return 'EVEN';
}
var num = parseFloat(spread);
if (isNaN(num)) {
return spread;
}
if (num > 0) {
return '+' + num.toFixed(1);
} else {
return num.toFixed(1);
}
});
registerDesignerFunction('formatMoneyline', function(params, data) {
// params[0] = the raw moneyline value (e.g., "-175" or "145")
var ml = params[0];
if (!ml || ml === '') {
return '';
}
var num = parseInt(ml);
if (isNaN(num)) {
return ml;
}
if (num > 0) {
return '+' + num;
} else {
return '' + num;
}
});
Connect each function to the appropriate text element via its Custom Field selector.
Variation 2: Live Score Ticker¶
Step 1: Data Source Setup¶
Create a Google Sheet with scoring columns. Update this sheet during the game (manually, or via an automated integration):
| Column | Description | Example |
|---|---|---|
home_team |
Home team | Chiefs |
away_team |
Away team | Eagles |
home_score |
Home team score | 17 |
away_score |
Away team score | 14 |
game_period |
Period/quarter/half | Q3 |
clock |
Game clock remaining | 4:22 |
game_status |
Status | in-progress / halftime / final |
Step 2: Score Display JavaScript¶
registerDesignerFunction('getScoreDisplay', function(params, data) {
// Reads from data object — field names match Google Sheet column headers
var homeScore = data['home_score'] || '0';
var awayScore = data['away_score'] || '0';
var period = data['game_period'] || '';
var clock = data['clock'] || '';
var status = data['game_status'] || '';
if (status === 'final') {
return 'FINAL';
}
if (status === 'halftime') {
return 'HALFTIME';
}
if (status === 'pre-game') {
return 'Kickoff Coming Up';
}
// Build period + clock display
if (clock && period) {
return period + ' ' + clock;
}
return period || 'In Progress';
});
registerDesignerFunction('getScoreString', function(params, data) {
var home = data['home_score'] || '0';
var away = data['away_score'] || '0';
var homeTeam = data['home_team'] || 'Home';
var awayTeam = data['away_team'] || 'Away';
return awayTeam + ' ' + away + ' – ' + home + ' ' + homeTeam;
});
Variation 3: Game Phase Creative Switcher¶
This is the most sophisticated pattern — the creative changes its entire messaging approach based on which phase of game day it is.
registerDesignerFormattingFunction(
'applyGamePhaseCreative',
function(element, dataValue, dataObject, elementSettings, cssSelector) {
var gameStatus = dataObject['game_status'] || 'pre-game';
var homeTeam = dataObject['home_team'] || 'Home';
var awayTeam = dataObject['away_team'] || 'Away';
var homeScore = dataObject['home_score'] || '0';
var awayScore = dataObject['away_score'] || '0';
var kickoffTime = dataObject['game_time'] || 'Tonight';
var doc = element.ownerDocument;
// Target the headline element
var headline = doc.querySelector('#game-headline');
if (!headline) return;
if (gameStatus === 'pre-game') {
headline.textContent = awayTeam + ' at ' + homeTeam + ' — Kickoff ' + kickoffTime;
headline.style.color = '#ffffff';
}
else if (gameStatus === 'in-progress') {
headline.textContent = awayTeam + ' ' + awayScore + ' – ' + homeScore + ' ' + homeTeam;
headline.style.color = '#ffff00'; // yellow for live
}
else if (gameStatus === 'halftime') {
headline.textContent = 'HALFTIME: ' + awayTeam + ' ' + awayScore + ' – ' + homeScore + ' ' + homeTeam;
headline.style.color = '#ff6600'; // orange for halftime
}
else if (gameStatus === 'final') {
var winner = parseInt(homeScore) > parseInt(awayScore) ? homeTeam : awayTeam;
headline.textContent = 'FINAL: ' + winner + ' wins! ' + awayScore + ' – ' + homeScore;
headline.style.color = '#00ff88'; // green for final
}
},
['game_status', 'home_team', 'away_team', 'home_score', 'away_score', 'game_time']
);
Geo Rules for Betting Compliance¶
For any creative that displays odds, spreads, or betting CTAs:
- Go to your Campaign → Screen Rules
- Add a State geo rule: include only states where sports betting is legal in your campaign window
- Create a second creative (general brand awareness) with no betting content
- Apply that secondary creative to screens in non-legal states
This setup ensures compliance without requiring manual screen-by-screen management.
Related Guides¶
- How to Create a Countdown Ad — Pre-game countdown leading into game day
- Day & Night Time-of-Day Creatives — Combine with time-of-day rules for pre-game vs. in-game windows
- JavaScript Guide — Full function reference
- Super Bowl & Game Day Campaigns
- Entertainment & Sports