@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日