@laplace.live/persona-sdk 是外掛 API 的具型別用戶端,也是其傳輸 schema 的事實來源。本頁是型別參考;外掛 API 講的是如何開啟伺服器、金鑰與方法一覽
同構——Node 22+、Bun、瀏覽器與 OBS 瀏覽器來源都能執行——唯一的執行階段相依套件是 zod,用來支撐這些傳輸 schema
npm install @laplace.live/persona-sdk下面這些表格直接對應 SDK 自己的宣告。所有內容都從套件入口匯出,因此一行
import type { … } from '@laplace.live/persona-sdk' 就能取到全部
你能用它做什麼
Persona Console 是這套 SDK 能做到什麼的官方參考。它是整個應用程式的遙控器——場景、圖層以及填滿圖層的素材庫、表情、動作、參數綁定、快速鍵、追蹤與設定——全在一個瀏覽器分頁裡,版面按直播途中用手機操作來設計
控制台不依賴桌面版程式碼。它的全部功能都透過外掛 API 實作,因此你也可以用同一套 SDK 實作這些功能
開發用戶端時可參考以下做法:
- 按能力判斷,而不是按格式。每個依實例劃分的區塊都會讀取
InstanceRuntime.capabilities後自行停用,而不是先呼叫、再處理unsupported-for-format - 把重新連線當成一次快取清空。socket 斷線期間錯過的事件不會補發,快取可能已與伺服器狀態不一致,重新連線時應清除快取並重新取得資料
- 處理好終止性的關閉碼。
4001與4002代表停止重新連線;其餘情況都值得重試 - 訂閱交給 SDK 管。
persona.on(…)會在重新連線後重新訂閱,呼叫端無需手動管理重新連線後的訂閱
從 HTTPS 網頁使用未加密的 ws:// 連線時,請透過 127.0.0.1 連線到本機的
Persona。若要連線到另一台電腦,請以 HTTP 提供網頁,或透過 Persona 前面的 TLS
代理連線——見 控制台說明
LLM 提示詞
把下面的提示詞貼給你的 AI 助理,或者存進專案的 CLAUDE.md、AGENTS.md 這類規則檔案,它就有了替你寫外掛所需的全部背景。提示詞以英文寫成,方便各家模型理解;裡面引用的文件網址都可以直接抓取
You are helping me build a plugin for LAPLACE Persona, a desktop VTuber app
(https://laplace.live/persona). Persona exposes a local WebSocket server called the
Plugin API, and `@laplace.live/persona-sdk` is its typed client.
Before writing any code, fetch the reference documents listed at the end — they are
the single source of truth for method names, events, error codes and types. Do not
rely on memory and do not invent names.
## Setup
- Install `@laplace.live/persona-sdk`. Isomorphic: Node 22+, Bun, browsers and OBS
browser sources. Its only runtime dependency is zod.
- The user enables the server in Persona under Settings → Plugin API and creates an
API key there. Tokens look like `sk-lp-v1-…`. Default endpoint: `ws://127.0.0.1:25034`.
- Everything — values and types — is exported from the package root; there is no
other entry point.
## Client
```ts
import { PersonaClient } from "@laplace.live/persona-sdk";
const persona = new PersonaClient({
token: "sk-lp-v1-…", // required; everything else is optional
clientInfo: { name: "My Plugin", version: "1.0.0" }, // display-only identity
});
await persona.connect();
const { scenes, activeSceneId } = await persona.call("scene.list");
await persona.call("scene.patch", {
background: { mode: "color", color: "#00ff00", imageAssetId: null },
});
persona.on("motion.started", (m) => console.log("playing", m.group));
const mouth = persona.driveParameter("MouthOpen", 0.8); // parameter lease
mouth.set(0.3);
mouth.release();
```
`call(method, params?)` is fully typed — the method name determines the request and
response shapes. Methods cover scenes, model instances, stage objects, expressions,
motions, speech, hotkeys, automations, model and asset registration, settings,
tracking and pose input, per-key storage and parameter injection; events notify you
of scene, selection, expression, motion, speech, tracking and registry changes. The
exact method and event names, parameter shapes, error codes and limits live in the
reference below — look them up instead of guessing. Gate features on the
capabilities reported by `app.info` and `instance.list`, never on version strings.
## Reference
Fetch these before writing code. The documentation is served as raw Markdown —
append `.mdx` to any page URL (`.en.mdx` for English):
- https://laplace.live/persona/plugin-api.en.mdx — server setup, API keys, and every
method, event, error code and limit
- https://laplace.live/persona/sdk.en.mdx — the SDK client: options, auth modes and
every exported type用戶端
import { PersonaClient } from "@laplace.live/persona-sdk";
const persona = new PersonaClient({ token: "sk-lp-v1-…" });
await persona.connect();
const { scenes, activeSceneId } = await persona.call("scene.list");
await persona.call("scene.patch", {
background: { mode: "color", color: "#00ff00", imageAssetId: null },
});
persona.on("motion.started", (m) => console.log("playing", m.group));PersonaClient 用這些選項建構。只有 token 是必填的
Prop
Type
ClientInfo
clientInfo 決定了你的應用程式會以什麼名字出現在 Persona 設定的已連線的用戶端下。它是可選的、自行宣告的、僅供顯示——絕不用於授權——SDK 會在每次重新連線時重新宣告一遍
const persona = new PersonaClient({
token,
clientInfo: { name: "My Overlay", version: "1.2.0", developer: "You" },
});Prop
Type
認證
預設情況下權杖以 ?token= 查詢參數傳遞,這也是瀏覽器唯一能做到的方式。在 Node 裡,你可以改為提供一個會設定請求標頭的 socket,把權杖當成 Authorization: Bearer 標頭傳送——用另外安裝的 ws 套件:
import WebSocket from "ws";
const persona = new PersonaClient({
token,
auth: "header",
createWebSocket: (url, headers) => new WebSocket(url, { headers }),
});WebSocketLike 是 createWebSocket 必須回傳的 socket 形態。全域的 WebSocket 與 ws 用戶端在結構上都滿足它,不需要任何型別斷言
Prop
Type
參數租約
driveParameter 持續為模型參數提供一個值,並回傳一個 InjectionHandle。心跳由 SDK 負責,租約在最後一次寫入約一秒後到期——所以行程意外結束時,參數會自動還原,而不是卡在那裡
const mouth = persona.driveParameter("MouthOpen", 0.8);
mouth.set(0.3);
mouth.release();同一個參數同一時刻只能由一個工作階段持有;第二個會拿到 conflict 錯誤
第三個參數還能帶上 weight,以及一個 onError——心跳在背景失敗時由它回報,呼叫端不必去 await 每一次續租
Prop
Type
關閉
onClose 回報伺服器或網路發起的每一次關閉,附上原始 socket 的關閉碼與原因,以及 terminal——表示用戶端是否停止重新連線。它在用戶端處理完自己的那部分(狀態、待決呼叫)之後才觸發,所以在裡面呼叫 close() 是安全的;你自己發起的 close() 不會回報。一旦提供了它,終止性關閉就不再走 onWarning
Prop
Type
PersonaClientState
type PersonaClientState = "closed" | "connecting" | "open" | "reconnecting";常數
| 匯出項 | 值 | 意義 |
|---|---|---|
PROTOCOL_VERSION | 4 | 傳輸格式發生破壞性變更時遞增;伺服器會在 hello 裡回報自己的版本 |
DEFAULT_API_HOST | 127.0.0.1 | 除非使用者開啟區域網路存取,伺服器只綁定這裡 |
DEFAULT_API_PORT | 25034 | 除非你更動,應用程式就監聽這個連接埠 |
SCENE_ACTIVATION_TIMEOUT_MS | 120000 | 沒設定 requestTimeoutMs 時,SDK 等待 scene.activate 的時間 |
CLOSE_KEY_REVOKED | 4001 | 終止性關閉碼——金鑰已被撤銷 |
CLOSE_FORCE_DISCONNECTED | 4002 | 終止性關閉碼——使用者中斷了這個工作階段 |
CLOSE_SERVER_STOPPING | 1001 | 伺服器正在停止。不是終止性的——等它回來重連 |
INJECT_LEASE_TTL_MS | 1000 | 注入的參數在最後一次寫入多久之後還原 |
INJECT_HEARTBEAT_MS | 100 | SDK 多久重送一次持有中的租約 |
STORAGE_KEY_MAX_LENGTH | 128 | 儲存鍵名的最大長度 |
STORAGE_VALUE_MAX_LENGTH | 65536 | 單一值序列化之後的長度上限 |
STORAGE_KEYS_MAX | 256 | 一把 API 金鑰能持有的鍵數 |
SPEECH_URL_MAX_LENGTH | 8000000 | speech.play 的 url 上限,夠放下一段約 40 秒的 base64 WAV |
API_ERROR_CODES、EVENT_NAMES、INPUT_NAMES、APP_CAPABILITIES、FPS_LIMIT_PRESETS 與 EFFECTS_QUALITY_LEVELS 以 as const 陣列的形式提供,所以選擇器可以精確列出應用程式接受的取值。isApiErrorCode、isEventName 與 isAppCapability 是它們的型別守衛,injectTargetKey 則為注入目標建構標準的鍵
personaWsUrl 把主機、連接埠與一個 secure 旗標——或者使用者填的那一行位址——組裝成 socket 的 URL:host、host:port、裸寫或帶方括號的 IPv6,以及原樣照搬的完整 ws(s):// URL。權杖不屬於它,PersonaClient 會自己附加。parsePort 與 isValidPort 是配套的輸入驗證
模型與素材
模型與素材共用同一種項目形態。kind 永遠表示這個項目是什麼,origin 永遠表示它從哪來——bundled 是隨應用程式附帶的,user 是使用者註冊的——旁邊還有應用程式內容清單宣告的創作者署名
Prop
Type
type ModelFormat = "live2d" | "vrm";
type ContentOrigin = "bundled" | "user";
type AssetKind =
| "image"
| "video"
| "audio"
| "prop"
| "ibl"
| "lut"
| "animation"
| "cameraMotion";
type InventoryKind = ModelFormat | "pngtuber" | AssetKind | "effect";effect 是唯一不指向檔案的那一種:素材庫用它列出 內建特效
model.list 回傳 ModelRef,也就是 kind 收窄為 ModelFormat 的 ContentRef。asset.list 回傳 AssetRef,它把 kind 收窄為 AssetKind 並多出一個 exists——檔案一旦從磁碟上消失就是 false
Prop
Type
Prop
Type
CatalogItem
CatalogItem 是選擇器使用的內容中繼資料型別,與傳輸方式無關。本機註冊表與遠端目錄項目都會轉換成此型別。它在 ContentRef 的基礎上移除安裝前尚未確定的 origin,並新增目錄資訊。download 描述如何下載尚未安裝的項目;缺少此欄位表示項目已在本機
Prop
Type
擺放
螢幕空間以像素計,原點是舞台中心;世界空間以公尺計。傳輸格式裡的旋轉一律是弧度,儘管面板上顯示的是角度
Prop
Type
Prop
Type
Place2D 與 Place3D 是物件版本——欄位相同,多一個 opacity
場景項目
場景的 items 陣列按 z 序同時存放模型與物件
type SceneItem = SceneModelItem | SceneObjectItem;Prop
Type
Prop
Type
Prop
Type
ObjectContent
物件渲染的內容,按 kind 區分。除 capture 之外,目前都可以從介面裡建立——見 圖層
type ObjectContent =
| { kind: "image"; assetId: string }
| {
kind: "video";
assetId: string;
loop: boolean;
muted: boolean;
volume: number;
}
| { kind: "prop"; assetId: string }
| {
kind: "web";
url: string;
width: number;
height: number;
fps: number;
transparent: boolean;
css: string;
layer: WebLayer;
shutdownWhenHidden: boolean;
}
| { kind: "capture"; source: CaptureKind; sourceId: string; label: string };
type ObjectSpace = "2d" | "3d";
type WebLayer = "behind" | "front";
type CaptureKind = "display" | "window";Prop
Type
釘附
Prop
Type
AttachAnchor
type AttachAnchor =
| { kind: "root" }
| { kind: "bone"; bone: string }
| {
kind: "artMesh";
id: string;
verts: [number, number, number];
weights: [number, number, number];
};object.anchors 會以 AnchorOption 的形式回傳父物件提供的錨點——一個錨點加上它的顯示名稱。VRM 給的是模型根節點加上它的人形骨骼,Live2D 給的是模型根節點加上它的每一個 ArtMesh,每個 ArtMesh 列還帶有 order,即取得清單時的繪製順序
AttachDepth
type AttachDepth =
{ kind: "front" } | { kind: "behind" } | { kind: "artMesh"; id: string };depth 是 Live2D 物品 在所附著模型內部的位置——它的後方、前方,或它某個 ArtMesh 的正上方。只有附著到 Live2D 模型的 Live2D 實例才會用到它,由 instance.attach 設定
Prop
Type
depthStops(meshes) 依從後到前的順序排出深度控制項的各格——後方、依繪製順序每個 ArtMesh 的上方、前方;depthStopIndex(meshes, depth) 找出某個深度落在哪一格,父模型已經沒有的 ArtMesh 視為最前一格。pinRiders(items, instanceId) 回傳直接或經由一串釘選附著在 instanceId 上的所有實例,它永遠不能釘到這些實例上;pinnableParents(models, items, instanceId) 據此產生釘選選擇器的列,用 ridesThis 標出需要停用的那些。defaultAttach(parentInstanceId) 是一個全新的釘選:錨點在根部、位於前方、不切分,也沒有任何調校
Prop
Type
Prop
Type
場景
Prop
Type
Prop
Type
Prop
Type
背景
Prop
Type
type BackgroundMode = "transparent" | "color" | "image";相機
Prop
Type
Prop
Type
cameraDistanceLimits(environment, radius) 回傳滑鼠導覽遵守的距離範圍:DISTANCE_MIN 與 DISTANCE_MAX,即 0.35 與 20;環境裡的 3D 場景擺得比約 10 公尺寬的房間更大或更小時,範圍依比例放寬。radius 是 3D 場景自身的包圍半徑,取自 scene.inspect 回傳的 environment.radius——場景載入完成之前為 null,舊版建置不回傳——傳 null 則得到預設範圍
燈光
Prop
Type
type SceneLightType = "directional" | "point" | "ambient" | "area" | "spot";
type ShadowQuality = "off" | "low" | "medium" | "high" | "ultra";
type ShadowFilter = "pcf" | "pcss";SCENE_LIGHT_TYPES、SHADOW_QUALITY_LEVELS 與 SHADOW_FILTERS 以 as const 陣列提供光源類型、陰影等級與過濾方式,順序與面板中的一致
followCamera 讓燈光的位置與角度相對相機計算,followCameraOptions 選擇它跟隨相機的哪些運動——DEFAULT_LIGHT_FOLLOW_CAMERA_OPTIONS 三項全開——followCameraReference 則保存關掉的選項定格下來的部分。想切換跟隨又不移動燈光,請呼叫 scene.setLightFollowCamera,不要直接改這個欄位,請參閱 燈光
Prop
Type
Prop
Type
iblAssetId 與 modelAssetId 分別指定環境貼圖與 3D 場景,帶 environment-map-model 能力的建置可以同時接受兩者。寫它們請走 environmentWithAsset(),它設定你選的那一項、保留另一項,並丟棄跟著舊場景走的燈光覆寫。選中貼圖會關掉 bakeFromModel;只有原本既沒有貼圖也沒有 3D 場景時,新場景才會把它開啟,在兩個 3D 場景之間替換時保持不變;傳 null 則兩項都清掉。environmentClearPatch(environment, slot) 產生移除 'model'、'map' 或(傳 null 時)兩者的修改:模型會帶走 bakeFromModel 與自己的燈光覆寫,只移除貼圖時照明交給剩下的 3D 場景。另外兩個輔助函式對應面板上那兩顆按鈕:applyEnvironmentLighting() 開啟兩條燈光路由並把場景自己的光壓到理想強度,applyEnvironmentLook() 將 EnvironmentLook 套用到場景
Prop
Type
Prop
Type
Prop
Type
Prop
Type
SCENE_VOLUMETRIC_LIGHTING_SPECS 給出霧氣各滑桿的範圍,defaultSceneVolumetricLighting() 回傳場景初始的霧氣設定,healSceneVolumetricLighting() 把任意值修正到範圍內。霧氣依燈開啟,走 SceneLight.volumetric:sceneLightCanHaze(type) 判斷某種類型能否點亮霧氣,sceneLightHazes(light) 判斷某盞燈此刻是否正在點亮
特效
SceneEffects 是刻意扁平的:每個「開關加數值」的特效都放在頂層,工具因此可以通用地走訪它們。某個特效出現在面板的哪個位置,是呈現層的選擇,不是結構
Prop
Type
哪些特效出現在 圖層清單 裡,由 environment.effectLayers 另外記著——一串 ToggleEffectKey,也就是這份註冊表裡所有「開關加數值」的鍵。它和 enabled 是兩件事:enabled 管開著沒有,effectLayers 管有沒有一列。把某個特效 patch 成開,它的列會被自動補上,所以外掛只寫 enabled 就夠了
type SceneToneMapping = "none" | "neutral" | "aces" | "agx";Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
漸層與邊緣光共用一套 24 種混合模式 EffectBlendMode,EFFECT_BLEND_MODES 依面板中的順序列出它們
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
rain 和 snow 在結構上和其他特效並無二致,行為卻不同:它們是場景裡的幾何體,而不是後製鏈上的一環。參見 雨 和 雪
模型與物件也各有自己的一套特效——SceneModelItem 與 SceneObjectItem 上的 effects 與 effectLayers,先於場景特效生效,透過 instance.setEffects 編輯。它只包含 LayerEffectKey 這個子集:color、levels、colorWheels、colorShift、selectColors、gradient、blur、bloom、diffusion、rim、outline 與 dropShadow。泛光的顏色選取欄位只在這裡生效。參見 圖層特效
轉場
transition 是進入場景時播放的效果——見 場景轉場。不帶 scene-transitions 能力的建置不會提供它。SCENE_TRANSITION_TYPES 列出所有類型,defaultSceneTransition() 回傳每個場景初始的直接切換,healSceneTransition() 把任意值修正到範圍內
Prop
Type
environment.itemTransition 是場景裡模型、物件與生成物體出現與消失的方式——見 顯示與隱藏。舊版建置不帶這個欄位。ITEM_TRANSITION_STYLES 列出所有樣式,defaultItemTransition() 回傳每個場景初始的 300 毫秒 glitch,ITEM_TRANSITION_MAX_MS 是 5000 毫秒的上限
Prop
Type
執行階段狀態
instance.list 會回報舞台上每個項目實際能做什麼,用戶端因此可以直接停用某個控制項,而不是靠錯誤才發現
Prop
Type
type InstanceCapability =
| "motions"
| "expressions"
| "placement-2d"
| "placement-3d"
| "mtoon"
| "idle-clips"
| "idle-motions"
| "pose"
| "live2d-params"
| "speech";speech 只有已載入的 Live2D 模型才會回報,所以它只出現在執行階段這一份能力表裡,format 上是看不出來的。應用程式自身的能力是另一份,由 hello 與 app.info 回報——功能開關要依它判斷,而不是去比對 version
type AppCapability =
| "storage"
| "speech"
| "automations"
| "controllers"
| "model-editing"
| "asset-inspection"
| "layer-effects"
| "scene-transitions"
| "area-lights"
| "spot-lights"
| "camera-follow-lights"
| "shadow-filters"
| "environment-map-model"
| "spawn"
| "tracking-lost"
| "motion-stop"
| "stage-capture";stage.capture(能力 stage-capture)回傳 { dataUrl, width, height }——舞台視窗的一張帶透明通道的 PNG,最長邊不超過 maxEdge,取值 64 – 2048,預設 1024。看不見螢幕的用戶端就是靠它看結果;其餘約定見 外掛 API
Prop
Type
files 讓每個動作保留模型檔案裡的原始序號:檔案缺失的動作以空字串佔位,後面動作的序號因此不變。motionSlots(files) 回傳可播放的項目及其序號。對空位呼叫 motion.play 得到的是 not-found,一個檔案都沒有的分組不會列出。Live2D 模型還會把模型資料夾裡 model3.json 沒有宣告的每一個 .motion3.json 一併列出,各自成為一個以檔案名稱命名的分組
motion.stop(應用程式能力 motion-stop)把正在播放的動作淡出,讓待機接回去;只有待機在播時它回傳 { stopped: false }。motion.playing 的 oneShot(motion.started 上也有)說明這段動作是不是 stop 會結束的那一種
Prop
Type
Prop
Type
Prop
Type
快速鍵
Prop
Type
Prop
Type
HotkeyConfig 是去掉 registered 之後的同一形態——也是 hotkey.set 接受的資料結構
自動化
應用程式的自動化是另一回事——它屬於應用程式而不是模型,每條自動化攜帶一組操作,由快速鍵、事件或呼叫觸發。automation.list 回傳它們,automation.run 執行其中一條,automation.state 在清單變化時推送完整的新清單。需要 automations 能力,用法見 外掛 API
Prop
Type
type AutomationActionKind =
| "effect-toggle"
| "effect-params"
| "effect-clip"
| "camera-pose"
| "reset-camera"
| "play-camera-motion"
| "stop-camera-motion"
| "layer-visibility"
| "stream-mode"
| "switch-scene"
| "toggle-expression"
| "play-motion"
| "play-audio"
| "audio-control"
| "remove-all-expressions"
| "load-model"
| "model-position";actionKinds 與 triggerKinds 的型別是 string[] 而不是聯集型別,這是刻意的:伺服器可以送來比目前 SDK 更新的種類。automationActionLabel 會為一個操作種類給出英文標籤,未知的歸到一個通用名稱下;automationLabel 則給出整條自動化的標籤——有 title 就用它,否則把各個操作的標籤依清單順序用 · 連起來,這個順序並不是播放順序。AUTOMATION_BOUNDARY_KINDS 與 isAutomationBoundary 指的是準備類的種類 switch-scene 與 load-model,它們在時間軸開始前完成
設定
Prop
Type
SettingsPatch 是可寫入的子集:lipSync、controller(只有 enabled 開關)、window、ui 與 performance,全部可選。追蹤與姿態則透過 tracking.* 與 pose.* 修改。這兩個型別都由 SDK 同樣匯出的執行階段 Zod schema SettingsSchema 與 SettingsPatchSchema 推導而來:解析修補時會捨棄未知與唯讀的鍵,解析回應時則放行未知欄位。PersonaClient 不會替你驗證設定
type TrackingSourceId = "persona-ios" | "ifacialmocap" | "vts-ios";
type PoseSourceId = "vmc" | "mocopi";
type TrackingSourceKind = TrackingSourceId | PoseSourceId | "mediapipe";
type TrackingStatus = "off" | "waiting" | "tracking" | "no-face";
type PoseStatus = "off" | "waiting" | "tracking";
type EffectsQuality = "low" | "medium" | "high";追蹤來源
tracking.sources 是設定好的來源清單——網路臉部來源、姿勢來源,以及攝影機。可以同時跑好幾個,模型實例透過 SceneModelItem 上的 faceSourceId、poseSourceId 與 handSourceId 依 id 綁定到它們身上
Prop
Type
tracking.source、pose.source 與 pose.port 是多來源出現之前的欄位,如今只是各自通道裡第一個網路來源的鏡像,留給舊用戶端使用。新程式碼讀 sources
mediapipe 攝影機 來源帶有自己的選項,預設值見 DEFAULT_MEDIAPIPE_CONFIG
Prop
Type
type HandTrackingMode = "arms" | "fingers";來源的種類不再只對應一個通道——攝影機可以同時供給三個。sourceSupportsChannel(source, channel) 判斷一個來源在目前的任務開關下能否供給 face、hands 或 pose,sourceChannelEnabled(source, channel, masters) 還會算上來源自己的開關,以及網路來源的臉部與姿勢總開關
口型同步
settings.lipSync 與 lipSync.configure 共用同一份 LipSyncConfig,對應桌面端的麥克風。不支援麥克風口型同步的建置,Settings 裡沒有這一項
Prop
Type
lipSync.state 與 lipSync.calibrate 回傳 LipSyncState。校正步驟見 麥克風口型同步
Prop
Type
SceneModelItem 上的 lipSyncMode 依模型決定麥克風是否驅動它:
type LipSyncMode = "off" | "always" | "when-untracked";儲存
storage.* 的值是任意 JSON。上限見 常數,用法見 外掛儲存空間
type JsonValue =
string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };注入
Prop
Type
type InjectTargetType = "input" | "live2d-param" | "vrm-expression";InjectTarget 是去掉 value 與 weight 之後的同一形態。對於 input 目標,id 取自 INPUT_NAMES——VTube Studio 自己的詞彙表,加上 52 個原始 ARKit 通道,也是模型 .vtube.json 裡參照的那些名稱
VTS 詞彙表這 21 個:
FaceAngleX FaceAngleY FaceAngleZ FacePositionX FacePositionY FacePositionZ EyeOpenLeft EyeOpenRight EyeLeftX EyeLeftY EyeRightX EyeRightY Brows BrowLeftY BrowRightY MouthSmile MouthOpen MouthX CheekPuff JawOpen TongueOut
再加上 52 個 ARKit 前綴的通道,名稱就是 ARKit blendshape 首字母大寫之後的寫法——ARKitJawOpen、ARKitMouthSmileLeft、ARKitEyeBlinkRight 等等。加前綴是因為 VTS 詞彙表裡已經有了 JawOpen、CheekPuff 與 TongueOut,而 .vtube.json 的輸入名稱比對不分大小寫
這三個名稱正是 ARKIT_TWINS:它們和各自的 ARKit 通道是同一個浮點數,兩邊都收,最終都按 ARKit 那個 id 處理。INPUT_RANGES 給出每個輸入的量程——頭部與手部角度是度,ARKit 通道是 0..1,其餘無量綱
input 目標還接受 控制器 的輸入。一號設定檔沿用 VTS 那 19 個名稱(ControllerStickLeftX/Y、ControllerStickRightX/Y、搖桿按下、十字鍵、面鍵、肩鍵、扳機、options 與 home),二號往後在 Controller 後面插入編號,比如 Controller2StickLeftX 與 Controller12Cross,設定檔數量沒有上限。BASE_CONTROLLER_INPUT_NAMES 列出一號設定檔的預設控制項,controllerInputName 建構帶編號的名稱,getInputRange 解析它們的量程——搖桿與十字鍵的 Y 軸向上為正
攝影機 的手部與 麥克風 又帶來兩組輸入。HAND_INPUT_NAMES 包含 VTS 的 26 個手部輸入,外加 HandLeftAngleY 與 HandRightAngleY:每隻手的偵測、位置、角度與張合,每根手指一個值(如 HandLeftFinger_1_Thumb),以及 BothHandsFound 與 HandDistance。VOICE_INPUT_NAMES 包含 VTS 的 VoiceVolume、VoiceFrequency、VoiceVolumePlusMouthOpen、VoiceFrequencyPlusMouthSmile、VoiceA 到 VoiceO 與 VoiceSilence,外加 VoiceMouthOpen 與帶正負號的 VoiceMouthSpread。和所有 input 目標一樣,注入它們只對 Live2D 有效
系統游標是第三組:MOUSE_INPUT_NAMES 裡的 MousePositionX 與 MousePositionY,取值是游標所在那塊螢幕上的 −1..1,向右為正、向上為正,和 FacePositionY 一樣。它們和臉部追蹤無關,也不受追蹤總開關影響
Live2D 模型上這些輸入如何落到參數,見 參數繫結
訊息格式
用 SDK 的話不需要這些——它們是給直接對接協定的人準備的
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
ApiErrorCode 是 ErrorMessage 所攜帶 code 值的封閉集合——各自的意義見 錯誤。PersonaApiError 是 call 拋出的類別,攜帶的正是這個 code
最後更新於 2026年9月20日