Build a TikTok game in Combos
Build Gift Arena: a small game where the streamer fights a boss and viewers supply energy, healing, and damage. You will get a complete project, a local gift simulator, a Combos publishing path, and the handoff required for real TikTok gifts. Uses Combos SDK 1.1.0.
Play the demo · Download the complete project
Real TikTok interaction currently has no available activation path. You can complete local development, simulation, and Combos publishing now. Publishing a post does not enable real gifts. The later integration materials and LIVE acceptance steps prepare for a future platform activation process; they are not an application flow you can complete today.
1. Declare gifts as game effects
Start with the rules, before writing connection code. In this example the player starts with 70 HP and 40 energy; the boss has 100 HP. An attack costs 10 energy, deals 10 damage, and triggers 8 damage in return if the boss survives. Viewers help through these three effects:
| In-game gift | Stable instruction | Effect for count = "1" | Effect for count = "3" |
|---|---|---|---|
| Battery | gift_battery | +20 energy | +60 energy |
| Heal | gift_heal | +15 HP | +45 HP |
| Flash | gift_flash | −10 boss HP | −30 boss HP |
// rules.js — this game's contract
export const GIFT_CATALOG = Object.freeze({
gift_battery: { target: 'energy', perCount: 20, max: 100 },
gift_heal: { target: 'hp', perCount: 15, max: 100 },
gift_flash: { target: 'bossHp', perCount: -10, max: 100 },
});This declaration lives in your game code. It is neither an SDK registration call nor a TikTok gift catalog. Battery/Heal/Flash are this example’s effect names, not real TikTok gift names or IDs. Do not assume a Rose or any price automatically maps to one of them.
Set these policies before integration: this game accepts integer counts 1–1000, caps HP/energy at 100 and boss HP at 0, and applies effects without consuming a player turn. A capped effect is successful even if the value cannot increase further. New commands after victory/defeat return false; start a new round to accept more. These are example game rules, not platform limits. For your game, explicitly design stacking, cooldowns, round transitions and expensive effects, and give the same rules to the person configuring the mapping.
2. Understand the game–platform relationship
Viewer → TikTok gift mapping → Combos → SDK onCommand → game effect → boolean result → Combos → TikTok acknowledgment.
| Owner | Responsibility |
|---|---|
| Your game | Declare supported instructions; render and commit effects; deduplicate; return the actual outcome |
| Combos host and SDK | Login and launch binding; session renewal; command transport, heartbeat, reconnection and result delivery |
| Combos / TikTok integration contact | Register the intended game entry and authorized accounts; configure the launch path and actual gift-to-instruction mapping |
One mapped interaction might produce instruction: "gift_flash", count: "3". Your game applies 30 damage. count is the mapped command quantity, not a coin price or necessarily the number of gifts. Confirm the mapping’s quantity rule, including combos, during registration. The game does not read launchData, sign TikTok requests, create a WebSocket, or implement webhooks itself.
3. Implement the command contract
type TTLiveCommand = {
messageId: string;
instruction: string;
count: string;
timestamp: string;
};| Field | What to do |
|---|---|
messageId | Treat as an opaque deduplication key. The same message must not apply the effect twice |
instruction | Match the stable string in your own catalog; reject unsupported instructions |
count | Decimal integer string. Validate first; only convert to Number after bounding it safely |
timestamp | Source command time as a string. It is not an identity, deduplication key or ordering guarantee |
The public command has no viewer ID, nickname or real gift ID. Do not build a per-viewer leaderboard from fields that this API does not supply.
Return true after the effect is committed, false for a known rejection, and throw if the outcome cannot be confirmed, such as a storage failure. An unknown outcome stays unacknowledged and may be retried. Catching every error and returning true loses gifts; returning false after an uncertain partial effect reports an unproven failure.
The SDK can resend pending results within a document. Your game must handle reloads too. The example saves game state and the message result in one IndexedDB transaction, waits for commit, then returns the boolean. A duplicate returns the saved boolean. A new round preserves receipts. The live save is scoped to the Combos account/game and separated from practice.
Read connection status
getTTLiveClient() returns one client per game document. getLiveStatus() checks it; onStatusChange() immediately emits current status, then changes. Status contains state, reason, action, and confirmedAtMs (last confirmed start/heartbeat in milliseconds; 0 means unknown).
connecting: starting.connected: interaction session confirmed.recovering: let the SDK reconnect.unknown: no current confirmation; it does not mean the broadcast ended.ended: the interaction session ended. Followaction:wait,none, orrelaunch_game. Forrelaunch_game, reopen this game from LIVE Studio; do not restart the broadcast just because of a connection issue.
4. Run the complete project
Prepare and launch
Download and extract the ZIP. You need a current browser and Python 3 for the local static server; Node and npm are not required. Keep the extracted tiktok-arena folder intact:
cd tiktok-arena
python3 -m http.server 8080 --bind 127.0.0.1Open http://127.0.0.1:8080/ and choose Start practice. Do not double-click index.html: JavaScript modules need an HTTP origin. The online demo works without installing anything. The game’s language selector supports the same ten languages as this site.
| File | Read or change it for… |
|---|---|
rules.js | Gift declaration, combat rules, count validation and deduplication |
storage.js | Atomic game-state and receipt storage |
connection.js | SDK capability checks, login, connection and cancellation |
game.js | The shared live/practice handler and UI |
index.html, style.css, draw.js, i18n.js | Layout, canvas artwork and ten-language UI |
Three reproducible demos
Start a new round before each of the first two rows. The recovery row continues the second demo without resetting. The initial values are HP 70, energy 40, boss HP 100. Every gift button creates a new message; Repeat last message reuses its ID.
| Demo | Actions | Expected result |
|---|---|---|
| Single gifts | Count 1; click Battery, Heal, Flash | Energy 60, HP 85, boss HP 90; each result true |
| Stacked gifts and replay | Count 3; click Flash, then Repeat | Boss HP 70 after both deliveries; second result true, duplicate: true |
| Recovery and rejection | Refresh after the previous demo, Start practice, Repeat; then Unknown instruction | Boss HP still 70; unknown instruction returns false, state unchanged |
Also try count 0, 1.5, 1001, and 9007199254740993: no effect and false. Test a win with a fresh round and Flash ×10, then send a new gift: it is rejected until a new round. Play with Attack and Charge to exercise normal combat. The Last delivery panel shows the input, result and final values. These are synthetic local commands; they never buy gifts or send TikTok ACKs.
5. Connect the same game inside Combos
Keep the supplied connection.js and game.js when publishing. Combos injects window.combos; do not install an imaginary npm SDK or add a second SDK script. The Connect Combos LIVE button does the following:
- Calls
init()and checks thegetTTLiveSessioncapability and client getter. That capability name is not a public method to call. - Runs
checkSession()and, if needed,login()from the button action. Cancellation stops the flow; successful login continues the same action. - Uses
getUserInfo()to scope the local save. The IDs are not credentials or server-side authorization proof. - Subscribes to status, then calls
getTTLiveClient().connect({ onCommand })with the same handler used in practice. - Keeps Disconnect / cancel usable while connection retries are pending. Cancellation or leaving the document unsubscribes and calls
disconnect().
The core handler below is already implemented in the download; the full version also refreshes the UI. store.update() is the transaction in storage.js, not an SDK API:
import { executeCommand } from './rules.js';
// `store` is the account/game-scoped store opened by prepareStore().
function commandHandler(store) {
return async (command) => {
const { result } = await store.update((saved) =>
executeCommand(saved, command),
);
return result; // State + receipt committed; no catch-and-return-false.
};
}connect() can stay pending while retrying; a resolved promise alone does not prove a real gift worked. Practice controls are hidden in LIVE mode. Verify both the visible effect and its execution result in a registered session.
6. Publish this project to Combos
Install the official Combos CLI . In the demo, click Save cover PNG. Place the resulting cover.png next to the tiktok-arena folder. From that parent folder, run:
combos --version
combos account login
combos post create --dir ./tiktok-arena --title 'Gift Arena' --cover ./cover.png --platform pc --screen-orientation landscape --allow-recommend=false
combos post getComplete the browser login using the intended publishing account. Open the returned share link and test the game inside its Combos player. Preserve the generated combos.json binding; update the same post from this directory with:
combos post update --dir ./tiktok-arena --platform pcThis sample is a static PC landscape game. The upload directory must contain index.html at its root and all relative assets. Publishing does not enable TT. A normal share link without the registered LIVE Studio launch context can run practice but cannot manufacture a real interaction session. See H5 publishing for the full publishing workflow.
7. Prepare materials for future integration
There is currently no available activation entry or confirmed onboarding process for real TikTok interaction. Game/account registration and real-gift mapping must wait for platform guidance; neither the SDK nor the CLI provides a public self-service registration API. Prepare the following materials for that future process. This is a preparation checklist, not an open application:
| Supply | Required detail |
|---|---|
| Game | Published Combos share URL, bound Post/project ID, game name and PC landscape target |
| Test access | The intended test/streamer account, via the approved onboarding channel; never a password or session token |
| Effect catalog | The three instruction strings, per-count effects, 1–1000 quantity rule, caps and behavior after round end |
| Mapping request | Which approved TikTok gift maps to each instruction, the quantity/combination rule and expected game result |
| Acceptance plan | Single gift, combo, redelivery/recovery and ended-session cases below |
Ask the contact to confirm the exact registered launch entry, game/account eligibility and completed gift mapping before testing. The actual TikTok gift IDs, names and prices must come from that configured catalog. For an agreed Flash ×3 mapping, the game should receive:
{
"instruction": "gift_flash",
"count": "3"
}Do not put TikTok secrets, CAS tokens or manually constructed launchData in the game. Those are handled by the platform. If registration is not complete, you have a published Combos game and a working simulator; real TikTok acceptance is still pending.
8. Validate real LIVE after activation becomes available
These steps become executable only after the platform provides activation and confirms the game entry, eligible account, and gift mapping. At present, complete the local simulator and Combos publication checks above; do not treat them as real LIVE acceptance.
- Use the approved account and open the registered game entry in TikTok LIVE Studio. In the game, connect Combos LIVE and confirm
connected. - With the test participant’s agreement, trigger one configured test interaction. Record
messageId, instruction/count, the before/after values, and the returned result. Ask the integration contact to confirm the platform received the corresponding ACK. Do not include credentials or viewer personal data in the record. - Repeat for the configured combo quantity. Confirm count × effect, caps, and no duplicated effect on redelivery. Compare with the local demos above.
- Exercise a temporary network interruption and recovery; the same known command must not apply twice. Refresh within the supported host flow and confirm state/receipt recovery; if the host asks for relaunch, follow it.
- End the interaction and check
ended/relaunch_game. Test cancel while connecting and leaving the game. Stale commands must not create another effect.
Browser storage protects this example only within the same browser/origin/account/game. It does not provide cross-device recovery or a trusted economy. Do not clear it during LIVE. At 10,000 receipts the sample stops accepting new commands rather than evicting old results. For persistent rewards or production scale, commit the effect and receipt in your own authoritative backend and define retention from confirmed outcomes.
When it does not work
| Symptom | Next action |
|---|---|
unsupported_host | Open the game in the supported Combos player; check init() capabilities and SDK availability |
login_cancelled / missing_profile | Sign in using the intended account, then retry the connection |
tt_not_launched, ended, or relaunch_game | Confirm registration/account eligibility and reopen the registered game from LIVE Studio |
| Connected, but no effect | Check the gift mapping, exact instruction/count and round state; ask the contact to trace the message ID |
false result | Check unknown instructions, invalid counts or an ended round; this is a known game rejection |
invalid_save, receipt_capacity, storage_blocked, or operation_failed | Stop LIVE testing; inspect browser storage availability and console locally. Preserve receipts, diagnose before restarting, and do not convert uncertainty into a successful ACK |