Live polls & score predictions
Engagement is a key driver of success in live sports, media streaming, and interactive user apps. Real-time polling keeps fans and users connected, enhancing the experience with instantaneous feedback and predictions. By the end of this document, you will:
- Understand how to implement real-time polling in your application
- Learn how to create and manage polls efficiently
- Enhance engagement with different types of polls and predictions
- Discover how to handle poll results and user feedback
- Implement secure and scalable polling systems
How PubNub helps
PubNub provides a robust infrastructure for implementing real-time polling systems. The platform's features enable you to create engaging polling experiences that scale globally.
Role in the solution | PubNub feature (click to learn more!) |
---|---|
| Pub/Sub |
| Message Storage |
| Access Manager |
Use case overview
PubNub's real-time infrastructure enables you to create various types of polls for different engagement scenarios. Whether you're running a live sports event, a virtual conference, or an interactive game show, PubNub provides the tools to implement real-time polling that keeps your audience engaged.
The Live Events demo app showcases PubNub's real-time capabilities for implementing match polls and side polls, but there are many other polling scenarios you can implement:
Poll Type | Description |
---|---|
Event Predictions |
|
Quick Engagement |
|
Audience Feedback |
|
Gamification & Quizzes |
|
Planning your poll timing
When planning your poll timing strategy, it's important to consider the event flow and critical moments where polls would be most engaging. Take into account your audience's engagement patterns and time zones, especially for global audiences. The complexity of your poll and the time needed for decision-making should also influence your timing choices. Don't forget to consider how your polls integrate with other engagement features. Getting the timing right can make a significant difference in participation rates and overall engagement.
Basic setup for polling
To implement real-time polling with PubNub, you'll need to set up your channels and configure proper security settings.
First, initialize the PubNub client with your credentials and security settings:
const pubnub = new PubNub({
publishKey: 'your-publish-key',
subscribeKey: 'your-subscribe-key',
userId: 'ID-to-uniquely-identify-the-poll-participant'
});
SDK configuration
You can use any PubNub SDK to implement this solution. For optimal performance and security:
- Use unique user IDs for each user, but reuse it for different client devices ( mobile, tablet, via browser) of the same user.
- Keep your publish and subscribe keys secure.
- Consider using environment variables for sensitive data.
For more information on available configuration options, refer to the Configuration documentation of the SDK you're using. The Live Events demo app uses the JavaScript SDK via the JavaScript Chat SDK, which provides chat-specific methods as well as the core JavaScript SDK operations, but that's not important to illustrate how to work with polls.
Architectural approaches
When implementing polling systems, there are two main architectural approaches to consider:
Minimal channel approach
This approach uses a minimal set of channels to handle the entire polling lifecycle. It's suitable for most polling implementations and provides a good balance of simplicity and functionality.
Channel Type | Channel Name | Purpose | Permissions |
---|---|---|---|
Poll Broadcasting | game.new-poll | Broadcast poll questions and choices | Users need read permission |
Vote Submission | game.poll-votes | Submit individual votes | Users need write permission |
Results | game.poll-results | Broadcast ongoing and final results | Users need read permission |
For vote submission, you have two options for structuring the data:
-
include IDs in the channel name:
Channel: game.poll-votes.{pollId}
Message: {"choice": "A"} -
include IDs in the message payload:
Channel: game.poll-votes
Message: {"pid": "p1", "qid": "q1", "choice": "A"}
Many channels approach
This approach uses a more granular channel structure, creating separate channels for different aspects of the polling system. It's useful for:
- Complex polling scenarios with multiple question types
- Systems requiring fine-grained access control
- Applications needing to scale to very large numbers of concurrent polls
If you decide to go with the multiple channel approach, you can customize the channel structure based on your specific requirements such as separate channels for different question types, dedicated channels for specific user groups, and specialized channels for analytics or monitoring.
Match and side polls
In the Live Events demo app, we implement two types of polls: match polls and side polls, but you can implement any type of poll you want using PubNub.
The actual polls are regular messages with some additional metadata. Real-time messaging forms the core of PubNub's platform, delivering messages globally in under 100 milliseconds.
A PubNub message can contain any kind of serializable data, like objects, numbers and UTF-8 encoded strings. Its format may be plain text, a URL-encoded object, or most commonly, JavaScript Object Notation (JSON). The max size of a message is 32 Kibibytes (KiB). You can check the size of your message payload using our message size calculator.
Configure PubNub channels
Channels are the fundamental building blocks of PubNub's communication infrastructure. Think of channels as dedicated pathways or rooms where messages flow between connected clients. For more information on channels, refer to Channels.
To implement real-time polling, you'll need to establish dedicated channels for different aspects of the polling lifecycle.
const pollDeclarations = 'game.new-poll' // New poll announcements
const pollVotes = 'game.poll-votes' // User votes
const pollResults = 'game.poll-results' // Poll results
Create and announce polls
Creating a poll involves publishing a message to the poll declarations channel.
At its foundation, PubNub operates a global network of data centers that manage message delivery through persistent socket connections. When a user sends a message, it travels to the nearest PubNub edge server, is instantly replicated across the network, and delivered to all subscribers on that channel.
The Live Events demo app uses the PubNub JavaScript SDK under the hood, so the code snippets will be written in TypeScript. However, you can use any PubNub SDK to implement this solution.
Each poll is designed to contain a single question, but you can also implement polls with multiple questions.
Here's a basic example:
const poll = {
id: "unique-poll-id",
title: "Who will win the match?",
type: "prediction",
duration: 600, // Duration in seconds
showAt: Date.now(), // When to show the poll
options: [
{ id: 1, text: "Home Team" },
{ id: 2, text: "Away Team" },
{ id: 3, text: "Draw" }
]
};
// Store the poll in history for reference
await pubnub.publish({
show all 19 linesManaging message storage
When deciding on message storage, you'll want to think about your specific needs. For important polls that you'll want to reference later or use for analytics, storing them in history is essential. On the other hand, quick engagement polls might work better with short-term storage.
Keep an eye on message size to maintain storage efficiency, and set appropriate TTL values based on your requirements. Message persistence is particularly useful for maintaining audit trails and supporting analytics (unless you're using Events & Actions which doesn't require message persistence).
For more information on message storage, refer to the Message Persistence documentation.
Rate limiting and scaling
When implementing live polls at scale, it's important to consider rate limiting and scaling strategies to ensure reliable performance and prevent system overload.
Message rate limits
PubNub enforces certain rate limits to maintain system stability. For more information on PubNub's limits and scaling capabilities, refer to the API Limits documentation.
Scaling strategies
To handle high-volume polling scenarios:
- Channel organization
- Message optimization
- Client-side rate limiting
- Server-side considerations
- Use separate channels for different types of polls (e.g.,
game.match
,game.side
). - Implement channel groups for efficient subscription management.
- Consider using wildcard subscriptions for related poll channels.
- Mind the 32 KiB message size limit.
- Use sending by POST for larger payloads.
- Implement efficient message structures to minimize data transfer.
- Implement client-side throttling to prevent message flooding.
- Use exponential backoff for retry attempts.
- Set appropriate timeouts for operations.
- Implement proper error handling and retry logic.
- Use message persistence for important poll data.
- Monitor system performance and adjust limits as needed.
Collect and process votes
When a user votes, send their choice to the votes channel. Your backend server should handle vote validation and counting:
// Client-side code for submitting votes with timestamps
async function submitVote(pollId, questionId, choiceId) {
await pubnub.publish({
channel: pollVotes,
message: {
pollId: pollId,
questionId: questionId,
choiceId: choiceId
// No need to include userId - PubNub will add publisher automatically
}
});
}
// PubNub Function for handling vote updates with timetoken
export default async (request) => {
show all 56 linesVote validation and counting
Always perform validation on the server side to ensure the integrity of your polls. Here are some key considerations:
- Prevent duplicate votes from the same user
- Validate that votes are submitted within the poll's active period
- Verify that the selected option is valid for the poll
- Handle concurrent votes to prevent race conditions
- Implement proper error handling and logging
- Ensure the user has permission to vote in the poll
- Verify the poll is currently live when the user attempts to vote
The Live Events demo app uses a simplified approach to vote validation and counting. In a production environment, your code needs to be more robust.
Single vote per user
To enforce single votes per user, you have two options:
-
Using PubNub Functions with KV Store (recommended for serverless implementations):
show all 38 lines// In a PubNub Function
const db = require('kvstore');
export default (request) => {
const userId = request.message.userId;
const pollId = request.message.pollId;
const questionId = request.message.questionId;
const choice = request.message.choice;
// Create unique key for this user's vote
const voteKey = `${userId}-${pollId}-${questionId}`;
return db.getItem(voteKey)
.then(existingVote => {
if (existingVote) { -
Using a backend server (recommended for more complex implementations):
show all 35 lines// In your backend server
async function handleVoteMessage(msg) {
const userId = msg.userId;
const pollId = msg.pollId;
const questionId = msg.questionId;
const choice = msg.choice;
// Create unique key for this user's vote
const voteKey = `${userId}-${pollId}-${questionId}`;
try {
// Check if user has already voted
const existingVote = await yourDatabase.get(voteKey);
if (existingVote) {
Choose the approach that best fits your architecture:
- Use PubNub Functions with KV Store for serverless implementations where you want to keep everything within the PubNub ecosystem.
- Use a backend server when you need more complex logic, integration with other services, or have specific database requirements.
Multiple question polls
When implementing polls with multiple questions, you need to consider how to handle partial completion and result inclusion.
Scenario | Description |
---|---|
Polls and quizzes with multiple questions |
|
Abandoned polls |
|
Viewing results |
|
Timestamp tracking for vote updates
When handling vote updates, it's important to implement proper timetoken tracking to ensure data consistency and handle edge cases:
- Determine the most recent vote in case of multiple submissions from the same user
- Track when votes were submitted relative to the poll's active period
- Resolve conflicts when votes are submitted simultaneously
- Maintain an audit trail of voting activity
The timetoken of the last received update should be used to determine the most recent vote in case of multiple submissions from the same user.
Using PubNub's timetoken
PubNub automatically adds a timetoken to every message when it's published. To convert a timetoken to a Unix timestamp (seconds), divide the timetoken by 10,000,000 (10^7).
Here's sample code that demonstrates handling timestamp tracking for votes:
// Client-side code for submitting votes with timestamps
async function submitVote(pollId, questionId, choiceId) {
await pubnub.publish({
channel: pollVotes,
message: {
pollId: pollId,
questionId: questionId,
choiceId: choiceId
// No need to include userId or timestamp - PubNub will add publisher and timetoken automatically
}
});
}
// PubNub Function for handling vote updates with timetoken
export default async (request) => {
show all 56 linesFor multiple question polls, you can extend this approach to track completion status:
// Track completion status for multi-question polls
async function trackPollCompletion(pollId, userId) {
const db = require('kvstore');
const userPollKey = `${pollId}-${userId}`;
try {
// Get poll configuration
const pollConfig = await db.get(`poll-config-${pollId}`);
if (!pollConfig) return false;
// Get all votes for this user in this poll
const votes = await db.get(`poll-votes-${pollId}-${userId}`);
if (!votes) return false;
// Count unique questions answered
show all 58 linesPublish and handle results
After the voting period ends, publish the results.
Your backend server should calculate the final vote counts and publish the results
Even though it's not a requirement for polls, you might want to gamify the poll experience by rewarding the user for completing a poll.
async function publishResults(pollId, correctOption, pollType) {
await pubnub.publish({
channel: pollResults,
message: {
id: pollId,
correctOption: correctOption,
pollType: pollType
},
storeInHistory: true
});
}
Processing poll results
When processing poll results, you'll need to carefully calculate and validate the final vote counts. It's often helpful to implement a short delay before publishing results to ensure all votes are properly counted. Make sure to store the results for future analytics and reference. If you're using a scoring system or leaderboards, remember to update them with the new results. Don't forget to send notifications to relevant users to keep them informed about the outcome.
For more information on receiving messages, refer to the Receive Messages documentation.
User interface
Creating an engaging user interface is crucial for successful polling implementation. This section covers key considerations and best practices for building effective polling interfaces.
Poll display components
When designing your polling interface, focus on creating a cohesive experience that guides users through the entire polling process.
Start with a well-designed poll container that makes the current poll immediately visible, with a clear, easy-to-read question and accessible voting options. This foundation ensures users can quickly understand what they're voting on and how to participate.
The voting interface should be intuitive, with a straightforward selection mechanism and clear visual feedback when options are selected or votes are submitted. Make sure to handle invalid votes gracefully with appropriate error messages, helping users correct their mistakes without frustration.
For the results display, you may consider implementing real-time updates to show votes as they come in, with clear visualizations that make the data easy to understand. When available, include historical context to help users understand the significance of the current results.
Throughout the interface, maintain a mobile-responsive design that works seamlessly across all devices. This ensures a consistent experience regardless of how users access your polls, whether they're on a desktop computer, tablet, or smartphone.
Here's a basic example of a poll component:
interface Poll {
id: string;
title: string;
options: Array<{
id: number;
text: string;
score?: number;
}>;
isActive: boolean;
userVote?: number;
}
function PollComponent({ poll }: { poll: Poll }) {
const [selectedOption, setSelectedOption] = useState<number | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
show all 182 linesMore features
The above scenario presents the minimum use of PubNub features to increase fan participation. Take a look at the following additional features that can further enhance your fan engagement.
Poll security
To implement real-time polling, you'll need to establish dedicated channels for different aspects of the polling lifecycle. These channels should be secured using Access Manager:
const pollDeclarations = 'game.new-poll' // New poll announcements
const pollVotes = 'game.poll-votes' // User votes
const pollResults = 'game.poll-results' // Poll results
// Configure Access Manager for secure channel access
await pubnub.grant({
channels: [pollDeclarations, pollVotes, pollResults],
authKeys: ['user-auth-key'],
read: true,
write: true,
ttl: 1440 // Token expiration in minutes (24 hours)
});
When securing your channels, consider the following:
- Set appropriate TTL (Time-To-Live) values for tokens to ensure they expire as needed.
- Use unique authentication keys tailored to different user roles.
- Regularly update credentials to enhance security.
- Keep track of access patterns to detect any anomalies.
- Grant write access to the poll declarations channel only to poll creators.
- Allow write access to the votes channel only to voters.
- Provide write access to the results channel only to results publishers.
- Ensure all users have read access to the necessary channels.
For more information, refer to the Access Manager documentation.
Interactive quizzes
Interactive quizzes are a powerful way to engage your audience while testing their knowledge, from simple trivia to complex educational assessments.
Quiz types and features
Quiz Type | Description | Use Cases |
---|---|---|
Multiple choice |
|
|
True/False |
|
|
Timed quizzes |
|
|
Simple quiz implementation example
Consider the following example of a simple quiz implementation an addition to the basic polling implementation. Here's an example of how to implement a simple quiz using PubNub:
// Quiz question structure
const quizQuestion = {
id: "q1",
type: "multiple-choice",
question: "What is the capital of France?",
options: [
{ id: "a", text: "London" },
{ id: "b", text: "Paris" },
{ id: "c", text: "Berlin" },
{ id: "d", text: "Madrid" }
],
correctAnswer: "b",
timeLimit: 30, // seconds
points: 10
};
show all 36 lines