Persona 會執行一個本機 WebSocket 伺服器,供其他應用程式驅動:切換場景、新增並擺放圖層、開關表情、播放動作、播放帶口型的語音、讀取追蹤狀態,以及直接寫入模型參數
它預設關閉。在 設定 → 外掛 API 中開啟,建立一把金鑰,再把權杖貼進你的外掛
開啟
| 控制項 | 作用 |
|---|---|
| 啟用 API | 啟動伺服器。沒有金鑰就連不上 |
| 連接埠 | 預設 25034 |
| 允許區域網路存取 | 綁定到本機回送位址之外,讓手機或另一台電腦也能連上 |
開啟區域網路存取後,Persona 會顯示本機對外回應的位址,讓你知道該把瀏覽器指向哪裡
如果連接埠已被佔用,這一節會直接顯示錯誤,而不是默默失敗
API 金鑰
金鑰是必要的——未經認證的連線會被拒絕。建立金鑰 會讓你為金鑰命名,並把它的權杖完整顯示一次;該列上的複製按鈕隨時可以再次取出完整權杖
| 動作 | 作用 |
|---|---|
| 複製權杖 | 把完整權杖複製到剪貼簿 |
| 重新命名金鑰 | 原地改名 |
| 撤銷金鑰 | 刪除它。正在使用它的工作階段會被關閉,並被告知不要重連 |
每個金鑰列還會顯示已連線、從未使用,或者以相對時間顯示最近使用於…。用戶端表明身分後,該列會記住最近一次的名稱與版本,因此沒有用戶端連線時也認得出這把金鑰
權杖保存在作業系統的安全儲存區裡。在沒有安全儲存區的電腦上,建立金鑰會失敗並給出說明,而不是把權杖寫進明文檔案
外掛資料夾
外掛可以請求 Persona 註冊一個模型或素材檔案,之後再載入它。這只對位於你在 外掛資料夾 中新增過的資料夾內的檔案有效——其他任何路徑都會以 forbidden-path 拒絕
已連線的用戶端
伺服器執行期間,每個開啟中的工作階段都會列出來,顯示它宣告的名稱、版本、開發者、用的是哪把金鑰,以及從哪裡連入。瀏覽器用戶端會顯示自己的頁面來源
從不表明身分的用戶端會列為 未識別的用戶端,功能完全一樣。該列上的中斷按鈕會關閉那個工作階段,並告訴它不要重連
把權杖當成密碼看待。它授予對應用程式的控制權,包括載入任何從你的外掛資料夾註冊的模型或素材。除非你開啟區域網路存取,伺服器只監聽本機回送位址
Web 控制台
persona.laplace.live 是這套 API 的瀏覽器前端——確認伺服器是否正常最快的方式,也是直播途中用手機或平板遙控的順手工具
填入主機、連接埠與一把金鑰,它就能驅動正在執行的應用程式:場景、圖層、表情、動作、特效、相機與 燈光、快速鍵、追蹤、設定,以及一個參數注入器。版面和桌面端的 控制面板 一樣——左邊一欄圖層,下面固定著舞台、追蹤、快速鍵、參數、設定五列,右邊是選取項的詳細內容,寬度夠了就並排——只是這裡每一節都是一個真正的網址,可以加入書籤。表情與動作屬於你選取的那個模型,不再各佔一頁;特效也和應用程式裡一樣是圖層清單裡的列,點開就能調它自己的參數。每一節都以選取實例的實際能力為準,它的格式回答不了的功能會自行停用,而不是出現錯誤
圖層清單底部的新增到場景按鈕開啟的是和應用程式裡一樣的 素材庫,內容透過 API 從註冊表填入。瀏覽器打不開原生檔案對話方塊,所以在這裡註冊要靠路徑——路徑必須位於你在 外掛資料夾 中新增過的資料夾內
場景素材 裡,環境貼圖 和 LUT 和應用程式裡一樣各佔一個標籤頁,點一列即套用到目前場景,並跳出提示確認;動畫 沒有專屬標籤頁,因為沒有哪個介面能播一段片段——它從 全部 標籤頁的類型選擇器註冊,再從控制台自己的 選擇… 按鈕挑給某個圖層當待機動畫
金鑰可以記在瀏覽器裡——未加密——也可以只在本次工作階段中保留。沒有任何資料會走到網際網路:這個頁面直接和你電腦上或區域網路裡的 Persona 通訊
以 HTTPS 提供的頁面只能向 127.0.0.1 開啟
socket,所以託管版控制台只能驅動與瀏覽器同一台電腦上的
Persona。要連到另一台裝置上的實例,請用純 HTTP 提供控制台,或者在 Persona
前面加一層 TLS 代理並開啟 使用 TLS
(wss://)。首次連線還可能要求授予區域網路權限
沒有 API 介面的功能都留在桌面應用程式裡:更新程式、API 金鑰、系統匣圖示、視窗邊界、原生檔案選擇器、舞台控制點、快速鍵錄製,以及 3D 物件與環境自帶的 烘焙燈光。應用程式與場景快速鍵也只能在桌面端建立,控制台裡只能觸發
Stream Deck
Stream Deck 外掛 是同一套 API 的另一個第一方用戶端,把切換模型與場景、觸發快速鍵、表情與動作,以及移動、縮放形象放到實體按鍵上
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.ended | 一段動作播放結束 |
speech.started | 一段 speech.play 的語音開始播放 |
speech.ended | 該段語音結束——播完、被停止或被頂替都算 |
hotkey.state | 快速鍵設定變更 |
shortcut.state | 全域快速鍵的清單、名稱或系統註冊狀態發生變化 |
tracking.status | 臉部或身體追蹤狀態變更 |
registry.changed | 有模型或素材被註冊——需要重新讀取對應的清單 |
方法一覽
以模型為作用域的方法接受可選的 instanceId,預設作用於場景的 主模型
| 分組 | 方法 |
|---|---|
scene.* | list get activate create duplicate rename delete setShortcut patch |
instance.* | list get add setModel remove setVisible setTrackingSources reorder setPrimary setPlacement setMToon setIdle info |
selection.* | get set |
object.* | add rename setContent setSpace setPlacement attach setLightOverrides anchors |
expression.* | list active toggle getPersistence setPersistence |
motion.* | list play playing |
speech.* | play stop |
hotkey.* | list set trigger |
shortcut.* | list trigger |
model.* / asset.* | 各自的 list 與 register |
settings.* | get patch |
tracking.* / pose.* | tracking.addSource tracking.updateSource tracking.removeSource、setEnabled、setSource,外加 pose.setPort 與 tracking.status |
stage.* | resetTransform resetCamera shockwave |
app.* / session.* | app.info app.localAddresses app.stats,以及 session.identify |
storage.* | get set delete list |
param.* | inject release |
能力
有兩級能力表說明目前這個建置做得到什麼。依它們判斷,不要去比對版本號
應用程式層級的那份由 hello 與 app.info 回報——目前是 storage、speech 與 shortcuts。每個實例自己的那份在 instance.list 的 capabilities 裡,能力表涵蓋不到的方法會以 unsupported-for-format 拒絕
追蹤來源
Persona 可以同時執行多個已設定的追蹤來源。settings.tracking.sources 列出它們,tracking.addSource、tracking.updateSource 與 tracking.removeSource 管理這份清單。instance.setTrackingSources 依來源 id 將實例的臉部與身體通道(faceSourceId、poseSourceId)綁定到對應來源。設為 null 會停止該通道的追蹤,不存在的 id 也視同 null。新實例預設綁定各通道的預設來源;同一個來源可以同時驅動多個實例
臉部追蹤來源可以是 vts-ios 或 ifacialmocap,也可以用選填的 phoneIp 固定一部傳送裝置。身體追蹤來源使用 vmc;每個來源各佔一個 port,若該連接埠已被其他來源佔用,請求會被拒絕。已淘汰的 vts-ios-native 會在協定層被拒絕
tracking.status 同時回傳臉部與身體通道的彙總狀態,以及 sources 對應表中每個已設定來源的獨立狀態;tracking.status 事件帶有相同的結構
舊的單一來源方法仍然作用於對應通道的第一個來源:tracking.setSource 修改其種類,pose.setSource 在需要時建立 VMC 來源並回傳它的連接埠,pose.setPort 則修改該連接埠。tracking.setEnabled 與 pose.setEnabled 仍是整個通道的總開關
語音
speech.play 播放一段音訊,並用它的響度驅動口型。它只作用於能力表裡帶 speech 的實例——這取決於渲染引擎而不是格式,所以判斷依據是實例的執行階段能力。目前使用 預設引擎 的 Live2D 會回報這項能力,舊版 WebGL 模式則不會
| 參數 | 說明 |
|---|---|
url | https:、http:(本機 TTS 橋接)或內嵌的 data:audio/* 負載 |
volume | 選填,0..1 |
instanceId | 選填,預設場景的主模型 |
一個模型同一時刻只有一段語音,新的播放會頂替正在播的那段。呼叫回傳即表示播放已經開始,沒能開始則是 invalid-state。speech.started 與 speech.ended 成對包住這段語音,speech.stop 提前結束它
全域快速鍵
帶 shortcuts 能力的建置可以列出並觸發應用程式自己的 應用程式快速鍵——那些一次跑完一串舞台改動的巨集
| 方法 | 作用 |
|---|---|
shortcut.list | 列出全部快速鍵 |
shortcut.trigger | 依 id 觸發其中一條 |
快速鍵的動作內容留在應用程式一側。清單裡給出的是 id、title、accelerator、registered,以及一份 actionKinds——足夠畫出一列按鈕,不必知道每條動作具體做什麼。title 為 null 時用 actionKinds 拼一個標籤出來,SDK 的 shortcutLabel 做的就是這件事
accelerator 同樣可以是 null。那不是錯誤,只是這條快速鍵沒綁組合鍵——shortcut.trigger 照樣能觸發它。清單、名稱或系統註冊狀態一有變化,shortcut.state 就會送來完整的新清單
actionKinds 是開放的:新版本可能送來這份 SDK
還不認識的種類。當成未知項處理,別當成錯誤——shortcutActionLabel
會把它們歸到一個通用名稱下
外掛儲存空間
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 會退回到逐個訂閱,保留能用的,並逐一報出其餘事件的名稱
最後更新於 2026年9月3日