TikTok for Developers

Docs

Friend Leaderboard

Enable social engagement and drive retention by implementing friend leaderboards in your TikTok mini game. This guide covers the Open Data Domain architecture, API integration, and best practices for creating compelling leaderboard experiences.

The Open Data Domain is a secure, isolated environment that enables mini games to access social features without exposing sensitive user data directly to the main game environment. This architecture allows you to implement friend leaderboards while maintaining user privacy and platform security.

  • Friend Leaderboards: Display rankings of mutual followers who play your game
  • Achievement Surpassing: Show when players beat their friends' scores
  • Social Engagement: Leverage TikTok's social graph to drive competitive gameplay
  • User Retention: Use social connections to encourage repeat visits

User experience flow

The friend leaderboard integration follows a three-stage user journey:

Stage 1: Authorization Stage

When a user initiates an action that requires social data (such as clicking "View Friend Leaderboard"), the system requests authorization for:

  • Avatar and Nickname: Display name and profile picture for identity
  • Friend Relationship Chain: Mutual follower connections for the leaderboard

Incremental Authorization: If the user has previously authorized only one of these permissions, only the missing permission will be requested.

Stage 2: Data Retrieval Stage

After authorization, the game retrieves friend data from the cloud and renders the leaderboard.

Stage 3: Leaderboard Display Stage

The rendered leaderboard is displayed to the user, showing their ranking relative to friends.

System architecture

The Open Data Domain architecture consists of three main components:

Component overview

Component

Description

Responsibilities

Main Game Domain (Game Frame)

Your primary game environment

Game logic, user input, rendering game content

Open Data Domain (Open Data Frame)

Isolated iframe environment

Accessing social data, rendering leaderboards, handling friend interactions

Main Frame (Architecture)

Platform-managed bridge

Message routing, security enforcement, cross-domain communication

Communication flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   User Action   │────▶│  Game Frame     │────▶│  Authorization  │
│  (Click button) │     │  (Main Domain)  │     │     Request     │
└─────────────────┘     └─────────────────┘     └────────┬────────┘
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Leaderboard    │◀────│  Image Display  │◀────│  Authorization  │
│    Display      │     │                 │     │     Response    │
└─────────────────┘     └─────────────────┘     └─────────────────┘
┌───────┴─────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Open Data     │◀────│  Message Pass   │◀────│  Cloud Storage  │
│    Domain       │     │   (postMessage) │     │   Request       │
│ (Renders List)  │     │                 │     │                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘

The complete sequence flow:

  1. User clicks "View Leaderboard" button in the game
  2. Game Frame requests authorization from the platform
  3. Authorization dialog displays to the user
  4. User completes authorization
  5. Game Frame sends a postMessage to the Main Frame
  6. Main Frame forwards the message to the Open Data Frame
  7. Open Data Frame requests friend cloud storage data
  8. Platform queries the relationship chain and returns friend data
  9. Open Data Frame renders the leaderboard
  10. Rendered content is committed back through the Main Frame
  11. Main Frame converts content to a cross-domain image
  12. Image URL is passed to the Game Frame
  13. Game Frame displays the leaderboard on a "tainted" canvas overlay

Implementation guide

Developer responsibilities

As a game developer, you are responsible for:

  1. Integration Point: Display a "View Friend Leaderboard" button at appropriate moments in your game (e.g., after level completion, on the main menu, or in the world rankings screen)
  2. Customization: Design and implement your own leaderboard layout, styling, and display logic. The platform provides the data; you control the presentation.
  3. Optimization: Monitor key metrics including:
    • Leaderboard view button click-through rate (CTR)
    • Leaderboard exposure rate
    • User engagement with social features

Suggested integration points

  • Post-Level Completion: Show friend rankings immediately after a user completes a level
  • Main Menu: Provide persistent access to leaderboards from the main game screen
  • World Rankings: Offer a toggle between global and friend leaderboards
  • Achievement Milestones: Prompt users to check leaderboards when they achieve high scores

Note: TikTok is concurrently developing in-game direct message sharing capabilities that will integrate with friend leaderboards, enabling richer social interactions.

API Reference

Prerequisites

  • CLI Tool: Install the TikTok mini games CLI
  • npm install -g @ttmg/cli
  • Game Engine Support: Native open data domain currently supports Cocos games. For Cocos integration, refer to the Cocos Creator documentation on building open data contexts.

Project Configuration

Enable the Open Data Domain feature by modifying your game.json file:

{
  "dev": {
    "port": 9527
  },
  "openDataContext": "openDataContext",
  "app_id": "your_client_key"
}

The openDataContext field specifies the directory containing your open data domain code. The system automatically loads openDataContext/index.js as the entry point.

Canvas Configuration

The Open Data Domain renders on a shared canvas that overlays your main game. Configure the canvas position and dimensions:

let sharedCanvas = GameGlobal.tt.getSharedCanvas();
let sharedContext = sharedCanvas.getContext('2d');

// Set internal resolution (coordinate system)
sharedCanvas.width = 800;
sharedCanvas.height = 800;

// Set display size and position using CSS-style properties
sharedCanvas.style.width = '80vw';
sharedCanvas.style.height = '40vh';
sharedCanvas.style.top = '20vh';
sharedCanvas.style.left = '10vw';

Important: The canvas uses viewport-relative units (vw, vh) for responsive positioning across different screen sizes.

Game Domain APIs

These methods are called from your main game code.

getOpenDataContext

Retrieves a reference to the Open Data Context for cross-domain communication.

// TTMinis SDK
const { postMessage } = TTminis.game.getOpenDataContext();

// Native implementation
const { postMessage } = tt.getOpenDataContext();

// Send message to open data domain
postMessage({ type: 'show', data: {} });

authorizeOpenContext

Requests authorization to access the user's avatar, nickname, and friend relationship chain. This must be called before accessing any social features.

TTMinis.game.authorizeOpenContext({
  // When get_status_only is true, only checks status without showing UI
  get_status_only: false,
  success: (result) => {
    const { code } = result;
    // Use this code for server-side authentication
  },
  fail: () => {},
  complete: () => {}
});

// Native implementation
tt.authorizeOpenContext({
  get_status_only: false,
  success: (result) => {
    const { code } = result;
  },
  fail: () => {},
  complete: () => {}
});

Parameters

Field

Type

Description

Required

get_status_only

boolean

When true, only checks authorization status without displaying the authorization dialog

No

success

function

Callback on successful authorization

No

fail

function

Callback on authorization failure

No

complete

function

Callback when authorization flow completes

No

Success Callback Result

Field

Type

Description

code

string

Authorization code for server-side authentication

setUserCloudStorage

Stores user data in the cloud for persistence across sessions and for sharing with the leaderboard system.

TTMinis.game.setUserCloudStorage(options);

// Native
tt.setUserCloudStorage(options);

Options Object

Field

Type

Description

Required

data

Array

Array of key-value pairs to store

Yes

data[].key

string

Key name (max 128 characters)

Yes

data[].value

string

Value to store (max 2048 characters)

Yes

success

function

Callback on successful save

No

fail

function

Callback on save failure

No

complete

function

Callback when operation completes

No

Usage Example:

TTMinis.game.setUserCloudStorage({
  data: [
    { key: 'highScore', value: '15000' },
    { key: 'level', value: '25' }
  ],
  success: () => {
    console.log('Score saved successfully');
  }
});

removeUserCloudStorage

Removes specific keys from the user's cloud storage.

TTMinis.game.removeUserCloudStorage(options);

// Native
tt.removeUserCloudStorage(options);

Options Object

Field

Type

Description

Required

keyList

Array<string>

Array of keys to remove

Yes

success

function

Callback on successful removal

No

fail

function

Callback on removal failure

No

complete

function

Callback when operation completes

No

Open Data Domain APIs

These methods are called from within the Open Data Domain (openDataContext/index.js).

onMessage

Listens for messages sent from the main game domain.

tt.onMessage((data) => {
  if (data.command === 'render') {
    // ... rendering logic
  }
});

Animation Methods

Standard animation frame methods for smooth rendering:

// Request animation frame
const frameId = tt.requestAnimationFrame(() => {
  // Render logic
});

// Cancel scheduled animation frame
tt.cancelAnimationFrame(frameId);

Timer Methods

// Set timeout
const timeoutId = tt.setTimeout(() => {
  // Timeout logic
}, 1000);

// Clear timeout
tt.clearTimeout(timeoutId);

getSharedCanvas

Gets the shared canvas for rendering Open Data Domain content.

const canvas = tt.getSharedCanvas();
const ctx = canvas.getContext('2d');

canvas.width = 400;
canvas.height = 200;

createImage

Creates an image object for loading and displaying images.

const image = tt.createImage();
image.src = 'https://example.com/image.png';

getFriendCloudStorage

Retrieves cloud storage data for the user's friends, essential for building leaderboards.

GameGlobal.tt.getFriendCloudStorage({
  keyList: ['highScore', 'level'],
  success: (data: getFriendCloudStorageResponse) => {
    GameGlobal.tt.showInfo('success:' + JSON.stringify(data));
  },
  fail: (err) => {
    GameGlobal.tt.showInfo('fail:' + JSON.stringify(err));
  },
  complete: () => {
    console.log('=============> complete');
  }
});

Response Interface

interface FriendCloudStorageData {
  display_name?: string;
  avatar_url?: string;
  open_id?: string;
  data?: UserCloudStorageKVData[];
  user_id?: number;
}

interface UserCloudStorageKVData {
  key?: string;
  value?: string;
}

interface getFriendCloudStorageResponse {
  data?: FriendCloudStorageData[];
  status_code?: number;
  status_msg?: string;
}

Response Fields

Field

Type

Description

display_name

string

Friend's display name

avatar_url

string

URL to friend's avatar image

open_id

string

Friend's unique OpenID

data

Array

Array of key-value storage data

user_id

number

Friend's user ID

getUserCloudStorage

Retrieves the current user's cloud storage data within the Open Data Domain.

GameGlobal.tt.getUserCloudStorage({
  keyList: ['key_test_abc'],
  success: (data: getUserCloudStorageResponse) => {
    GameGlobal.tt.showInfo('success:' + JSON.stringify(data));
  },
  fail: (err) => {
    GameGlobal.tt.showInfo('fail:' + JSON.stringify(err));
  },
  complete: () => {
    console.log('=============> complete');
  }
});

Response Interface

interface UserCloudStorageKVData {
  key?: string;
  value?: string;
}

interface getUserCloudStorageResponse {
  data?: UserCloudStorageKVData[];
}

setUserCloudStorage (Open Data Domain)

Sets cloud storage data from within the Open Data Domain.

GameGlobal.tt.setUserCloudStorage({
  data: [{ key: 'key_test_abc', value: 'value_test_abc' }],
  success: (data) => {
    GameGlobal.tt.showInfo('success:' + JSON.stringify(data));
  },
  fail: (err) => {
    GameGlobal.tt.showInfo('fail:' + JSON.stringify(err));
  },
  complete: () => {
    console.log('=============> complete');
  }
});

removeUserCloudStorage (Open Data Domain)

Removes cloud storage data from within the Open Data Domain.

GameGlobal.tt.removeUserCloudStorage({
  keyList: ['key_test_abc'],
  success: (data) => {
    GameGlobal.tt.showInfo('success:' + JSON.stringify(data));
  },
  fail: (err) => {
    GameGlobal.tt.showInfo('fail:' + JSON.stringify(err));
  },
  complete: () => {
    console.log('=============> complete');
  }
});

Social Sharing Methods

shareToUser

Initiates sharing to a specific friend via direct message. Can only be called from within the Open Data Domain.

tt.shareToUser({
  imageUrl: 'https://example.com/share-image.png',
  openid: 'friend_openid_from_leaderboard',
  subtitle: 'Check out my new high score!',
  templateType: 1,
  title: 'Beat my score!',
  success: () => {
    console.log('==========> success');
  },
  fail: () => {
    console.log('=========> fail');
  }
});

Parameters

Field

Type

Description

Required

imageUrl

string

URL of image to display in share card

Yes

openid

string

Friend's OpenID (obtained from leaderboard data)

Yes

subtitle

string

Subtitle text in share card

No

templateType

number

Visual template style: 1 or 2

No

title

string

Main title in share card

No

success

function

Callback on successful share

No

fail

function

Callback on share failure

No

Template types

Two visual templates are available:

  • Template Type 1: Standard share card with title, subtitle, and image
  • Template Type 2: Alternative layout with different visual styling

shareAppMessage

Initiates sharing via direct message from the main game domain, opening the contact selection interface. Can only be called from the main game domain.

tt.shareAppMessage({
  imageUrl: 'https://example.com/share-image.png',
  subtitle: 'Join me in this amazing game!',
  templateType: 1,
  title: 'Play with me!',
  query: 'a=1&b=2',
  path: 'sub_package_path',
  success: () => {
    console.log('========> success');
  },
  fail: () => {
    console.log('=========> error');
  }
});

Parameters

Field

Type

Description

Required

imageUrl

string

URL of image to display in share card

Yes

subtitle

string

Subtitle text

No

templateType

number

Visual template style: 1 or 2

No

title

string

Main title

No

query

string

Query string appended to launch parameters

No

path

string

Subpackage path for loading specific game content

No

success

function

Callback on successful share

No

fail

function

Callback on share failure

No

shareToStory

Allows users to share content to their TikTok story. Can only be called from the main game domain.

tt.shareToStory({
  success: () => {
    console.log('==========> success');
  },
  fail: () => {
    console.log('=========> fail');
  }
});

Implementation example

Main Game Domain Code

class LeaderboardManager {
  private openDataContext: any;

  constructor() {
    this.openDataContext = TTMinis.game.getOpenDataContext();
  }

  // Show friend leaderboard
  showLeaderboard() {
    // Request authorization
    TTMinis.game.authorizeOpenContext({
      get_status_only: false,
      success: (result) => {
        // Send message to open data domain
        this.openDataContext.postMessage({
          type: 'showLeaderboard',
          data: {
            authCode: result.code
          }
        });
      },
      fail: (error) => {
        console.error('Authorization failed:', error);
      }
    });
  }

  // Hide leaderboard
  hideLeaderboard() {
    this.openDataContext.postMessage({
      type: 'hide'
    });
  }

  // Save high score
  saveHighScore(score: number) {
    TTMinis.game.setUserCloudStorage({
      data: [
        { key: 'highScore', value: score.toString() }
      ],
      success: () => {
        console.log('High score saved');
      }
    });
  }
}

Open Data Domain Code (openDataContext/index.js)

// Listen for messages from main domain
tt.onMessage((message) => {
  switch (message.type) {
    case 'showLeaderboard':
      renderLeaderboard();
      break;
    case 'hide':
      clearCanvas();
      break;
  }
});

// Get canvas context
const canvas = tt.getSharedCanvas();
const ctx = canvas.getContext('2d');

// Render leaderboard
function renderLeaderboard() {
  tt.getFriendCloudStorage({
    keyList: ['highScore'],
    success: (result) => {
      const friends = result.data;

      // Clear canvas
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // Sort by high score
      friends.sort((a, b) => {
        const scoreA = parseInt(a.data?.[0]?.value || '0');
        const scoreB = parseInt(b.data?.[0]?.value || '0');
        return scoreB - scoreA;
      });

      // Draw leaderboard
      friends.forEach((friend, index) => {
        const y = index * 80 + 20;

        // Draw rank number
        ctx.fillStyle = '#333333';
        ctx.font = 'bold 20px sans-serif';
        ctx.fillText(`${index + 1}`, 20, y + 40);

        // Draw avatar
        const avatar = tt.createImage();
        avatar.src = friend.avatar_url;
        avatar.onload = () => {
          ctx.drawImage(avatar, 60, y, 60, 60);
        };

        // Draw name
        ctx.fillStyle = '#333333';
        ctx.font = '16px sans-serif';
        ctx.fillText(friend.display_name, 140, y + 30);

        // Draw high score
        const score = friend.data?.[0]?.value || '0';
        ctx.fillStyle = '#666666';
        ctx.font = '14px sans-serif';
        ctx.fillText(`Score: ${score}`, 140, y + 50);
      });
    }
  });
}

function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

Best practices

User experience

  1. Timing: Display leaderboard prompts at meaningful moments (after level completion, on high score achievement)
  2. Visibility: Make the "View Leaderboard" button prominent but non-intrusive
  3. Progressive Disclosure: Start with a simple button and expand to full leaderboard on interaction

Performance

  1. Data Caching: Cache friend data locally to reduce API calls
  2. Lazy Loading: Load leaderboard data only when requested by the user
  3. Canvas Optimization: Minimize redraws in the Open Data Domain

Privacy and compliance

  1. Incremental Authorization: Request only the permissions you need
  2. Clear Value Proposition: Explain to users why accessing their friend data enhances their experience
  3. Graceful Degradation: Provide alternative experiences for users who decline authorization

Troubleshooting

Issue

Possible Cause

Solution

Authorization dialog not appearing

get_status_only set to true

Set get_status_only: false to show the authorization UI

Friend data empty

User has no mutual followers playing the game

Display a message encouraging users to invite friends

Canvas not displaying

Incorrect canvas positioning

Verify style.width, style.height, style.top, style.left values

postMessage not working

Open Data Domain not properly configured

Check game.json has correct openDataContext path

Images not loading in Open Data Domain

Cross-origin restrictions

Ensure image URLs allow cross-origin access

Was this document helpful?
TikTok for Developers