Tournaments
Updated: Mar 3, 2026
Copy for LLM
Tournaments are time-limited competitive events
This guide covers what tournaments are, how they work from the player’s perspective, how to create and manage tournaments using the SDK, tournament lifecycle, sharing mechanics, notification behavior, and best practices for designing engaging tournament experiences.
What Are Tournaments?
A tournament is a competitive event with a defined start time, end time, and scoring mechanic. Players join a tournament, play the game, and submit scores. When the tournament ends, players are ranked, and results are shared.
Tournaments differ from leaderboards in several important ways:
| Feature | Leaderboards | Tournaments |
|---|---|---|
Duration | Permanent or long-running | Time-limited (hours to days) |
Urgency | Low (play anytime) | High (must play before it ends) |
Shareability | Passive | Active (players invite friends to join) |
Lifecycle | Always active | Has creation, active, and ended phases |
Social dynamics | Ongoing comparison | Event-based competition with a clear conclusion |
The time-limited nature of tournaments is what makes them special. A tournament that ends in 24 hours creates urgency that a permanent leaderboard cannot. Players think: “I need to play now, or I will miss my chance.” This urgency drives engagement spikes and brings players back during the tournament window.
How Tournaments Work: The Player Experience
From the player’s perspective, the typical tournament flow is:
- Discovery: The player sees a tournament -- either through a friend’s share, an in-game prompt, or a notification.
- Joining: The player taps to join the tournament. They may see the current standings, the time remaining, and information about any rewards.
- Playing: The player plays the game and submits a score to the tournament.
- Checking standings: The player can check their rank relative to other participants at any time during the tournament.
- Sharing: The player shares the tournament with friends to invite them to compete.
- Returning: The player returns during the tournament period to improve their score or check if anyone has passed them.
- Results: When the tournament ends, the player sees the final standings.
SDK API Reference
Creating a Tournament
Use
FBInstant.tournament.createAsync() to create a new tournament. This method opens a native dialog where the player configures and shares the tournament.async function createTournament(initialScore) { try { const tournament = await FBInstant.tournament.createAsync({ initialScore: initialScore, config: { title: 'Weekend Challenge', image: 'data:image/png;base64,...', // Base64-encoded image sortOrder: 'HIGHER_IS_BETTER', scoreFormat: 'NUMERIC', endTime: Math.floor(Date.now() / 1000) + 86400, // Ends in 24 hours (Unix timestamp) }, data: { gameMode: 'challenge', difficulty: 'normal', }, }); console.log('Tournament created!'); return tournament; } catch (error) { if (error.code === 'USER_INPUT') { console.log('Player cancelled tournament creation'); } else { console.error('Failed to create tournament:', error); } return null; } }
Create Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
initialScore | number | Yes | The creating player’s initial score for the tournament. |
config.title | string | No | The display title of the tournament. |
config.image | string | No | A base64-encoded image for the tournament card. |
config.sortOrder | string | No | Score sorting: 'HIGHER_IS_BETTER' (default) or 'LOWER_IS_BETTER'. |
config.scoreFormat | string | No | How scores are displayed: 'NUMERIC' (default) or 'TIME'. |
config.endTime | number | No | Unix timestamp (in seconds) for when the tournament ends. |
data | object | No | Custom data associated with the tournament. Available to all participants. |
Use
FBInstant.tournament.shareAsync() to let the player share an active tournament with friends. This opens a native share dialog.async function shareTournament() { try { await FBInstant.tournament.shareAsync({ score: currentScore, data: { gameMode: 'challenge', }, }); console.log('Tournament shared!'); } catch (error) { if (error.code === 'USER_INPUT') { console.log('Player cancelled sharing'); } else { console.error('Failed to share tournament:', error); } } }
Joining a Tournament
When a player opens your game from a tournament share or notification, the game is automatically placed in the tournament context. You can detect this and retrieve tournament data:
async function checkForTournamentContext() { const entryPointData = FBInstant.getEntryPointData(); const contextType = FBInstant.context.getType(); if (entryPointData && entryPointData.tournamentID) { console.log('Player entered from a tournament!'); console.log('Tournament ID:', entryPointData.tournamentID); // Load the tournament game mode startTournamentMode(entryPointData); } }
Posting a Score
Use
FBInstant.tournament.postScoreAsync() to submit a score to the active tournament.async function postTournamentScore(score) { try { await FBInstant.tournament.postScoreAsync(score); console.log('Tournament score posted:', score); } catch (error) { console.error('Failed to post tournament score:', error); } }
The platform keeps track of the player’s best score. If the new score is lower than the existing best (for
HIGHER_IS_BETTER tournaments) or higher (for LOWER_IS_BETTER tournaments), the existing score is preserved.Getting Tournament Data
Use
FBInstant.tournament.getTournamentsAsync() to retrieve a list of tournaments the player is currently participating in.async function getMyTournaments() { try { const tournaments = await FBInstant.tournament.getTournamentsAsync(); tournaments.forEach(tournament => { console.log('Tournament ID:', tournament.getID()); console.log('Title:', tournament.getTitle()); console.log('End Time:', new Date(tournament.getEndTime() * 1000)); console.log('Context ID:', tournament.getContextID()); const payload = tournament.getPayload(); if (payload) { console.log('Custom data:', JSON.parse(payload)); } }); return tournaments; } catch (error) { console.error('Failed to get tournaments:', error); return []; } }
Tournament Object Methods
| Method | Return Type | Description |
|---|---|---|
getID() | string | The unique identifier of the tournament. |
getContextID() | string | The context ID associated with the tournament. |
getEndTime() | number | Unix timestamp (seconds) of when the tournament ends. |
getTitle() | string \| null | The display title of the tournament. |
getPayload() | string \| null | The custom data payload as a JSON string. |
Switching to a Tournament Context
Use
FBInstant.tournament.joinAsync() to switch the player into a specific tournament context.async function joinTournament(tournamentID) { try { await FBInstant.tournament.joinAsync(tournamentID); console.log('Joined tournament:', tournamentID); // The context has now switched to the tournament startTournamentMode(); } catch (error) { if (error.code === 'USER_INPUT') { console.log('Player cancelled joining'); } else { console.error('Failed to join tournament:', error); } } }
Tournament Lifecycle
Every tournament goes through three phases:
1. Creation
A tournament is created by a player using
FBInstant.tournament.createAsync(). During creation:- The player sets their initial score
- The tournament configuration (title, image, sort order, end time) is established
- The player is prompted to share the tournament with friends
- The tournament becomes active immediately
2. Active Period
During the active period:
- Players can join the tournament by tapping on a share or notification
- Participants play the game and submit scores using
postScoreAsync() - Scores are ranked according to the configured sort order
- Players can check standings and share the tournament to invite more friends
- New participants can join at any time before the tournament ends
3. Ended
When the tournament’s end time is reached:
- No more scores can be submitted
- Final rankings are determined
- Players can view the results
- The tournament context remains available for viewing but is no longer active
Tournament Notifications
The Facebook platform automatically handles certain notifications related to tournaments:
- Share notifications: When a player shares a tournament, their selected friends receive a notification inviting them to join.
- Score beat notifications: When a participant’s score is surpassed by another player, the platform may send a notification encouraging them to return and improve their score.
- Ending soon notifications: As the tournament end time approaches, participants may receive a reminder to submit their final scores.
These notifications are managed by the platform and do not require additional SDK calls from your game. However, you can complement them with your own Custom Updates for a richer experience.
Complete Code Example
Here is a complete example demonstrating the full tournament flow in a game:
// --- Game initialization --- async function initializeGame() { await FBInstant.initializeAsync(); FBInstant.setLoadingProgress(100); await FBInstant.startGameAsync(); // Check if the player entered from a tournament const entryData = FBInstant.getEntryPointData(); if (entryData && entryData.tournamentID) { await startTournamentMode(entryData); } else { await showMainMenu(); } } // --- Main menu with tournament options --- async function showMainMenu() { // Check for active tournaments const tournaments = await FBInstant.tournament.getTournamentsAsync(); if (tournaments.length > 0) { // Show active tournaments displayActiveTournaments(tournaments); } // Show "Create Tournament" button displayCreateTournamentButton(); // Show "Play Solo" button displayPlaySoloButton(); } // --- Creating a tournament --- async function onCreateTournamentClicked() { // First, play a round to get an initial score const initialScore = await playGameRound(); // Create the tournament with the initial score try { await FBInstant.tournament.createAsync({ initialScore: initialScore, config: { title: 'Can You Beat Me?', image: generateTournamentImage(initialScore), sortOrder: 'HIGHER_IS_BETTER', scoreFormat: 'NUMERIC', endTime: Math.floor(Date.now() / 1000) + 172800, // 48 hours }, data: { gameMode: 'classic', creatorName: FBInstant.player.getName(), }, }); showMessage('Tournament created and shared!'); } catch (error) { if (error.code !== 'USER_INPUT') { console.error('Tournament creation failed:', error); } } } // --- Playing in a tournament --- async function startTournamentMode(tournamentData) { showMessage('Tournament Mode!'); showTournamentTimer(tournamentData.endTime); const score = await playGameRound(); // Post the score to the tournament try { await FBInstant.tournament.postScoreAsync(score); showMessage(`Score submitted: ${score.toLocaleString()}`); } catch (error) { console.error('Failed to post score:', error); } // Offer to share or play again displayPostGameOptions(score); } // --- Post-game options --- async function displayPostGameOptions(score) { // Option 1: Share the tournament const shareButton = document.getElementById('share-tournament'); shareButton.onclick = async () => { try { await FBInstant.tournament.shareAsync({ score: score, data: { gameMode: 'classic' }, }); } catch (error) { if (error.code !== 'USER_INPUT') { console.error('Share failed:', error); } } }; // Option 2: Play again to improve score const playAgainButton = document.getElementById('play-again'); playAgainButton.onclick = async () => { const newScore = await playGameRound(); try { await FBInstant.tournament.postScoreAsync(newScore); showMessage(`New score: ${newScore.toLocaleString()}`); displayPostGameOptions(newScore); } catch (error) { console.error('Failed to post score:', error); } }; } // --- Joining an existing tournament --- async function onTournamentSelected(tournament) { try { await FBInstant.tournament.joinAsync(tournament.getID()); const payload = tournament.getPayload(); const data = payload ? JSON.parse(payload) : {}; await startTournamentMode({ ...data, endTime: tournament.getEndTime(), }); } catch (error) { if (error.code !== 'USER_INPUT') { console.error('Failed to join tournament:', error); } } } initializeGame();
Best Practices
Choose the Right Duration
Tournament duration significantly affects player behavior:
- Short tournaments (1-4 hours): Create intense, focused competition. Best for games with short play sessions and highly engaged audiences. Players check in frequently.
- Medium tournaments (12-48 hours): The sweet spot for most games. Long enough for players in different time zones to participate, short enough to maintain urgency.
- Long tournaments (3-7 days): Best for games where scores accumulate over multiple sessions. Allow casual players to participate on their own schedule, but may lose urgency.
For most games, 24-48 hour tournaments perform well. They give enough time for friends to discover and join while maintaining a sense of urgency.
Make Tournaments Accessible
Not every player is highly skilled. If your tournaments only reward the very best players, most participants will feel like they have no chance and stop engaging. Consider:
- Multiple skill brackets: Create separate tournaments for different skill levels, or match players with others of similar ability.
- Participation rewards: Give all participants something for joining, not just the winner. Even a small bonus for participating encourages people to try.
- Progress-based scoring: Instead of (or in addition to) skill-based scores, consider scoring based on improvement or activity. “Most improved” or “most games played” metrics give less skilled players a path to success.
Promote Sharing
Tournaments grow through sharing. Encourage players to share at multiple points:
- After creating a tournament: The creation dialog naturally prompts sharing, but you can also remind the player to share if they skip it.
- After submitting a score: “Share your score with friends and challenge them to beat it!”
- When a friend beats their score: “Sarah just passed you! Share the tournament and invite more friends to compete.”
- When time is running out: “Only 2 hours left! Make sure your friends have joined.”
Create a Visual Identity
Give each tournament a distinct visual identity with a custom image, title, and theme. This makes the tournament feel like a special event rather than just another leaderboard. If your game has seasonal content, tie tournaments to those themes.
Show Tournament Status Prominently
When a player is in an active tournament, always show:
- Their current rank
- The score they need to beat to move up
- Time remaining
- A button to play again or share
This information should be visible on the main screen, not hidden in a menu.
Stagger Tournament Availability
Rather than running tournaments continuously, stagger them so that there are natural gaps between events. This prevents tournament fatigue and makes each tournament feel special. Consider a cadence like:
- Daily mini-tournaments (1-4 hours) during peak hours
- Weekend tournaments (Friday to Sunday)
- Monthly special events with unique themes
Use Custom Data for Rich Experiences
The
data parameter in tournament creation allows you to attach custom data that all participants can read. Use this to:- Specify a game mode or difficulty level for the tournament
- Define special rules or modifiers
- Include theme information for visual customization
- Store any game-specific configuration
Next Steps
- Leaderboards -- Implement persistent competitive rankings alongside time-limited tournaments.
- Custom Updates -- Send rich messages to complement tournament notifications.
- Play With Friends -- Find friends to invite to your tournaments.
- Building Social Games -- Learn the strategic principles behind competitive social game design.