Match stats
Live match statistics bring sports events to life by providing real-time data about the game in progress. As scores change, possession shifts, and players make key moves, viewers expect to see these updates instantly. Real-time synchronized match stats enhance the viewing experience by informing fans with accurate, up-to-the-second information.
By the end of this document, you will understand:
- How PubNub's Pub/Sub messaging delivers real-time match statistics
- How Message Persistence ensures late-joining viewers see current stats
- How to implement efficient partial updates for specific stat changes
How PubNub helps
Role in the solution | PubNub feature (click to learn more!) |
---|---|
| Pub/Sub |
| Message Persistence |
| Structured messages |
Use case overview
The Live Events demo showcases how PubNub's real-time infrastructure delivers match statistics to all viewers simultaneously. This creates a cohesive viewing experience where everyone sees the same statistics synchronized with game events.
PubNub's cross-platform capabilities ensure viewers can access these synchronized match statistics whether they're watching on mobile devices, tablets, or desktops, providing a consistent experience across all platforms.
The demo demonstrates these core capabilities:
- Instant statistics delivery (< 30ms within a single PubNub region)
- Multiple stat types (for example, scores, possession, cards, player stats or such stats for non-football as current song or stage number)
- Current stats state for viewers joining mid-game
- Partial updates for bandwidth efficiency
As the live stream proceeds, the statistics panel is updated to reflect what is actually shown on the stream. Key metrics such as the current score, shots on goal, yellow cards, red cards, and top scorer are kept accurate and in sync with the stream.
Match stats are updated through PubNub Messaging (Pub/Sub) on a dedicated stats channel in two scenarios:
- When an event in the game causes the stats to update (such as a goal scored or a yellow card given).
- At regular intervals of approximately every 10 seconds to ensure continuous synchronization.
- When new viewers join the stream, they receive the most recent stats automatically through Message Persistence.
This approach ensures that statistics remain synchronized with stream content, while Message Persistence guarantees that viewers joining at any point immediately see the current stats without waiting for the next game event.
Basic setup for match stats
The Live Events demo implements match statistics using PubNub's Message Persistence feature to ensure late-joining viewers immediately see the game's current state.
// Initialize PubNub
const pubnub = new PubNub({
publishKey: process.env.PUBNUB_PUBLISH_KEY,
subscribeKey: process.env.PUBNUB_SUBSCRIBE_KEY,
userId: "admin",
});
// Publish match stats update that will be persisted
pubnub.publish({
channel: "game.match-stats",
message: {
statBox1: {
info: [{ stat: "2-1" }] // Current score
},
statBox2: {
show all 20 linesThis implementation demonstrates how to publish match stats updates that will be persisted. For retrieving stats when joining mid-game, see the Current stats for late-joiners section.
Channel architecture
The Live Events demo uses a dedicated PubNub channel specifically for match statistics:
export const matchStatsChannelId = "game.match-stats";
This architecture decision separates stats from other real-time features (chat, commentary, reactions), providing several benefits:
- Statistics messages don't compete with other app communication.
- Stats can follow different update patterns and frequencies.
For more complex implementations, you could consider alternative channel architectures:
- Using separate channels for each stat type (e.g.,
game.match-stats.goals
,game.match-stats.possession
) - Including game identifiers for multi-game scenarios (e.g.,
football-game-123.match-stats
) - Using PubNub's wildcard subscription (e.g., subscribing to
game.match-stats.*
to receive all stats)
These approaches provide more granular control but add complexity to your implementation. Choose a channel architecture that fits your specific application needs.
In the implementation, the demo creates a subscription to this dedicated channel:
// Create a subscription to the dedicated match stats channel
const channel = chat.sdk.channel(matchStatsChannelId)
const subscription = channel.subscription({
receivePresenceEvents: false // No need for presence
})
// Subscribe to start receiving statistics updates
subscription.subscribe()
This subscription establishes a persistent connection to PubNub's network, enabling real-time reception of all stats updates as they occur during the game.
Real-time message delivery
The core of the match stats feature is PubNub's real-time message delivery. When a stats update is published, it reaches all viewers:
// Set up message handler to process incoming stats updates
subscription.onMessage = messageEvent => {
processReceivedMessage(messageEvent.message)
}
The onMessage
handler executes whenever a new stats update arrives through PubNub's global network. The handler processes the message and updates the relevant statistics in the application state.
Behind the scenes, PubNub's global network of data centers routes these messages to all subscribed clients with minimal latency, regardless of their geographic location. This creates a synchronized viewing experience where all fans see the same stats at virtually the same time.
Current stats for late-joiners
The Live Events demo uses PubNub's Message Persistence to ensure that viewers joining mid-game immediately see the current match statistics:
// When joining mid-game, fetch most recent stats update
chat.sdk
.fetchMessages({
channels: [matchStatsChannelId],
count: 1 // Only need the most recent stats update
})
.then(result => {
if (result && result.channels[matchStatsChannelId]) {
const previousMatchStats = result.channels[matchStatsChannelId][0]
if (previousMatchStats) {
processReceivedMessage(previousMatchStats.message)
}
}
})
This implementation efficiently retrieves only the most recent stats update, which contains the current state of all match statistics. By setting count: 1
, the demo minimizes data transfer while ensuring viewers are immediately up-to-date when they join.
Message Persistence provides several key benefits for match stats:
- Viewers joining at any point see the current stats immediately
- No need to wait for the next update to see the current score
- Consistent viewing experience regardless of when viewers join
Efficient channel architecture for partial updates
While the Live Events demo updates only specific statistics in the UI, it still receives complete stat messages, which is not optimal for bandwidth efficiency. The demo uses a single channel approach where all statistics are bundled together in one message, regardless of which values have changed.
For example, a goal scored might generate a stats update message like this:
// Example of a partial stats update message after a goal
{
"statBox1": { // Score display
"info": [
{ "stat": "3-1" } // Only the score needs to be updated
]
},
"statBox5": { // Shots on goal
"info": [
{ "stat": "6" } // Update shots on goal count
]
}
}
This message would update only the score and shots on goal, without affecting other statistics like cards or player information.
Even with this approach, there are several advantages:
- Smaller message sizes: PubNub charges based on message size. By sending only the stats that changed rather than the complete set of stats, the demo uses less data, which lowers costs and helps messages arrive faster.
- Quicker delivery: Sending compact, focused updates helps messages be processed and delivered more quickly.
- Less network traffic: Unlike systems that need to send complete state updates, PubNub works great with this "just send what changed" approach, saving bandwidth while keeping all viewers in sync.
Bandwidth-efficient approach
For production applications with many concurrent viewers, a more efficient approach would use the channel architecture described earlier:
- Create separate channels for each statistic (e.g.,
game.match-stats.score
,game.match-stats.possession
) - Only publish updates when a specific statistic changes
- Use wildcard subscriptions to receive all stats (
game.match-stats.*
)
This approach provides several significant advantages:
- True partial updates - Only the changed statistics generate network traffic
- Minimal message sizes - Each message contains only a single statistic value
- Precise update targeting - Viewers can subscribe only to statistics they're interested in
- Reduced bandwidth consumption - Perfect for high-volume events with thousands or millions of viewers
For example, a goal scored in this architecture would generate a message like this:
// Published to channel: game.match-stats.score
{
"value": "3-1"
}
// Published to channel: game.match-stats.shots
{
"value": "6"
}
New viewers joining mid-game would need to fetch the most recent message from each statistic channel once, but this approach dramatically reduces ongoing bandwidth usage throughout the event.
For the client-side implementation, you would:
// Subscribe to all match stats
pubnub.subscribe({
channels: ['game.match-stats.*']
});
// Process incoming messages based on their channel
pubnub.addListener({
message: function(event) {
// Extract the stat type from the channel name
const statType = event.channel.split('.').pop();
// Update only that specific statistic
updateSpecificStat(statType, event.message.value);
}
});
show all 32 linesThis approach truly reduces message sizes and network traffic, unlike systems that need to send complete state updates, helping your application scale efficiently to large audiences.
Implementation best practices
When implementing match stats with PubNub, consider these best practices from the Live Events demo:
Optimize network efficiency
The stats implementation disables presence events since they're not needed:
const subscription = channel.subscription({
receivePresenceEvents: false
})
This reduces unnecessary network traffic, cost, and processing overhead, especially important during high-viewership events when thousands or millions of viewers might be watching simultaneously.
Clean subscription management
The demo properly manages subscriptions to prevent memory leaks and unnecessary background processing:
useEffect(() => {
// Create subscription and set up handlers
const subscription = channel.subscription({ receivePresenceEvents: false })
subscription.onMessage = messageEvent => {
processReceivedMessage(messageEvent.message)
}
subscription.subscribe()
// Clean up subscription when component unmounts
return () => {
subscription.unsubscribe()
}
}, [chat])
This cleanup ensures that resources are properly released when viewers navigate away from the live event.
Send only changed data
While the demo uses a less efficient approach, a best practice for production applications is to send only the specific statistics that have changed, rather than the entire stats object:
// Best practice: Send only the specific stats that changed
pubnub.publish({
channel: `game.match-stats.score`, // Use dedicated channel for each stat type
message: {
value: updatedScore
}
});
As described in the Efficient channel architecture section, this approach minimizes message size and reduces bandwidth usage, creating a more efficient and scalable system.
By implementing these best practices, you can create an effective, responsive match statistics system for your own sports applications, using PubNub's real-time infrastructure to keep fans engaged with the latest game data.