An implementation of the WebSocket-based VTube Studio API for Node and browser JavaScript.
See the generated API documentation for type definitions and comments for the various public classes and interfaces, or use an editor with TypeScript support to automatically get types and comments when inspecting objects and fields.
See the official VTube Studio API documentation for more details on the semantic meaning of individual fields and the behaviors of the various API calls.
# NPM
npm i vtubestudio
# Yarn
yarn add vtubestudio
This package has no runtime dependencies. To avoid node_modules bloat, use npm install --only=prod
or NODE_ENV=production
to skip unnecessary dev dependencies.
This library is platform-agnostic, allowing it to be used for both Node projects and for browsers via tools like webpack. Use it by importing and instantiating an ApiClient:
// ES Modules (NodeJS or browser with bundler)
import { ApiClient } from "vtubestudio";
// CommonJS/Require (NodeJS)
const vts = require("vtubestudio");
const ApiClient = vts.ApiClient;
// ES Modules (CDN)
import { ApiClient } from "https://unpkg.com/vtubestudio/lib/esm/vtubestudio.min.js?module";
// Global Variable (CDN)
// <script src="https://unpkg.com/vtubestudio/lib/iife/vtubestudio.min.js">
const ApiClient = VTubeStudio.ApiClient;
function setAuthToken(authenticationToken) {
// store the authentication token somewhere
}
function getAuthToken() {
// retrieve the stored authentication token
}
const options = {
authTokenGetter: getAuthToken,
authTokenSetter: setAuthToken,
pluginName: "Your Plugin Name",
pluginDeveloper: "Your User Name",
// Optionally set the URL or port to connect to VTube Studio at; defaults are as below:
//port: 8001,
//url: "ws://localhost:${port}",
};
const apiClient = new ApiClient(options);
See IApiClientOptions in the docs for all available options.
The ApiClient automatically handles connecting to the VTube Studio API, including seamlessly reconnecting broken WebSocket connections, and authenticating your plugin with VTube Studio. To enable these features, you must provide the authTokenGetter
and authTokenSetter
functions above, to persist the authentication token generated by VTube Studio.
Barebones implementations, for reference:
// Browser
function setAuthToken(authenticationToken) {
localStorage.setItem("VTS_AUTH_TOKEN", authenticationToken);
}
function getAuthToken() {
return localStorage.getItem("VTS_AUTH_TOKEN");
}
// NodeJS
function setAuthToken(authenticationToken) {
fs.writeFileSync("./auth-token.txt", authenticationToken, {
encoding: "utf-8",
});
}
function getAuthToken() {
return fs.readFileSync("./auth-token.txt", "utf-8");
}
API calls may throw an error if the API client is not prepared to send requests; it may not be able to connect to VTube Studio, it may be waiting for the user to accept the authentication prompt, or the Plugin API might be disabled in VTube Studio settings. For best results, wrap every call in a try-catch, and use the following patterns where applicable:
// Run commands when the client connects:
apiClient.on("connect", async () => {
// The API client just finished authenticating with VTube Studio, or reconnecting if it lost connection.
// Run any commands you need to persist across reconnections here, such as event subscriptions:
apiClient.events.modelLoaded.subscribe((data) => {
// ...
});
// ...
});
// Check if the client is connected before running commands:
if (apiClient.isConnected) {
const response = await apiClient.currentModel();
// ...
}
The API client exposes the request/response message pairs provided by the VTube Studio API as single asynchronous functions. You can use them like so:
// async/await
async function loadModel() {
try {
const response = await apiClient.modelLoad({ modelID: "YourNewModelID" });
console.log("Successfully loaded model:", response.modelID);
} catch (e) {
console.error("Failed to load model:", e.errorID, e.message);
}
}
// Promise callbacks
apiClient
.modelLoad({ modelID: "YourNewModelID" })
.then((response) => {
console.log("Successfully loaded model:", response.modelID);
})
.catch((e) => {
console.error("Failed to load model:", e.errorID, e.message);
});
See ApiClient in the docs for all available calls, and review the official VTube Studio API documentation for more details on what calls are available and what each field means. In general, you pass an object representing the
data
property of the request to the library method, and get an object back representing thedata
property of the response. If the requestdata
object is empty, the request method instead takes no parameters, and if the responsedata
object is empty, the request method returnsPromise<void>
.
To subscribe to VTube Studio events, such as the ModelLoadedEvent
, call the functions in apiClient.events
, like this:
apiClient.events.modelLoaded.subscribe((data) => {
// this callback will fire every time a model is loaded or unloaded in VTube Studio
console.log("Model loaded:" + data.modelName);
});
An additional options object may be passed as the second parameter to control the execution of the API call. For example, to change the default timeout to 1 minute:
const stats = await apiClient.statistics(undefined, { timeout: 1 * 60 * 1000 });
The API client expects a WebSocket implementation to be available. In the browser, the native WebSocket will be used automatically. In NodeJS or other contexts, you must provide an external implementation, such as the one from the ws
package. In this case, you can explicitly provide a webSocketFactory
function, like so:
const WebSocket = require("ws");
const options = {
// ...
webSocketFactory: (url) => new WebSocket(url),
};
const apiClient = new ApiClient(options);
Examples are included in the repository's examples
folder for a React app (created with create-react-app
) and a plain NodeJS app.
Version v3.2.0
contains a minor breaking change from previous versions:
- The
ApiClient
no longer attempts to import thews
package, due to complications with bundling for browser environments when thews
package is present in the package hierarchy. In NodeJS and other server environments, you must explicitly pass a WebSocket constructor via thewebSocketFactory
option. See the example above for reference.
Version v3.x.x
contains several breaking changes from previous versions:
- The constructor for
ApiClient
was changed to take an options object, instead of the factory methods or a taking a message bus. - The message bus abstractions (
WebSocketBus
and related functions) have been removed. - The
ApiClient
automatically handles instantiating WebSockets, connecting to VTube Studio, and making authentication calls. These no longer need to be performed manually by plugin code. - The object-oriented wrapper classes (
Plugin
,CurrentModel
,Expression
, etc.) have been fully removed. The automatic authentication flow provided byPlugin
has been rolled intoApiClient
. - Error codes related to message bus failures (
MessageBusError
, andMessageBusClosed
) have been removed, as WebSocket errors are now handled silently byApiClient
. - The typings provided for API call methods that take no data payload now explicitly call for
undefined
as the data object instead ofvoid
, which would accept anything.
Version v2.x.x
contains several breaking changes from previous versions:
- The constructor for
ApiClient
was marked as private, and replaced with two factory methods:ApiClient.fromWebSocket(ws)
andApiClient.fromMessageBus(bus)
, to avoid unnecessarily exposing the end user to the library's message bus abstractions.- It is recommended that normal users should pass the websocket object to
ApiClient.fromWebSocket(ws)
directly instead of creating aWebSocketBus
and passing that tonew ApiClient(bus)
orApiClient.fromMessageBus(bus)
.
- It is recommended that normal users should pass the websocket object to
- The object-oriented wrapper classes (
Plugin
,CurrentModel
,Expression
, etc.) have been deprecated, and will be removed in the next major version. The stateful nature of the wrappers implied that they were somehow synchronized with VTube Studio, but this was not the case. This became even more obvious with the introduction of highly dynamic concepts like Live2D items and expressions.- Users should switch to making calls to the API directly using the
ApiClient
class instead of the methods onPlugin
. This unfortunately also includes handling the authentication workflow yourself for now. A basic example is provided above. - A future release may extend the
ApiClient
class to provide the automatic authentication handling that thePlugin
class provided.
- Users should switch to making calls to the API directly using the
- The
MockApiServer
class has been removed (as well as the related classEchoBus
). This class was not intended to be used in production code and was a poor substitute for testing against VTube Studio itself. - Expression parameters were changed to use the new shape
{ name: string, value: number }
instead of the deprecated{ id: string, target: number }
. - The
ErrorCode
enum valuesInternalClientError
,MessageBusError
, andMessageBusClosed
were changed to-100
, -101
, and-102
to avoid conflicting with-1
(used in the VTube Studio API to indicate the absence of an error).- Code using the enum itself should continue to work as expected, but any code directly checking for the error code numbers will need to be manually fixed.
Pull requests welcome! Please adhere to the linting and default formatting provided by the TypeScript compiler, and ensure that your code successfully compiles before opening your PR.
After cloning the repository or fetching the latest code:
npm install
Before opening a pull request, ensure your code compiles:
npm run build
MIT © 2022 Joshua Thome