Pular para o conteúdo principal
LiveBridgeLiveBridge

Quickstart

This is exactly the guide the starter kit gives your AI assistant (AGENTS.md). Hand it to Claude, Cursor or Copilot and ask for a game.

Workflow

  1. npx @livebridge/dev init my-game creates a folder from the starter game (id: 3-40 chars of a-z, 0-9 and -, immutable later).
  2. Edit game.js, index.html, style.css, manifest.json.
  3. npx @livebridge/dev serve runs it at http://localhost:5173/index.html?t=lbg_dev. Also open http://localhost:5173/__panel: a simulator panel to fire gifts, chat, likes, follows, shares, joins, change the viewer name and avatar, and switch name/avatar sending on and off.
  4. npx @livebridge/dev check runs the server's own static checks locally. It must be clean.
  5. Once: create a token at https://livebridge.vn/app/dev/cli then npx @livebridge/dev login. After that npx @livebridge/dev upload (add --test-only to check without releasing; add --create if the game does not exist on the web yet).
  6. Every upload must raise `version` in manifest.json (for example 1.0.0 to 1.0.1).

AGENTS.md, CLAUDE.md, README.md, hidden files (starting with .) and node_modules are not part of the upload.

The LiveBridge SDK

Load it with exactly this line (before the game script). Do not copy or bundle the SDK: serve swaps this URL for a local copy, and in production it is LiveBridge's own.

<script src="https://livebridge.vn/sdk/livebridge.js"></script>
<script>
  const game = new LiveBridgeGame({
    onAction(actionId, event, rule) { /* a gift the streamer mapped to one of your actions */ },
    onGift(event) { /* EVERY gift, mapped or not */ },
    onChat(comment, user, event) {},
    onFollow(user, event) {},
    onShare(user, event) {},
    onMember(user, event) {},   // someone joined the room
    onLike(user, event) {},     // event.like.count = likes in this batch
  });
</script>

All handlers (all optional):

  • onReady(config, actionMap): the SDK has read the manifest and the gift mapping. config is your manifest.json. `actionMap` can be `null` if the first read failed (network error, 429): the mapping then arrives later through onConfig. Never assume actionMap is set.
  • onConfig(actionMap): the streamer just saved the gift mapping (no page reload), or the mapping arrived late after onReady.
  • onAction(actionId, event, rule): a gift matched a rule. rule has actionId, amount (default 1), and giftName or minDiamonds/maxDiamonds.
  • onGift(event): every gift. Always called, also when onAction is.
  • onChat(comment, user, event), onFollow(user, event), onShare(user, event), onMember(user, event), onLike(user, event).
  • onEvent(event): every raw event (gifts, chat, ...).
  • onConnect(), onDisconnect(): socket opened / closed. The SDK reconnects after a few seconds unless the link was revoked.
  • onTikTokStatus("connected" | "connecting" | "ended" | "disconnected"): state of the streamer's TikTok room. The socket can be open while the streamer has not pressed connect for TikTok.
  • apiOrigin (optional): only needed if you self-host the SDK. Do not.
  • Helpers: LiveBridgeGame.resolveGiftAction(actionMap, event), LiveBridgeGame.getDiamonds(event) (= diamondCount x repeatCount), LiveBridgeGame.getUser(event); game.destroy() stops it.

Event shape (after the privacy filter; optional fields may be missing, check before using):

{ type: "gift", seq, ts, channel,
  user: { id, uniqueId, nickname, avatar, roles?: { moderator, subscriber, follower, member } },
  gift: { id, name, diamondCount, repeatCount, icon? } }          // chat: chat: { comment }
                                                                   // like: like: { count, total? }

gift.icon is always "" for developer games (the CSP blocks gift images from the TikTok CDN and LiveBridge has no gift-image proxy yet): draw your own gift icons if you need them.

Gift mapping rules: exact gift name > diamond range (diamondCount x repeatCount within minDiamonds..maxDiamonds) > the rule whose actionId is "default". The streamer can change the mapping at any time, so always handle onGift for unmapped gifts too.

Privacy (the streamer chooses per game). Names and avatars are sent by default. When the streamer turns names off: user.id becomes anon-<16 hex>, user.uniqueId becomes viewer-<4 hex>, user.nickname becomes Viewer #<4 hex>; each viewer always gets the same code within one room and game. user.avatar is an image URL through the LiveBridge proxy (https://livebridge.vn/api/avatar?u=<uniqueId>&s=96, allowed by the CSP; the proxy answers a default image when it has none), never a TikTok CDN URL. When avatars are off, and also when names are off (the proxy URL carries the real name): user.avatar is "". The game must look right in both cases (for example an initial letter instead of an avatar).

Token mode. The game only has ?t=. Without ?t= the SDK runs in demo mode (no connection, no error). A revoked link (the streamer pressed "Disconnect this game", changed TikTok account, an admin took the game down) closes the socket with code 4003: the SDK stops for good and calls onDisconnect; show "Link expired, reopen the game from LiveBridge". window.__LIVEBRIDGE__.status is idle, open, closed or revoked.

Create `new LiveBridgeGame({...})` as soon as the page loads, not after a click. The automatic checker opens the game in a real browser and waits for status open.

manifest.json

JSON Schema: https://livebridge.vn/schemas/game-manifest.json. Rules:

  • id: the id of the game you created, ^[a-z0-9-]{3,40}$, immutable; no reserved word (admin, api, app, www, sdk, dev, developers, games, static, cdn, mail, livebridge, test) or existing game id.
  • version: x.y.z, greater than the live one.
  • name, summary, description: each has both vi and en, non-empty, no blocked words.
  • platform: "web". connection: { "needsApp": false, "sendUsername": true, "sendAvatar": true } (needsApp is always false).
  • entry: path of the main HTML file from the folder root (case-sensitive). Uploaded games use entry, not url.
  • cover: a 16:9 cover image (for example 1280x720) that exists in the folder. screenshots: at most 8 images. tags: at most 5.
  • connectOrigins: where the game may call over the network, as https://host or wss://host (lower case, no path, no *). Default [].
  • actions: what the game can do, each { id, name, name_vi, description, description_vi }. defaultGiftMapping: the suggested mapping, each row has either giftName or minDiamonds (optionally with maxDiamonds), plus an actionId that exists in actions.

Forbidden

  • No network access outside `connectOrigins` (fetch, XHR, WebSocket, images, fonts beyond what the CSP section allows). If you need your own API, declare its origin in connectOrigins.
  • Never store or send viewer data anywhere else: names, avatars, ids and comments are for display inside the game only; do not write them to localStorage, cookies, IndexedDB or your own server, and do not put them in URLs.
  • Never ask for, read or use `?key=` or the streamer's connection code (room key). The game only receives ?t=.
  • Do not assume a keyboard or mouse: the game is used while the streamer is live, and the players are viewers acting through gifts and chat.
  • No external analytics (Google Analytics, Sentry, Mixpanel, ...), no iframes or ad scripts.
  • Viewer data is untrusted: show it with textContent, never innerHTML, eval or document.write.
  • No illegal, sexual, extreme-violence, hateful or real-money gambling content. LiveBridge may take a game down at any time (see the developer terms).
  • Use relative paths (game.js, ./img/a.png), never starting with /: the game runs under https://<game-id>.<sandbox domain>/v/<version>/.

CSP (the sandbox content security policy)

Every game file is served with this CSP; serve applies the same one so you see problems early:

  • default-src 'self'.
  • Scripts: the game's own files, LiveBridge's /sdk/, inline scripts, WebAssembly ('wasm-unsafe-eval'), and the CDNs https://cdnjs.cloudflare.com, https://cdn.jsdelivr.net, https://unpkg.com.
  • Network calls (fetch, XHR, WebSocket): the game itself, the LiveBridge API, and connectOrigins.
  • Images, audio, video, fonts: the game's files, data:, blob:, the LiveBridge API, the three CDNs and connectOrigins (https). Google Fonts is allowed (CSS from fonts.googleapis.com, fonts from fonts.gstatic.com).
  • Web Workers: the game's files or blob:. No <object>, <base>, <form action>.
  • The game can be embedded in OBS and the dashboard (no frame-ancestors), but cannot embed outside pages.

How to test

  • npx @livebridge/dev serve [folder] --port 5173. It simulates LiveBridge's token endpoints (/api/game-token/actions|status|manifest, /ws?t=lbg_dev) with the fake token lbg_dev. The gift mapping comes from defaultGiftMapping in manifest.json (a real streamer may change it).
  • Panel http://localhost:5173/__panel: gift buttons (a sample of TikTok gifts, with repeat count), chat, like x N, follow, share, join, "random viewer" (name + avatar), send-name / send-avatar switches (see what the game gets when the streamer turns them off), and a "resend config" button (exercises onConfig).
  • After each change reload the game page, then run npx @livebridge/dev check.
  • On upload LiveBridge also opens the game in a real browser against a fake API: the page must finish loading within 20 seconds, throw no uncaught JavaScript error, and the SDK must connect.

Pre-upload checklist (check codes and fixes)

check and upload print issues as [code] (field) with a "Fix" line. The usual fixes:

  • zip_invalid: re-create the archive with a normal zip (no password, no rar/7z). The CLI zips for you so this is rare.
  • zip_too_big: more than 50 MB unpacked. Compress images/audio, delete unused files, or load big assets from a CDN.
  • zip_too_many_files: more than 2,000 files. Bundle JS/CSS, combine images into sprite sheets.
  • zip_path_escape: a path has .., \ or starts with /. Rename the file.
  • zip_symlink: a symbolic link. Replace it with the real file.
  • zip_duplicate: two files differ only by letter case. Rename one.
  • manifest_missing: no manifest.json at the folder root.
  • manifest_invalid: one field is wrong. Read the field name in the issue and fix it against the JSON Schema.
  • version_not_newer: version is not greater than the live one. Raise it.
  • version_exists: this version number was already uploaded. Raise version.
  • entry_missing: entry does not point to a real file. Fix the path (case-sensitive).
  • cover_missing: the cover (or a screenshots image) is missing. Add the file or fix the path.
  • file_type_forbidden: only html htm js mjs css json png jpg jpeg gif webp avif svg ico mp3 ogg wav m4a mp4 webm woff woff2 ttf otf wasm txt map are allowed. Delete the others.
  • words_blocked: the name/summary/description has an unsuitable word. Reword it.
  • url_not_https, url_unreachable: for external_web games (uploaded on the web, not through the CLI).
  • smoke_timeout: the page did not respond within 20 seconds. Look for an infinite loop or heavy work at load.
  • smoke_js_error: a JavaScript error during the test run. Open ?t=lbg_dev, read the Console, fix it.
  • smoke_sdk_not_connected: the SDK did not connect. Load https://livebridge.vn/sdk/livebridge.js and call new LiveBridgeGame({...}) on page load.
  • smoke_load_failed: the entry could not be opened. Check entry and that the file exists.
  • smoke_infra: a problem on the checker's side. Upload the same version again in a few minutes; no need to change the version.
  • sdk_missing (CLI only, a warning): no .html file loads /sdk/livebridge.js.

Limits: at most 10 games per developer, 20 uploads per day, the files of the 5 newest versions are kept.

How to read a check failure

Every issue has a code, a field (if any), a description and a "Fix" line in Vietnamese and English. Work in this order: (1) fix the first issue exactly as its Fix line says; (2) run check again; (3) repeat. If the developer pastes you text that starts with LiveBridge upload check failed for <game> v<version>: (the Sao chép lỗi cho AI / copy-for-AI button on the web, or the last block of check), that is the issue list: fix each item.