Skip to main content

Get Top Scores

The loadleaderboardtopscores method retrieves ranked scores from a leaderboard. You can filter by collection (social/global), timespan (daily/weekly/all-time), and limit the number of results returned.

Overview

Use this method to display leaderboard rankings in your game. It supports pagination, filtering by social circle, and time-based rankings.

Basic Usage

JavaScript Example

MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: 'high-scores',
collection: 1, // 0 = Social, 1 = Global
timespan: 0, // 0 = All-time, 1 = Weekly, 2 = Daily
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
result.scores.forEach((score, index) => {
console.log(`${score.playerRank}. ${score.playerName}: ${score.playerScore}`);
});
} else {
console.error('Failed to load scores');
}
}
});

TypeScript Example

import MoitribeSDK from '@veniso/moitribe-js';
import type { LeaderboardScoreData } from '@veniso/moitribe-js';

interface ScoresResult {
success: boolean;
scores: LeaderboardScoreData[];
msg?: string;
}

MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: 'high-scores',
collection: 1,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result: ScoresResult) => {
if (result.success) {
result.scores.forEach((score: LeaderboardScoreData) => {
console.log(`Rank ${score.playerRank}: ${score.playerName} - ${score.playerScore} points`);
console.log(`Submitted: ${new Date(score.timestamp * 1000).toLocaleDateString()}`);
});
}
}
});

Parameters

ParameterTypeRequiredDescription
leaderboardidstringYesThe leaderboard ID to query
collectionnumberYes0 for Social (friends), 1 for Global (all players)
timespannumberYes0 for All-time, 1 for Weekly, 2 for Daily
maxresultsnumberNoMaximum number of scores to return (default: 10)
onlyDatabooleanYesSet to true for programmatic access
callbackfunctionYesCalled with the result
info

The leaderboardid must match an ID from your leaderboard metadata. Use Get Metadata to retrieve valid IDs.

Response Format

The callback receives a result object:

{
success: boolean;
scores: LeaderboardScoreData[];
msg?: string; // Error message if success is false
}

LeaderboardScoreData Structure

Each score in the array has this structure:

interface LeaderboardScoreData {
playerRank: number; // Player's position (1, 2, 3...)
playerScore: number; // The actual score value
playerName: string; // Player's display name
leaderboardID: string; // ID of this leaderboard
timestamp: number; // When score was submitted (Unix timestamp)
player: Player; // Full player object with ID and details
iconURL: string; // Player's profile icon URL
hiresURL: string; // High-resolution profile image URL
scoreTag: string; // Custom metadata attached to score
}

Example Response

{
success: true,
scores: [
{
playerRank: 1,
playerScore: 5000,
playerName: 'Alex',
leaderboardID: 'high-scores',
timestamp: 1701234567,
player: { id: 'player_123', name: 'Alex' },
iconURL: 'https://cdn.moitribe.com/profiles/alex_icon.png',
hiresURL: 'https://cdn.moitribe.com/profiles/alex_hires.png',
scoreTag: '{"level":10,"difficulty":"hard"}'
},
{
playerRank: 2,
playerScore: 4500,
playerName: 'Jordan',
leaderboardID: 'high-scores',
timestamp: 1701234500,
player: { id: 'player_456', name: 'Jordan' },
iconURL: 'https://cdn.moitribe.com/profiles/jordan_icon.png',
hiresURL: 'https://cdn.moitribe.com/profiles/jordan_hires.png',
scoreTag: '{"level":9,"difficulty":"normal"}'
}
]
}

Common Use Cases

Display Top 10 Leaderboard

Show the top-ranked players:

function displayTopScores(leaderboardId) {
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: 1,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
const container = document.getElementById('leaderboard');
container.innerHTML = '<h2>Top Players</h2>';

result.scores.forEach((score) => {
const row = document.createElement('div');
row.className = 'leaderboard-row';
row.innerHTML = `
<span class="rank">${score.playerRank}</span>
<img src="${score.iconURL}" class="avatar">
<span class="name">${score.playerName}</span>
<span class="score">${score.playerScore.toLocaleString()}</span>
`;
container.appendChild(row);
});
}
}
});
}

displayTopScores('high-scores');

Load Multiple Timespans

Show daily, weekly, and all-time rankings:

function loadAllTimespans(leaderboardId) {
const timespans = [
{ value: 2, name: 'Daily', element: 'daily-scores' },
{ value: 1, name: 'Weekly', element: 'weekly-scores' },
{ value: 0, name: 'All-Time', element: 'alltime-scores' }
];

timespans.forEach((timespan) => {
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: 1,
timespan: timespan.value,
maxresults: 5,
onlyData: true,
callback: (result) => {
if (result.success) {
displayScoresInElement(result.scores, timespan.element, timespan.name);
}
}
});
});
}

function displayScoresInElement(scores, elementId, title) {
const container = document.getElementById(elementId);
container.innerHTML = `<h3>${title}</h3>`;

scores.forEach((score) => {
const item = document.createElement('div');
item.textContent = `${score.playerRank}. ${score.playerName}: ${score.playerScore}`;
container.appendChild(item);
});
}

loadAllTimespans('high-scores');

Switch Between Social and Global

Toggle between friend and global rankings:

let currentCollection = 1; // Start with global

function toggleCollection(leaderboardId) {
// Switch between 0 (social) and 1 (global)
currentCollection = currentCollection === 1 ? 0 : 1;

const buttonText = currentCollection === 1 ? 'Show Friends' : 'Show Global';
document.getElementById('toggle-btn').textContent = buttonText;

loadLeaderboardScores(leaderboardId, currentCollection);
}

function loadLeaderboardScores(leaderboardId, collection) {
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: collection,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
const title = collection === 0 ? 'Friends Rankings' : 'Global Rankings';
displayScores(result.scores, title);
}
}
});
}

// Button handler
document.getElementById('toggle-btn').onclick = () => {
toggleCollection('high-scores');
};

Paginate Results

Load more scores with pagination:

let currentPage = 0;
const scoresPerPage = 10;

function loadScorePage(leaderboardId, page) {
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: 1,
timespan: 0,
maxresults: scoresPerPage,
onlyData: true,
callback: (result) => {
if (result.success) {
displayScorePage(result.scores, page);

// Enable/disable navigation buttons
document.getElementById('prev-btn').disabled = (page === 0);
document.getElementById('next-btn').disabled = (result.scores.length < scoresPerPage);
}
}
});
}

function nextPage() {
currentPage++;
loadScorePage('high-scores', currentPage);
}

function previousPage() {
if (currentPage > 0) {
currentPage--;
loadScorePage('high-scores', currentPage);
}
}

// Initial load
loadScorePage('high-scores', 0);
info

Note: The current SDK implementation returns top scores starting from rank 1. True pagination with offset is not directly supported. The example above shows the pattern, but maxresults controls the total number returned.

Display with Score Metadata

Parse and display scoretag metadata:

function displayScoresWithMetadata(leaderboardId) {
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: 1,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
result.scores.forEach((score) => {
// Parse metadata from scoretag
let metadata = {};
try {
metadata = JSON.parse(score.scoreTag || '{}');
} catch (e) {
metadata = {};
}

console.log(`${score.playerRank}. ${score.playerName}: ${score.playerScore}`);
if (metadata.level) {
console.log(` Achieved on level ${metadata.level}`);
}
if (metadata.difficulty) {
console.log(` Difficulty: ${metadata.difficulty}`);
}
});
}
}
});
}

TypeScript Leaderboard Component

Create a reusable leaderboard component:

import type { LeaderboardScoreData } from '@veniso/moitribe-js';

interface LeaderboardOptions {
leaderboardId: string;
collection: 0 | 1;
timespan: 0 | 1 | 2;
maxResults?: number;
}

class LeaderboardDisplay {
private gameId: string;

constructor(gameId: string) {
this.gameId = gameId;
}

async loadScores(options: LeaderboardOptions): Promise<LeaderboardScoreData[]> {
return new Promise((resolve, reject) => {
MoitribeSDK(this.gameId, 'loadleaderboardtopscores', {
leaderboardid: options.leaderboardId,
collection: options.collection,
timespan: options.timespan,
maxresults: options.maxResults || 10,
onlyData: true,
callback: (result: { success: boolean; scores: LeaderboardScoreData[] }) => {
if (result.success) {
resolve(result.scores);
} else {
reject(new Error('Failed to load scores'));
}
}
});
});
}

renderScores(scores: LeaderboardScoreData[], containerId: string): void {
const container = document.getElementById(containerId);
if (!container) return;

container.innerHTML = '';

scores.forEach((score) => {
const row = this.createScoreRow(score);
container.appendChild(row);
});
}

private createScoreRow(score: LeaderboardScoreData): HTMLElement {
const row = document.createElement('div');
row.className = 'score-row';

// Add medal emoji for top 3
const medal = this.getMedalEmoji(score.playerRank);

row.innerHTML = `
<span class="rank">${medal || score.playerRank}</span>
<img src="${score.iconURL}" alt="${score.playerName}" class="avatar">
<span class="name">${score.playerName}</span>
<span class="score">${score.playerScore.toLocaleString()}</span>
`;

return row;
}

private getMedalEmoji(rank: number): string | null {
const medals: { [key: number]: string } = {
1: '🥇',
2: '🥈',
3: '🥉'
};
return medals[rank] || null;
}
}

// Usage
const leaderboard = new LeaderboardDisplay('my-game-id');

leaderboard.loadScores({
leaderboardId: 'high-scores',
collection: 1,
timespan: 0,
maxResults: 10
}).then((scores) => {
leaderboard.renderScores(scores, 'leaderboard-container');
}).catch((error) => {
console.error('Failed to load leaderboard:', error);
});

Formatting Score Values

Number Formatting

Display scores with proper formatting:

function formatScore(score, leaderboardType) {
if (leaderboardType === 'time') {
// Format as time (milliseconds to MM:SS)
const minutes = Math.floor(score / 60000);
const seconds = Math.floor((score % 60000) / 1000);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
} else if (leaderboardType === 'distance') {
// Format as distance
return `${(score / 1000).toFixed(2)} km`;
} else {
// Format as points
return score.toLocaleString();
}
}

// Usage
result.scores.forEach((score) => {
const formattedScore = formatScore(score.playerScore, 'points');
console.log(`${score.playerName}: ${formattedScore}`);
});

Date Formatting

Display when scores were submitted:

function formatTimestamp(timestamp) {
const date = new Date(timestamp * 1000);
const now = new Date();
const diffMs = now - date;
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);

if (diffMins < 60) {
return `${diffMins} minutes ago`;
} else if (diffHours < 24) {
return `${diffHours} hours ago`;
} else if (diffDays < 7) {
return `${diffDays} days ago`;
} else {
return date.toLocaleDateString();
}
}

// Usage
result.scores.forEach((score) => {
const timeAgo = formatTimestamp(score.timestamp);
console.log(`Submitted ${timeAgo}`);
});

Error Handling

Handle common errors when loading scores:

MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: 'high-scores',
collection: 1,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
if (result.scores.length === 0) {
showMessage('No scores yet. Be the first to submit a score!');
} else {
displayScores(result.scores);
}
} else {
console.error('Failed to load scores:', result.msg);

// Handle specific errors
if (result.msg.includes('leaderboard')) {
showError('Leaderboard not found. Please check the leaderboard ID.');
} else if (result.msg.includes('network')) {
showError('Network error. Please check your connection.');
} else {
showError('Unable to load leaderboard. Please try again later.');
}
}
}
});

Best Practices

1. Cache Recent Results

Avoid repeated API calls by caching results:

const leaderboardCache = {
data: null,
timestamp: null,
ttl: 60000 // Cache for 1 minute
};

function loadScoresWithCache(leaderboardId) {
const now = Date.now();

// Use cache if fresh
if (leaderboardCache.data &&
leaderboardCache.timestamp &&
(now - leaderboardCache.timestamp) < leaderboardCache.ttl) {
displayScores(leaderboardCache.data);
return;
}

// Load fresh data
MoitribeSDK('my-game-id', 'loadleaderboardtopscores', {
leaderboardid: leaderboardId,
collection: 1,
timespan: 0,
maxresults: 10,
onlyData: true,
callback: (result) => {
if (result.success) {
leaderboardCache.data = result.scores;
leaderboardCache.timestamp = now;
displayScores(result.scores);
}
}
});
}

2. Handle Empty Results

Provide feedback when no scores exist:

function displayScoresOrEmpty(scores) {
const container = document.getElementById('leaderboard');

if (scores.length === 0) {
container.innerHTML = `
<div class="empty-state">
<p>No scores yet!</p>
<p>Be the first to submit a score.</p>
</div>
`;
} else {
displayScores(scores);
}
}

3. Highlight Current Player

Emphasize the current player's rank:

function displayScoresWithHighlight(scores, currentPlayerId) {
scores.forEach((score) => {
const row = createScoreRow(score);

if (score.player.id === currentPlayerId) {
row.classList.add('current-player');
}

container.appendChild(row);
});
}

4. Optimize Image Loading

Load profile images efficiently:

function createOptimizedScoreRow(score) {
const row = document.createElement('div');
row.className = 'score-row';

const img = document.createElement('img');
img.src = score.iconURL;
img.alt = score.playerName;
img.loading = 'lazy'; // Lazy load images
img.onerror = () => {
img.src = 'default-avatar.png'; // Fallback
};

row.appendChild(img);
// Add other elements...

return row;
}

Next Steps

Related topics: