obs-websocket-js: a typed JavaScript client for controlling OBS Studio
Consumes https://github.com/obsproject/obs-websocket
At a glance
- What is it?
- obs-websocket-js is the JavaScript client for the obs-websocket plugin, wrapping connect, reidentify, call and event handling in a promise-based API with JSON and MessagePack builds. It suits stream overlays and automation tools, and it is bound to whatever protocol version the running OBS instance speaks.
- Who is it for?
- obs-websocket-js is the right choice for a JavaScript or TypeScript tool that has to drive OBS Studio over the obs-websocket protocol, particularly a browser overlay or a Node automation script, and it is the wrong choice for a non-JavaScript caller or for anything that needs protocol features the plugin does not expose.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A typed client for the obs-websocket plugin, not for OBS itself
obs-websocket-js is a JavaScript client that connects to the obs-websocket plugin for Open Broadcaster Software. The relationship to the underlying plugin is stated directly: the package description says it consumes the obs-websocket project, and the protocol documentation it links to is the one generated for that plugin.
That distinction matters for anyone choosing a dependency. OBS Studio itself does not speak WebSocket. A plugin has to be installed and enabled in the running OBS instance, and it is that plugin which exposes the request and event protocol this client speaks. The library is a convenience layer over that protocol: connection handling, authentication, the request call, and event subscription, all expressed as promises and typed with TypeScript declarations.
The audience is developers building things around a stream: browser overlays that react to scene changes, chat-driven tools that trigger actions, automation scripts that start recording on cue. The repository is maintained by the OBS Websocket Community, and the project was created by Brendan Hagan, with the package description naming @Palakis/obs-websocket as the underlying implementation.
The npm package is obs-websocket-js, the default branch is master, and the README carries a prominent version warning: the documentation you are reading is for v5, with v4 documentation kept on a separate branch.
JSON and MessagePack builds, and the export map that picks them
The npm package ships two builds because obs-websocket supports two message encodings, and the choice has real consequences.
| Connection Encoding | JSON | Msgpack | |---|---|---| | Used by default | By web bundles | By node.js | Manually opting into | `import OBSWebSocket from 'obs-web-socket/json'` | `import OBSWebSocket from 'obs-web-socket/msgpack'` | Benefits | Easier debugging, smaller bundle | Connection uses less bandwidth | Downsides | Connection uses more bandwidth | Harder to debug, bigger bundle size
The default is decided by the environment rather than by you. The export map in package.json routes the browser condition to dist/json.js, and both the import and require conditions of the root entry to the MessagePack builds, so Node gets MessagePack and a web bundle gets JSON unless you override it.
The trade-off is bandwidth against debuggability. MessagePack keeps the connection small on the wire, which matters for a stream sending high-frequency events, while JSON payloads can be read directly in a network inspector. The documentation also notes that each build exists in modern and legacy variants: modern bundlers get a build using recent JavaScript features, and if you need older browsers you have to configure your bundler to transpile dependencies as well.
The package also sets sideEffects to false and ships type declarations for each entry point, so tree shaking and typed imports work without extra configuration.
Connecting: the signature that carries url, password and RPC version
The connect method takes three arguments and returns a promise:
connect(url = 'ws://127.0.0.1:4455', password?: string, identificationParams = {}): PromiseThe url defaults to ws://127.0.0.1:4455, which is the local OBS instance. The password is the one configured in the plugin. The third argument is the interesting one: an object sent with the Identify message, and the documentation says to use it to include an RPC version to guarantee compatibility with the server.
The promise resolves with data from the Hello and Identified messages, so the result carries both the server version and the negotiated RPC version. It rejects with a connection error, either matching an obs-websocket WebSocketCloseCode or with the code -1 when a non-compatible server is detected. That last detail is the one to remember: an incompatible server is reported as a specific code rather than a generic failure.
The documented usage runs from the simplest case to a full one:
import OBSWebSocket, {EventSubscription} from 'obs-websocket-js';
const obs = new OBSWebSocket();
// connect to obs-websocket running on localhost with same port
await obs.connect();
// Connect to obs-ws running on 192.168.0.4
await obs.connect('ws://192.168.0.4:4455');
// Connect to localhost with password
await obs.connect('ws://127.0.0.1:4455', 'super-sekret');
// Connect expecting RPC version 1
await obs.connect('ws://127.0.0.1:4455', undefined, {rpcVersion: 1});Passing undefined for the password to reach the third argument is a small ergonomic wart in an otherwise clean signature.
Event subscriptions, reidentify, and the high-volume audio event
The subscription model is where a live tool lives or dies, because a client can ask for every event OBS emits and drown in its own callbacks.
EventSubscription is a bitmask, and the connect example shows the pattern for opting into a high-volume stream alongside everything else:
await obs.connect('ws://127.0.0.1:4455', undefined, {
eventSubscriptions: EventSubscription.All | EventSubscription.InputVolumeMeters,
rpcVersion: 1
});Since the mask can be combined, asking for All already includes the volume meters, and the example is really showing how the flags compose rather than expressing a requirement to add a specific one. The practical advice is the opposite of that: a browser overlay usually wants a narrow set of events, and the bitmask exists so you can be narrow.
Changing the subscription after connecting is what reidentify is for:
await obs.reidentify({
eventSubscriptions: EventSubscription.General | EventSubscription.InputShowStateChanged
});The promise resolves when the server acknowledges the request, so you know the new subscription is in effect. That matters for a tool that starts narrow and widens when a scene with audio meters appears, without tearing down the connection.
Disconnecting is the third lifecycle method:
await obs.disconnect();The promise resolves when the connection is closed, and the documentation notes that disconnect keeps any registered event listeners, so a reconnect does not require re-registering handlers.
Sending requests with call and the request type strings
Every operation on the OBS side goes through a single method:
call(requestType: string, requestData?: object): PromiseThe requestType is a string naming one of the protocol's requests, and the requestData is an optional object of arguments for it. The result resolves with the response data from the server.
The strength of this design is that the client is not tied to a fixed set of typed methods, so any request the plugin supports is reachable without waiting for a library release. The cost is that the type checking on requestType depends on how the request names are typed in this package, and the requestData shape is only as safe as the string you pass, since the protocol is a bag of stringly typed messages.
For a tool that wants to be resilient across OBS versions, the request strings are the extension point. You can call a request that the running server does not implement and get an error back from the promise rather than a client-side exception, which is a reasonable behaviour for a script that talks to whatever OBS the streamer happens to have installed.
The package description names @Palakis/obs-websocket as the consumed implementation, and the README links the generated protocol documentation for the list of request types.
Where this client stops: version negotiation and the browser boundary
There are two boundaries worth being honest about.
The first is protocol version compatibility. The connect result includes obsWebSocketVersion and negotiatedRpcVersion, and the documentation shows them being read to confirm what was actually negotiated:
try {
const {
obsWebSocketVersion,
negotiatedRpcVersion
} = await obs.connect('ws://192.168.0.4:4455', 'password', {
rpcVersion: 1
});
console.log(`Connected to server ${obsWebSocketVersion} (using RPC ${negotiatedRpcVersion})`)
} catch (error) {
console.error('Failed to connect', error.code, error.message);
}Requesting a specific rpcVersion pins the protocol the client will speak, and a non-compatible server produces the -1 rejection code rather than a confusing runtime failure later. Pinning version 1 is a conservative choice, and the risk is that features introduced in later protocol versions are then unavailable even though the server supports them.
The second boundary is the browser. A browser overlay cannot open a WebSocket to ws://127.0.0.1 without a proxy in front of OBS, because the page's origin is not the machine running OBS, and mixed content rules block ws:// from an https:// page. The documentation mentions a proxy supporting https with a wss:// URL, which is the supported path, and the web-tester sample in the samples directory exists to exercise that path.
The package is also explicit about its runtime floor. The engines field requires node >=22.0.0, so an older Node in a build pipeline is not supported for the server side.
MIT licensed, version 5.0.8, and a repository still taking pushes
The package is MIT licensed and the current version is 5.0.8, released on 2026-03-14, with v5.0.7 on 2025-11-09 and v5.0.6 on 2024-08-11. The repository is not archived and the last push was on 2026-10-19, so the code is still moving even where the release tags are spaced months apart.
That gap between release tags and push activity is the normal shape for a library that follows a protocol rather than a feature roadmap. New work lands in master, and a release follows when the maintainers decide the current state is worth pinning.
The repository layout supports the multi-target build. There is a src directory, a tests directory, a scripts directory, and a samples directory with three examples: node-sample, vite-sample and web-tester. The build scripts in package.json show how the two encodings and the standalone bundle are produced, with tsup building the JSON and MessagePack entry points separately and an unpkg-specific build for the standalone file. The public files list is only dist and the declaration files, which keeps the installed package small.
For a consumer, the upgrade cost is low in the usual case, since the API is a small set of methods, and higher if you depend on the exact export paths, because the export map is where the JSON and MessagePack distinction lives and where a major version could move things.
Editorial conclusion
obs-websocket-js is the right choice for a JavaScript or TypeScript tool that has to drive OBS Studio over the obs-websocket protocol, particularly a browser overlay or a Node automation script, and it is the wrong choice for a non-JavaScript caller or for anything that needs protocol features the plugin does not expose. Before depending on it, check the RPC version your running obs-websocket speaks against the rpcVersion you request at connect time, and pick the JSON or MessagePack build deliberately, since the export map chooses MessagePack for Node and JSON for browser bundles by default.
Frequently asked questions
How do I install obs-websocket-js?
The recommended method is a package manager, npm install obs-websocket-js or yarn add obs-websocket-js, which is what you want for node.js, a webpack or rollup bundle, or the type definitions. A standalone file is also served from the jsdelivr and unpkg CDNs.
How do I connect to OBS Studio with obs-websocket-js?
Create an OBSWebSocket instance and call connect, which takes an optional websocket url defaulting to ws://127.0.0.1:4455, an optional password, and optional identification parameters. The returned promise resolves with data from the Hello and Identified messages, including the negotiated RPC version.
Should I use the JSON or the MessagePack build?
The export map picks MessagePack for node.js and JSON for web bundles by default. JSON is easier to debug and produces a smaller bundle but uses more bandwidth, while MessagePack uses less bandwidth and is harder to debug; you can opt in explicitly with the obs-websocket-js/json and obs-websocket-js/msgpack entry points.
How do I request only the OBS events I need?
Pass an eventSubscriptions bitmask in the connect identification parameters or in a later reidentify call. The flags can be combined, and reidentify resolves once the server has acknowledged the change, so a client can widen or narrow its subscription without reconnecting.
What happens if the obs-websocket server is on a different version?
The connect promise rejects with a connection error, either an obs-websocket WebSocketCloseCode or the code -1 when a non-compatible server is detected. You can pin the protocol by passing an rpcVersion in the identification parameters.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/obs-websocket-community-projects-obs-websocket-js)Was this useful?