Persona 会运行一个本地 WebSocket 服务器,供其他应用驱动:切换场景、添加并摆放图层、开关表情、播放动作、播放带口型的语音、读取捕捉状态,以及直接写入模型参数
它默认关闭。在 设置 → 插件 API 中打开,创建一个密钥,把令牌粘贴进你的插件
开启
| 控制项 | 作用 |
|---|---|
| 启用 API | 启动服务器。没有密钥就连不上 |
| 端口 | 默认 25034 |
| 允许局域网访问 | 绑定到回环地址之外,让手机或另一台机器也能连上 |
开启局域网访问后,Persona 会显示本机对外响应的地址,让你知道该把浏览器指向哪里
如果端口已被占用,这一节会直接显示错误,而不是悄无声息地失败
API 密钥
密钥是必需的——未经认证的连接会被拒绝。创建密钥 会让你为密钥命名,并把它的令牌完整显示一次;该行上的复制按钮随时可以再次取出完整令牌
| 操作 | 作用 |
|---|---|
| 复制令牌 | 把完整令牌复制到剪贴板 |
| 重命名密钥 | 原地改名 |
| 吊销密钥 | 删除它。正在使用它的会话会被关闭,并被告知不要重连 |
每个密钥行还会显示已连接、从未使用,或者按相对时间显示最近使用于…。客户端表明身份后,该行会记住最近一次的名称和版本,因此没有客户端在线时也认得出这把密钥
令牌保存在操作系统的安全存储里。在没有安全存储的机器上,创建密钥会失败并给出说明,而不是把令牌写进明文文件
插件目录
插件可以请求 Persona 注册一个模型或素材文件,之后再加载它。这只对位于你在 插件目录 中添加过的目录内的文件有效——其他任何路径都会以 forbidden-path 拒绝
已连接的客户端
服务器运行期间,每个打开的会话都会列出来,显示它声明的名称、版本、开发者、用的是哪个密钥,以及从哪里连入。浏览器客户端会显示自己的页面来源
从不表明身份的客户端会列为 未识别的客户端,功能完全一样。该行上的断开按钮会关闭那个会话,并告诉它不要重连
每接受一次连接,还会弹一条静音的系统通知,写明是哪个客户端、用的哪把密钥、从哪里连入——密钥落到别人手里,一被使用就会露出来。系统拒发通知时——没签名的开发版、权限被关掉——同一条消息会在控制面板打开期间以提示条的形式出现。它从不落到舞台上,那里的东西会被采集画面带走
把令牌当密码看待。它授予对应用的控制权,包括加载任何从你的插件目录注册的模型或素材。除非你打开局域网访问,服务器只监听回环地址
Web 控制台
persona-console.laplace.live 是基于这套 API 实现的浏览器控制台。作为第一方的「插件」,你可以用它检查连接,也可以在直播时通过手机或平板控制 Persona
填入主机、端口和密钥后,就能控制场景、图层、表情、动作、特效、相机、灯光、自动化、捕捉和设置。布局与桌面端的 控制面板 一致:左侧是图层列表,下方固定显示舞台、捕捉、自动化和设置;宽度足够时,选中项的详情显示在右侧。每个部分都有独立网址,可以收藏。选中模型后可调整它的表情、动作和参数绑定,选中场景效果下的特效后可调整其参数;选中的模型或 3D 物体还带有自己的图层效果。当前实例不支持的控件会禁用
图层列表底部的添加到场景按钮打开 素材库,内容通过 API 从 Persona 的注册表读取。在控制台注册文件时,需要输入文件在桌面端机器上的路径,且文件必须位于已配置的 插件目录 内
场景素材 中,环境贴图和 LUT 各有独立的标签页。点击素材即可应用到当前场景,随后会弹出确认提示。动画没有独立标签页,因为 API 不支持直接播放动画片段。请在全部标签页的类型选择器中注册动画,再通过选择…按钮将其设为某个图层的待机动画
选中场景后,灯光分区会列出 3D 物体和环境自带的 烘焙灯光,和桌面端一样可以开关、调整强度。已加载素材的尺寸和 glTF 扩展信息也在这里显示
密钥可以不加密地保存在浏览器中,也可以仅在本次会话中保留。控制请求直接发送到本机或局域网中的 Persona
从 HTTPS 托管版控制台使用未加密的 ws:// 连接时,请通过 127.0.0.1
连接与浏览器同一台机器上的 Persona。要连到另一台设备上的实例,请用纯 HTTP
提供控制台,或者在 Persona 前面加一层 TLS 代理并打开 使用 TLS
(wss://)。首次连接还可能要求授予局域网权限
没有 API 接口的功能都留在桌面应用里:更新程序、API 密钥、托盘图标、窗口边界、原生文件选择器、舞台手柄和快捷键录制。自动化 也只能在桌面端编辑:控制台的自动化页会列出并执行它们,主模型的快捷键则放在下方可折叠的虚拟形象快捷键分区里。在桌面端暂停的自动化会标为已暂停,无法执行
Stream Deck
Stream Deck 插件 是同一套 API 的另一个第一方客户端,把切换模型与场景、触发热键、表情和动作,以及移动、缩放形象放到实体按键上
MCP 服务器
@laplace.live/persona-mcp 是第三个:一个 Model Context Protocol 服务器,把这套 API 交给 AI 助手——Claude、Cursor、Codex——封成二十来个按任务划分的工具,其余的走 call_api,还有 stage.capture 让助手看得见自己改出来的结果
SDK
@laplace.live/persona-sdk 是配套的类型化客户端——在 Node、Bun、浏览器和 OBS 浏览器源之间同构
import { PersonaClient } from "@laplace.live/persona-sdk";
const persona = new PersonaClient({ token: "sk-lp-v1-…" });
await persona.connect();
await persona.call("expression.toggle", { name: "Smile" });
persona.on("motion.started", (m) => console.log("playing", m.group));每个方法都由同一张共享的类型表描述,因此一次调用的请求和响应形状由方法名决定。SDK 参考 覆盖安装、客户端选项、认证、参数租约和每一个导出的类型
事件
会话只会收到已订阅的事件。SDK 会在重新连接后自动恢复订阅
| 事件 | 触发时机 |
|---|---|
scene.changed | 任何场景变更:创建、删除、重命名、激活或编辑 |
scene.loading | 场景生效或渲染管线预热的开始与结束 |
settings.changed | 应用设置发生变化 |
instance.loaded | 一个模型实例在舞台上加载完成 |
selection.changed | 编辑选中项变动,来源不限 |
expression.changed | 当前生效的表情组合变化 |
expression.persistence | 某个模型的「记住表情」开关被切换 |
motion.started | 一段动作开始播放;带 motion-stop 的主机还会附上 oneShot |
motion.ended | 一段动作播放结束 |
speech.started | 一段 speech.play 的语音开始播放 |
speech.ended | 该段语音结束——播完、被停止或被顶替都算 |
hotkey.state | 快捷键配置变化 |
automation.state | 自动化的列表、名称、触发条件种类、启用状态或系统注册状态发生变化 |
tracking.status | 面部或姿态捕捉状态变化,或任一捕捉源自身的状态变化——包括摄像头的面部、手部和身体捕捉 |
registry.changed | 有模型或素材被注册——需要重新读取对应的列表 |
方法一览
以模型为作用域的方法接受可选的 instanceId,默认作用于场景的 主模型
| 分组 | 方法 |
|---|---|
scene.* | list get activate create duplicate rename delete patch inspect setLightFollowCamera |
instance.* | list get add setModel remove setVisible setEffects setTrackingSources reorder setPrimary setPlacement attach setMToon setIdle setControllerMovement setLipSync info |
binding.* | get preview set release inputs override |
controller.* | state rename setDeadZone remove assign |
selection.* | get set |
object.* | add addMany rename setContent setSpace setPlacement attach setLightOverrides anchors |
expression.* | list active toggle getPersistence setPersistence |
motion.* | list play playing stop |
speech.* | play stop |
hotkey.* | list set trigger |
automation.* | list run |
model.* / asset.* | 各自的 list 和 register,外加 registry.thumbnail |
settings.* | get patch |
tracking.* / pose.* | tracking.addSource tracking.updateSource tracking.removeSource、setEnabled、setSource,外加 pose.setPort 和 tracking.status |
lipSync.* | state configure restart calibrate |
stage.* | resetTransform resetCamera spawn clearSpawned capture |
app.* / session.* | app.info app.localAddresses app.stats,以及 session.identify |
storage.* | get set delete list |
param.* | inject release |
scene.create 追加一个空场景并激活它,和在应用里新建一个场景一样,所以返回里的 activeSceneId 就是新场景——新场景的 id 从这里读,而不是去比对前后两份场景列表,因为中间可能夹进别的会话建的场景
scene.patch 编辑当前场景的背景、相机、灯光、环境和 转场;场景条目则通过 instance.* 和 object.* 修改。enabled 为 false 的灯光会连同阴影一起熄灭,其余设置都会保留。该字段默认为 true,不支持关闭灯光的构建不会带这个字段
vrmCamera 整体替换:orbit、fov(1 – 179°)、roll(绕视线轴旋转的弧度)、projection(perspective 或 orthographic)和 clipAssetId。旧版本的构建不带 roll 和 projection——按 0 和 perspective 处理——所以提供这两项控件之前,先看返回的相机里有没有。环绕的角度可以取任意弧度,distance 也可以是负数,鼠标导航则保留自己更窄的范围。正交视图的宽度是 abs(distance),fov 保留但不起作用,distance 为负时两个轴都会翻转。stage.resetCamera 也会重置滚转和投影
object.add 可以带 place2d 或 place3d,物体在同一次编辑里就落在该位置,不会先在默认位置画出一帧;取值按 object.setPlacement 的规则修正。object.addMany { objects } 把最多 100 条同样的条目作为一次场景编辑添加——只保存一次,撤销也只算一步——并按请求顺序返回 instanceIds。只要有一个素材无法解析,就一个也不添加
instance.attach { instanceId, attach } 把一个 Live2D 实例作为 物品 钉到另一个 Live2D 模型上,attach 与 object.attach 的相同;传 null 则原地解除。attach.depth 取 { kind: 'front' }、{ kind: 'behind' } 或 { kind: 'artMesh', id },最后一种把物品插在父级模型那个 ArtMesh 的正上方——父级没有的 ID 按前方处理。attach.split 即 { itemArtMesh, depth },在物品自己的某个 ArtMesh 处切开,把它下面的部分送到第二个深度。object.anchors 的每一行带有 order,即取列表时该 ArtMesh 的绘制顺序,深度控件就是按它排列各档的。会形成循环的钉附返回 invalid-params,对 VRM 实例调用则返回 unsupported-for-format。对物体而言,目前只有 front 生效
stage.capture 需要 stage-capture 能力,返回 { dataUrl, width, height }:舞台窗口此刻显示的样子,一张带透明通道的 PNG,最长边缩到不超过 maxEdge(64 – 2048,默认 1024),且从不放大。看不见屏幕的客户端——比如经由 MCP 服务器 的 AI 助手——就是靠它确认自己的改动。每截一张,渲染都要停下一帧,所以按需取用,不要轮询。renderer-unavailable 表示当时没有舞台窗口,或者没有可复制的帧;舞台崩溃后直到重载画出第一帧为止都会返回它,所以该重试而不是放弃
能力
应用级和实例级的能力表说明当前版本支持哪些功能。请以能力表为准,不要通过比较版本号来判断
应用级的那份由 hello 和 app.info 报告——目前是 storage、speech、automations、layer-effects、scene-transitions、area-lights、spot-lights、camera-follow-lights、shadow-filters、environment-map-model、spawn、tracking-lost、motion-stop、stage-capture、controllers、model-editing 和 asset-inspection。每个实例自己的那份在 instance.list 的 capabilities 里,能力表覆盖不到的方法会以 unsupported-for-format 拒绝
后三项各自开启一批共享编辑器的方法:controllers 是 controller.*(读取已连接的设备与已保存的配置,改名、设死区、移除、发起分配),以及用 settings.patch 写 controller.enabled 这个总开关——设备指的是接在桌面端上的,不是浏览器所在那台机器上的;model-editing 是 binding.*(读取、预览、写回和释放某个模型的参数绑定,并做短时的输入覆盖)连同 instance.setBreath。binding.inputs 返回 { inputs, outputs }:inputs 是实时的捕捉输入,outputs 按输出 id 给出每个被绑定的参数此刻在模型上的读数——已经算完动作、表情和物理演算之后的值,读不回来的输出不列出;asset-inspection 是 scene.inspect 与 registry.thumbnail
instance.setControllerMovement 合并某个实例的 手柄移动 配置:enabled、slot(已保存的配置编号)、moveSpeed、turnSpeed、smoothing,VRM 上还有 walkEnabled、walkClip、walkSpeed 和 faceMovement。越界的值会被修正到默认值或边界上;对物体调用会返回 unsupported-for-format。walkClip 接受和 instance.setIdle 一样的引用
动作与待机
motion.list 列出模型的动作分组。Live2D 模型还会把模型文件夹里每一个 .model3.json 没有声明的 .motion3.json 一并列出,各自成为一个以文件名命名的分组——在桌面端动作分区里 录下来的动作 因此无需重新加载就会出现
motion.stop(应用能力 motion-stop)把正在播放的动作淡出,让待机接回去。只有待机在播时它什么都不做,返回 { stopped: false }。motion.playing 的 oneShot(motion.started 上也有)说明报告的这段动作是不是盖在待机之上的那一种,也就是 stop 会结束的那一种;旧版本的构建不带这个字段
instance.setIdle 合并某个实例的待机设置:
| 字段 | 说明 |
|---|---|
idleAnimation | 待机总开关。false 会停下 VRM 的片段,Live2D 上则停下每一段待机动作,包括模型自己的 Idle 组 |
idleClip | 仅 VRM。待机片段的引用 |
idleMotion | 仅 Live2D,需实例能力 idle-motions。motion.list 里的一个动作文件,在其他所有动作之下循环;null 表示随机播放模型的 Idle 组。模型不再列出的文件按 null 处理 |
trackingLostBehavior | 需 tracking-lost。hold 保持最后追踪到的姿态,idle 用约 0.3 秒回到待机、脸回来时再回去 |
trackingLostMotion | 需 tracking-lost,仅 Live2D。脸消失够久之后顶替待机的那个动作,null 表示不换 |
trackingLostDelay | 需 tracking-lost。「够久」是多少秒,0 – 60 |
instance.get 会把这几项一并报出
灯光
带 area-lights 能力的构建接受类型为 area 的 SceneLight:一块位于 x/y/z 的矩形,用 azimuth/elevation(度)指向,用 roll(−180 – 180°,默认 0)在自身平面内旋转,尺寸由 width 和 height 决定(各 0.01 – 20 场景单位,默认 1)。它照亮 PBR 材质和 MToon 虚拟形象——MToon 在自己的卡通着色里用四个光照采样来近似这块矩形——但不影响 Live2D,也不投射阴影。它的 range 和阴影设置会保留,以便之后切换类型
带 spot-lights 能力的构建接受类型 spot,它使用 x/y/z、azimuth/elevation 和 range(0.1 – 20 场景单位)。方位角和仰角都为 0 时光束指向 −Z,仰角为正时朝下。angle 是光锥的半角(1 – 89°,默认 30),penumbra 柔化光锥边缘(0 – 1,默认 0.3)。强度范围 0 – 20,与点光源相同。聚光灯照亮 PBR 和 MToon 材质,支持常规的阴影档位和半径,也能点亮体积光照的雾气。面光源和聚光灯——无论是新建还是切换类型——只在带对应能力的构建上提供
shadowFilter 为每盏平行光、点光源或聚光灯单独选择 pcf(默认)或 pcss,两者可以在同一个场景里并存。shadowQuality 仍然决定贴图分辨率,off 仍然关闭投射。PCF 按 shadowRadius(阴影贴图纹素)柔化。PCSS 会搜索遮挡物,阴影离投射物越远,半影越宽,宽度取决于 shadowSourceSize:光源直径,平行光以度计,点光源和聚光灯以米计(0 – 10,默认 0.1),为 0 时是硬光源。PCSS 不会改动已保存的 shadowRadius,旧场景加载后是 PCF。过滤方式的选择要按 shadow-filters 能力判断——本地新建的灯即使在不支持的构建上也可能带着 shadowFilter
带 camera-follow-lights 能力的构建接受灯光上的 followCamera,环境光会忽略它。它默认是 false;为 true 时,x/y/z 和各个角度都相对相机计算,于是灯光会随手动编辑相机、也随相机运动播放一起移动。followCameraOptions 选择跟随哪些部分,各项默认 true:position 跟随相机的注视目标和平移,rotation 跟随它在世界空间中含滚转在内的旋转,distance 跟随带符号的环绕距离。只改 fov 永远不会移动灯光。关掉的部分改用 followCameraReference——targetX、targetY、targetZ、distance 和世界空间的 rotation 四元数——即关掉那一刻定格的值;缺少参考时按目标为零、单位旋转、距离为零处理
scene.setLightFollowCamera { lightId, followCamera, options? } 为当前场景的一盏灯切换跟随,或把 options 合并进它的选择,同时不移动它、也不改变它的朝向:桌面端按屏幕上的相机(包括正在播放的相机运动)换算这盏灯的坐标,返回换算后的 { light },灯已不存在时为 light: null。scene.patch 和已保存的场景则按写入的值原样使用,不做这种换算。这些字段和这个方法都要按 camera-follow-lights 判断
SceneLight.volumetric 为单盏点光源、聚光灯或面光源开启受光照亮的雾气。它默认是 false,其他类型会忽略它。旧版本的构建不带这个字段,而是整个场景共用一个开关,所以提供这项控件之前先检查字段是否存在;打开那个开关保存的场景,载入后每盏能点亮雾气的灯都会打开 volumetric
SceneEnvironment 新增了两个可选字段 volumetricLighting 和 clusteredLighting。旧版本的构建不带它们,所以提供这两项控件之前先检查字段是否存在。volumetricLighting 决定整个场景里雾气的样子,本身没有开关——没有哪盏亮着的灯开启雾气时,就没有雾气。当前版本会返回完整的雾气设置,并把 clusteredLighting 修正为关闭;修改它们走 scene.patch 的环境
volumetricLighting 字段 | 取值 | 默认值 |
|---|---|---|
density | 0 – 10 | 1 |
intensity | 0 – 10 | 1 |
range | 距相机 1 – 100 场景单位 | 20 |
quality | low、medium、high | medium |
speed | 0 – 5;为 0 时雾气静止 | 0.2 |
雾气围绕开启了它的灯发光,会被深度遮挡,未被照亮处保持透明;只要有一盏亮着的场景光源开启了它,3D 物体和环境模型自带的点光源、聚光灯与面光源也会加入。它需要透视相机:正交投影下设置保留,但不渲染雾气。平行光和环境光不参与,Live2D 也不会遮挡它。clusteredLighting: true 会在 WebGPU 透视相机下,为 1 – 64 盏范围有限、不投射阴影的点光源加速表面光照。有点光源没有范围上限、数量超出,或渲染器、相机不受支持时,照旧使用常规光照。它不会降低阴影的渲染开销,也不会改变雾气所用的光源
带 environment-map-model 能力的构建上,SceneEnvironment 可以同时带 iblAssetId 和 modelAssetId。此时 bakeFromModel 选择从模型生成光照,贴图作为它的输入;为 false 时直接用贴图照明,没有贴图时退回模型自带的天空。showSkybox 只管显示,无论由谁照明,画的都是贴图或那片天空。清除其中一项会保留另一项,SDK 的 environmentClearPatch() 可以生成这种修改。旧版构建同一时间只能有一项环境素材,在那里设置其中一项时要清掉另一项
还有三个可选字段通过 scene.patch 调整全景图:skyboxBlur(0 – 1,默认 0)和 skyboxIntensity(0 – 2,默认 1)只影响看得见的天空盒,iblRotation(弧度,默认 0,可以超过一整圈)让全景图和它的光照一起转动,模型生成光照所用的天空也一样。旧版构建不带这些字段,所以字段存在时才提供对应控件
物理模拟
SceneEnvironment.physics 就是场景的 物理模拟 开关:3D 物体会落到舞台地面上,也会落在 VRM 虚拟形象身上。它默认是 false,旧版本的构建不带这个字段。模拟出的姿态从不保存,读回的物体摆放始终是写入的值,把 physics 关掉,每个物体都会回到原位
带 spawn 能力的构建还接受 stage.spawn,它把已注册的 prop 素材复制成一批受物理驱动的物体,投放到舞台上。这些副本只存在于渲染器里——没有场景条目,没有图层行,不能撤销,也不会保存——同一个文件的所有副本共用几何体、纹理和绘制调用,所以数量上百也依然轻量。当前场景的 physics 关闭时,调用返回 invalid-state;素材不是已注册的 prop 时返回 not-found;否则返回 { spawned, alive },其中 alive 统计所有素材的物体
| 字段 | 含义 |
|---|---|
assetId | 已注册的 prop 素材 |
count | 1 – 200,默认 1。舞台上的物体超过 500 个时,最早的会让出位置 |
origin | 散布立方体的中心,{ x, y, z },以米为单位。默认 { x: 0, y: 3, z: 0 },在虚拟形象站立处的上方 |
spread | 散布立方体的边长,0 – 20 米,默认 1 |
velocity | 所有物体共用的初速度,{ x, y, z },以米每秒为单位,每个轴在 ±50 以内。默认静止 |
scale | 0.01 – 100,默认 1 |
ttlMs | 每个物体存在多久,0 – 600000 毫秒,默认 30000。0 表示一直保留到被清除 |
transition | { style, inMs, outMs }:只为这批物体另选 显示与隐藏 样式,出现和消失各用 0 – 5000 毫秒。省略的字段沿用场景的设置 |
越界的数值会被修正到边界上。物体会在 ttlMs 到期、切换到其他场景或关闭 physics 时离开,也可以用 stage.clearSpawned {} 清除——它返回 { cleared },还会取消仍在加载素材的投放
场景转场
scene.activate 会等目标场景加载完成并接管舞台——这期间原场景照常运行、可以编辑——但不会等转场把它完全揭开。更新的切换会顶替尚未完成的那次,所以响应里报告的是实际生效的场景,不一定是你请求的那个。SDK 给切换场景留 120 秒,设置了 requestTimeoutMs 时以它为准
带 scene-transitions 能力的构建会在 Scene.transition 里带上每个场景的 转场,也就是进入该场景时播放的效果。scene.patch { transition } 会整个替换它,但不会播放:
| 字段 | 含义 |
|---|---|
type | cut、fade、wipe、circle、image 或 video |
durationMs | 100 – 10000 毫秒 |
color | 淡入、擦除或圆形展开的遮罩颜色,十六进制 |
assetId | 已注册的图片或视频,或 null |
switchPoint | 视频里切换场景的位置,0.05 – 0.95。请选一帧完全盖住舞台的画面 |
autoFade | 让视频淡入淡出。默认关闭 |
fadeInMs / fadeOutMs | 各为 0 – 10000 毫秒的播放时长,默认 300,分别以切换点之前和之后的时长为上限。0 关闭该淡变 |
所有切换场景的方式都使用目标场景的转场。除直接切换外,转场都会立即开始,并一直遮住舞台直到目标场景就绪,加载较慢时会超出 durationMs 自动延长。视频静音播放,速度随时长调整,等待期间停在切换帧上
显示与隐藏
SceneEnvironment.itemTransition { style, durationMs } 就是场景的 显示与隐藏 设置:场景里的模型、物体和生成物体如何出现与消失。旧版本的构建不带这个字段。style 是 glitch(带撕裂与灼烧的抖动溶解,默认)、dither(不带额外效果的抖动溶解)、pop(缩放出现与消失)或 cut,durationMs 取值 0 – 5000,默认 300。cut 无论 durationMs 是多少都不会渐变,0 也等同于 cut。修改它走 scene.patch 的环境
图层特效
带 layer-effects 能力的构建允许单个模型或物体带上自己的 特效,在它并入场景之前生效。每个场景条目都有自己的 effects 和 effectLayers,结构与场景的一致,由 instance.setEffects { instanceId, effects, effectLayers? } 编辑:
effects接受color、levels、colorWheels、colorShift、selectColors、gradient、blur、bloom、diffusion、rim、outline和dropShadow的部分设置,没写到的保持原值effectLayers传入时会替换已添加特效的列表。已启用的特效始终在列表里
响应是修正后的结果:数值被限制在范围内,未知的键被丢弃。格式有误的特效会让整个调用在任何改动生效之前失败
图层特效作用于模型和位于 3D 空间的物体。2D 物体会返回 unsupported-for-format;它们已保存的设置会保留,等空间改变后再生效
捕捉源
Persona 可以同时运行多个已配置的捕捉源。settings.tracking.sources 列出它们,tracking.addSource、tracking.updateSource 和 tracking.removeSource 管理这份列表。instance.setTrackingSources 按源 id 把实例的面部、姿态与手部通道(faceSourceId、poseSourceId、handSourceId)绑定到对应的源。设为 null 会停掉该通道的捕捉,不存在的 id 也按 null 处理。新实例默认绑定各通道的默认源;同一个源可以同时驱动多个实例
instance.setTrackingSources 还接受 handTrackingMode:arms 让捕捉到的手同时带动 VRM 的手臂、手腕和手指,fingers 只动手指,手臂留给 VMC 或 mocopi 的身体捕捉
面部捕捉源可以是 persona-ios、ifacialmocap 或 vts-ios,还可以用可选的 phoneIp 固定一台发送设备。姿态捕捉源使用 vmc 或 mocopi。port 属于 vmc、mocopi 和 ifacialmocap——若该端口已被其他源占用,请求会被拒绝,只有几个 ifacialmocap 源之间共用一个套接字;persona-ios 与 vts-ios 的端口由协议本身固定。已废弃的 vts-ios-native 会在协议层被拒绝
mediapipe 是 摄像头 捕捉源,最多只能有一个,没有 port 或 phoneIp。它的 mediapipe 选项开关三项任务——face、hands、body,分别对应三个通道——并设置 deviceId('' 表示默认摄像头)、mirror 和 delegate(CPU 或 GPU)。默认开启镜像、在 CPU 上运行,捕捉面部和手部而不捕捉身体。tracking.addSource 和 tracking.updateSource 接受部分选项,没写到的保持原样。捕捉到的手还会驱动 注入 一节列出的手部输入
tracking.status 返回网络面部源与姿态源的汇总状态,以及 sources 映射中每个已配置源的独立状态,摄像头也在其中;tracking.status 事件带有相同的结构,只有单个源的状态变化时也会触发
旧的单源方法仍然作用于对应通道的第一个网络源:tracking.setSource 修改其种类,pose.setSource 在需要时创建 VMC 源并返回它的端口,pose.setPort 则修改该端口。tracking.setEnabled 和 pose.setEnabled 仍是网络面部源与姿态源的总开关。摄像头只听它自己的 enabled,通过 tracking.updateSource 设置,任务选择与模型分配都会保留
麦克风口型同步
口型同步 用桌面端的麦克风驱动口型。麦克风和输入设备列表都是桌面端的,不是浏览器所在那台机器上的;音频和校准数据都不会经过 API。分析结果还会驱动 注入 一节列出的声音输入
| 方法 | 作用 |
|---|---|
lipSync.state | 读取配置、采集状态(off、starting、listening 或 error)、桌面端的输入设备、实时音量与元音采样,以及进行中的校准 |
lipSync.configure | 合并一份部分配置——enabled、deviceId、gain(0 – 30 dB)、noiseGate(−60 – 0 dBFS)、smoothing(0 – 0.3 秒)——并返回保存后的结果 |
lipSync.restart | 在下一帧重启采集。它既不会打开已关闭的麦克风,也不会等采集完成,请轮询 lipSync.state |
lipSync.calibrate | 执行一步校准并返回新状态 |
同一份配置也以 settings.lipSync 出现,settings.patch 同样接受。校准使用桌面端选中的麦克风,并与桌面面板共享:start 开始一份草稿,record 配合 phoneme(A、I、U、E、O,或代表背景噪音的 S)把该声音录两秒,六个都录完后用 preview 应用草稿,最后 save 或 cancel 结束。reset 让当前麦克风回到内置配置。录制会立即返回,进度请轮询 lipSync.state:其中的 calibration 会报告所处阶段 phase(ready、recording 或 verifying)、正在录制的声音和已录完的声音。不符合当前阶段的步骤会得到 invalid-state
instance.setLipSync { instanceId?, mode } 设置麦克风是否驱动某个模型:always(默认)、when-untracked(仅在它的面部没被捕捉时)或 off。对物体调用会返回 unsupported-for-format
语音
speech.play 播放一段音频,并用它的响度驱动口型。它只作用于能力表里带 speech 的实例——目前是已加载的 Live2D 模型。判断依据始终是实例的运行时能力而不是格式:0.53 及更早的版本在已移除的旧版 WebGL 引擎下不报告它
| 参数 | 说明 |
|---|---|
url | https:、http:(本地 TTS 桥接)或内联的 data:audio/* 负载 |
volume | 可选,0..1 |
instanceId | 可选,默认场景的主模型 |
一个模型同一时刻只有一段语音,新的播放会顶替正在播的那段。调用返回即表示播放已经开始,没能开始则是 invalid-state。speech.started 和 speech.ended 成对包住这段语音,speech.stop 提前结束它
自动化
带 automations 能力的构建可以列出并执行应用的 自动化——由快捷键、事件或请求触发的一串舞台改动
| 方法 | 作用 |
|---|---|
automation.list | 列出全部自动化 |
automation.run | 按 id 执行其中一条 |
操作负载、触发条件设置和编辑都留在应用一侧。列表里给出的是 id、title、accelerator、registered、enabled,以及 actionKinds 和 triggerKinds 两份数组——足够画出一行按钮,不必知道每条操作具体做什么。title 为 null 时用 actionKinds 拼一个标签出来,SDK 的 automationLabel 做的就是这件事。actionSceneIds 与 actionKinds 一一对应,记录每个 switch-scene 的目标场景(其他种类为 null),标签因此可以写出场景名——不过那个场景可能已经被删了
每条自动化都是一条时间线,它的时间和其他负载一样留在桌面应用里,所以 actionKinds 的顺序并不代表执行的先后。操作种类包括在时间线开始前作为准备步骤执行的 switch-scene 和 load-model;临时效果 effect-clip;play-camera-motion 和 stop-camera-motion;play-audio 和 audio-control;以及作用于虚拟形象的 toggle-expression、play-motion、remove-all-expressions、load-model 和 model-position。delay 已经移除——时间线上的空白就是等待。音频没有自己的方法:文件、播放和输出设备都留在桌面端,客户端要放声音,就执行一条自动化。triggerKinds 列出这条自动化在快捷键之外还有哪些种类的事件触发条件——目前有 scene、model-loaded、motion、face-tracking、microphone 和 parameter
没有分配键盘或手柄快捷键时 accelerator 为 null——automation.run 照样能执行它。暂停的自动化 enabled 为 false,会忽略所有触发,包括 automation.run。automation.run 在派发后立即返回,不等操作执行完。列表、名称、触发条件种类、启用状态或系统注册状态一有变化,automation.state 就会送来完整的新列表
actionKinds 和 triggerKinds 都是开放的:新版本可能送来这份 SDK
还不认识的种类。按未知项处理,别当成错误——automationActionLabel
会把未知的操作归到一个通用名字下
插件存储
storage.* 为插件提供持久化键值存储,按 API 密钥划分命名空间。使用同一密钥的所有会话共享数据,吊销密钥也会删除对应数据。值可以是任意 JSON
| 方法 | 作用 |
|---|---|
storage.get | 读一个键。从没写过的键返回 value: null |
storage.set | 写一个键 |
storage.delete | 删一个键。键本来就不存在也算成功 |
storage.list | 列出所有键,已排序 |
上限是键名 128 个字符、单个值序列化后 64 KB、每把密钥 256 个键。会突破键数上限的 storage.set 得到的是 invalid-state
错误
persona.call 会抛出带字符串 code 的 PersonaApiError:
| 代码 | 含义 |
|---|---|
parse-error | 这一帧不是合法的 JSON |
invalid-request | 消息封装格式有误 |
unknown-method | 当前构建没有这个方法 |
invalid-params | 参数未通过校验 |
not-found | 引用的 id 找不到对应对象 |
unsupported-for-format | 该实例的格式做不到这件事——比如对 Live2D 模型设置 MToon |
conflict | 参数租约被另一个会话持有 |
renderer-unavailable | 舞台无法响应——没有窗口,或渲染进程正在重载 |
forbidden-path | 路径不在任何已配置的插件目录内 |
invalid-state | 当前不允许,比如删除最后一个场景、存储超额、音频没能开始 |
internal | 其余所有情况 |
这套代码是协议的一部分:客户端会丢弃带未知代码的错误帧,所以新增代码只随协议版本一起到来,其他失败情况会沿用现有错误代码
断开连接
客户端会在连接意外断开后自动重连。以下关闭码会停止自动重连,以遵循密钥吊销或用户手动断开的操作:
| 代码 | 常量 | 原因 |
|---|---|---|
4001 | CLOSE_KEY_REVOKED | 密钥已被吊销 |
4002 | CLOSE_FORCE_DISCONNECTED | 用户断开了该会话 |
两者都会让客户端最终停在 closed 状态。传入了 onClose 时,终止性关闭会交给它,并跳过 onWarning;没有 onClose 时则回退到 onWarning。1001 不在此列——它表示服务器正在停止,等它回来重连即可
吊销密钥会立即丢弃待发帧,并以 4001 关闭使用该密钥的连接。仍在等待舞台响应的请求不会继续写入。已发送给舞台的操作可能完成,但其响应会被丢弃
版本不一致
协议自带版本号,与所连接的构建不匹配时 SDK 会发出警告。订阅一个旧版 Persona 不认识的事件并非致命错误——SDK 会退回到逐个订阅,保留能用的,并逐一报出其余事件的名称
当前协议版本是 4。此前的每次升级都从协议里移除了一些内容,所以针对旧协议编写的客户端或插件需要更新:
| 协议 | 变化 |
|---|---|
| 2 | Scene.behavior(视线跟随光标)已移除,场景数据和 scene.patch 里都没有了 |
| 3 | stage.shockwave 已移除,调用它会得到 unknown-method |
| 4 | shortcut.list、shortcut.trigger 和 shortcut.state 改为 automation.list、automation.run 和 automation.state;shortcuts 能力改名为 automations,负载里改用 automationId 和 automations |
最后更新于 2026年9月20日