OBSClient 4.0.0
dotnet add package OBSClient --version 4.0.0
NuGet\Install-Package OBSClient -Version 4.0.0
<PackageReference Include="OBSClient" Version="4.0.0" />
<PackageVersion Include="OBSClient" Version="4.0.0" />
<PackageReference Include="OBSClient" />
paket add OBSClient --version 4.0.0
#r "nuget: OBSClient, 4.0.0"
#:package OBSClient@4.0.0
#addin nuget:?package=OBSClient&version=4.0.0
#tool nuget:?package=OBSClient&version=4.0.0
OBSClient
A complete .NET client for OBS Studio, with two ways to work with it:
ObsClientspeaks the protocol. Every request and every event of obs-websocket, as typed, async C#. Complete, direct and nothing hidden: the API of OBS Studio, as it is.ObsModelspeaks OBS Studio. A live model of your scenes, inputs and filters, which you work with by name. Intuitive, and close to what you see in OBS Studio itself. New, and built onObsClient.
One package, one connection: use either, or both together. We set out to make a client that is complete and easy to use. ObsClient is the first, and ObsModel is the second.
Works with OBS Studio 28 and up (ObsModel needs 30.1), on Windows, Linux and macOS. Implements obs-websocket 5.7.6 (including multi-canvas support added in 5.7.0).
Targets net10.0, the current LTS release of .NET (see Compatibility).
Documentation, guides and the API reference: tinodo.github.io/obsclient
Two ways to work with OBS Studio
OBS Studio is controlled through obs-websocket, a protocol of requests and events. A protocol client hands you those, and the bookkeeping is yours: asking for the state, looking up ids, and following the events to keep your own picture of OBS Studio up to date. That is ObsClient, and it is complete. ObsModel is the next level: it keeps the picture for you, so that you work with OBS Studio the way you see it in its window.
ObsClient: the protocol |
ObsModel: OBS Studio by name |
|
|---|---|---|
| It is | the obs-websocket protocol, as typed, async C# | a live model of OBS Studio in memory, built on ObsClient |
| You think in | requests, responses and events | scenes, inputs, scene items, filters and outputs |
| You find things by | a name or a UUID, and ids that you look up first | their name: obs.Scenes["Gaming Scene"] |
| You read state | with a request, each time | from a property, answered from memory, with no request |
| A change | is one request; the call returns when OBS Studio has answered | is a method on the object; the call returns when the model shows the change |
| You hear about changes | with one event for each kind of change, and you apply them | with Changed and INotifyPropertyChanged, after the model has applied them, and Resynchronized when it read everything again |
| It covers | the whole protocol | the whole protocol too, by name: scenes, inputs (settings, audio, media), scene items (transforms), filters, outputs, studio mode and transitions, profiles and scene collections, canvases and the settings of OBS Studio. Request batches, and the scenes of canvases other than the main one, through obs.Client |
| It needs | OBS Studio 28 or newer | OBS Studio 30.1 or newer |
| In 4.0 | the API is closed | new in 4.0, and its API is closed too |
The same job at both levels: hide the webcam in the Gaming scene.
// ObsClient: a scene item is a number, so you look it up first.
int webcam = await client.GetSceneItemId("Gaming Scene", "Webcam");
await client.SetSceneItemEnabled("Gaming Scene", webcam, false);
// ObsModel: by name.
await obs.Scenes["Gaming Scene"].Items["Webcam"].SetEnabledAsync(false);
Which one? Use ObsModel to build something that shows or controls OBS Studio over time, such as a panel, a bot, an overlay or a user interface, when OBS Studio is 30.1 or newer. Use ObsClient for everything that the model does not cover yet, for request batches, for OBS Studio 28 to 30.0, and for a quick script that sends a few commands. Use both when you are in between: obs.Client is the ObsClient, on the same connection. Two ways to work with OBS Studio tells the whole story, with the same jobs done both ways.
Installation
Install from the NuGet Gallery
Or through the NuGet CLI: NuGet\Install-Package OBSClient -Version 4.0.0
From the command line: dotnet add package OBSClient --version 4.0.0
4.0.0 is the first stable release of 4.0. The public API of the client and of ObsModel (OBS Studio by name) is closed: the versions of 4 that follow add to it, and do not break it.
It targets net10.0 only; an application on .NET 8 or 9 stays on 4.0.0-rc.1, which also has the net8.0 library.
Please report what you find in an issue.
Upgrading from 3.x? See Moving from 3.x to 4.0, and the Breaking changes in the CHANGELOG.
ObsClient: the protocol, as it is
Every request of obs-websocket is a method of ObsClient with the name of the request, every event is an event with the name of the event, and every response is typed. What you send is what the protocol defines, and what you get is what OBS Studio answers. Calling requests, Handling events and Request batches tell the rest.
Simple
ObsClient client = new();
bool isConnected = await client.ConnectAsync();
if (isConnected)
{
await client.SetCurrentProgramScene("Gaming Scene");
Monitor[] monitors = await client.GetMonitorList();
client.Disconnect();
}
client.Dispose();
Request Batch
ObsClient client = new();
bool isConnected = await client.ConnectAsync(true, "P@ssw0rd", "localhost", 4455, EventSubscriptions.Filters | EventSubscriptions.Scenes);
if (isConnected)
{
RequestBatchMessage batchRequest = new();
batchRequest.AddToggleVirtualCamRequest();
batchRequest.AddSetStudioModeEnabledRequest(true);
batchRequest.AddSleepRequest(5000, null);
batchRequest.AddToggleVirtualCamRequest();
batchRequest.AddSetStudioModeEnabledRequest(false);
var result = await client.SendRequestBatchAsync(batchRequest, 6000);
client.Disconnect();
}
client.Dispose();
Names and UUIDs
Whatever OBS Studio can look up by name or by UUID — a scene, an input, a source — is one argument of a type that holds
either: a SceneRef, an InputRef or a SourceRef. A name, a Guid, and the Scene, Input or SceneItem that OBS Studio
gave you convert implicitly, so there is nothing to wrap:
await client.SetCurrentProgramScene("Gaming Scene"); // by name
await client.SetCurrentProgramScene(sceneUuid); // by UUID, which survives a rename
await client.SetCurrentProgramScene(scene); // a Scene from GetSceneList (identified by its UUID)
bool muted = await client.GetInputMute("Microphone");
await client.SetSceneItemEnabled(SceneRef.FromName("Gaming Scene"), item.SceneItemId, true);
Optional arguments are optional (await client.GetSceneItemList("Scene", canvasUuid: canvas.CanvasUuid)), and an argument that
is missing or wrong is refused with an ArgumentException before anything is sent.
Canvases (obs-websocket 5.7.0+)
ObsClient client = new();
bool isConnected = await client.ConnectAsync();
if (isConnected)
{
Canvas[] canvases = await client.GetCanvasList();
foreach (var canvas in canvases)
{
SceneListResponse scenes = await client.GetSceneList(canvas.CanvasUuid);
}
client.Disconnect();
}
client.Dispose();
ObsModel: OBS Studio, as you see it
New in 4.0.
ObsModel reads your scenes, inputs, scene items, filters and outputs (stream, recording, virtual camera, replay buffer) into a model in memory, follows what happens in OBS Studio, also what the user does in its window, and lets you work by name. It is built on the ObsClient above, which stays available as obs.Client. It needs OBS Studio 30.1 or newer.
await using var obs = new ObsModel();
await obs.ConnectAsync("localhost", 4455, "password"); // returns when the model is complete
await obs.Scenes["Gaming Scene"].SwitchToAsync();
await obs.Inputs["Microphone"].SetMutedAsync(true);
await obs.Scenes["Gaming Scene"].Items["Webcam"].SetEnabledAsync(false);
await obs.Inputs["Microphone"].Filters["Noise Gate"].SetEnabledAsync(true);
await obs.Outputs.Record.StartAsync(); // returns when OBS Studio has begun to record
bool visible = obs.Scenes["Gaming Scene"].Items["Webcam"].IsEnabled; // read from memory: no request is sent
obs.Changed += (_, e) => Console.WriteLine(e); // what changes, also what the user does in OBS Studio
What it gives you:
- Names, not plumbing.
obs.Scenes["Gaming Scene"].Items["Webcam"]: no UUIDs, no scene item ids, no indexes to look up first. - Reads without a round trip. A property is answered from memory, from any thread, and sends no request.
- Changes you can rely on. A change is a method on the object. Changes are made one at a time, in the order in which you ask for them, and each call returns when the model shows the change (an output returns when OBS Studio has begun to start or stop). When something goes wrong the exception says whether the change was made. A change is never made twice.
- Changes you can follow.
Changedsays what changed, in order, and every object implementsINotifyPropertyChanged, so a user interface can bind to it. When the model had to read everything again (a lost connection came back, OBS Studio switched its scene collection, or your handlers were too slow),Resynchronizedsays so: read again what you hold. - Made for the real OBS Studio. It reconnects and reads again, follows a switch of the scene collection, and knows that OBS Studio keeps a removed scene or input alive for as long as a scene item shows it. It is checked against a simulation of OBS Studio that reproduces what the real one does that surprises, and in CI against a real one.
- Nothing hidden.
obs.Clientis theObsClient.
Not in the model yet: the scenes of the canvases other than the main one (obs.Canvases lists the canvases), and request batches (the model makes its changes one at a time, in order). Use obs.Client for those, in the same program, on the same connection; the model follows what they do:
var scenes = await obs.Client.GetSceneList(obs.Canvases["Vertical"].Uuid); // not in the model yet: the scenes of another canvas
var video = await obs.GetVideoSettingsAsync(); // the model: the settings of OBS Studio itself are in it
await obs.Profiles.SetCurrentAsync("Gaming"); // the model: profiles and scene collections are in it
await obs.Scenes["Gaming Scene"].SwitchToAsync(); // the model
Controlling OBS Studio by name has all of it, and samples/ModelSample is a small command prompt to try it against your own OBS Studio.
Good to know about ObsClient
- Events are raised by a dispatcher of the client, one at a time and in the order in which they happened, on a thread that is not
the one that reads from OBS Studio: a handler can wait for a response from OBS Studio, and a slow handler only delays the events
behind it.
PropertyChangedand the statistics events are raised on the thread that causes the change: keep those handlers quick. - Requests time out after 500 ms by default. Change
ObsClient.RequestTimeoutif OBS Studio is busy (batches with longSleeprequests need a longer timeout; seeSendRequestBatchAsync). - Every call can be cancelled. Every request,
ConnectAsync,SendRequestBatchAsyncandReidentifyAsynctake aCancellationToken: cancelling it ends the wait with anOperationCanceledExceptionand leaves the connection usable. A request that was already sent is not undone. - A lost connection is reported, not hidden.
ConnectionStateandConnectionClosedtell you when it ends, requests that were waiting fail at once with anObsClientException, andAutoReconnectbrings the connection back. - See what goes over the wire. The client logs every message it sends and receives at the debug level (
Sending: …,Received: …) to theILogger<ObsClient>you give it. The answer to the authentication challenge is left out of what is sent.
Try it: the test app
OBSClient.TestApp is a Windows (WinUI 3) application to try every request and event of this library by hand
against your own OBS Studio, and to compare what OBS Studio sends with what the client returns. Every
release has it as a download (OBSClient.TestApp-<version>-win-x64.zip: unzip it, start
OBSClient.TestApp.exe), and every CI run publishes it as an artifact; see CONTRIBUTING.md.
Staying in sync with obs-websocket
The requests, the responses, the events and the request and event enums of the client are generated from the
machine-readable protocol definition of obs-websocket
(protocol.json) and the
few decisions that it cannot make for the client (tools/protocol/overlay.toml). The generated C# in OBSClient/Generated
is committed and compiled into the library like any other source; the definition, the overlay and the generator are
inputs of the build under tools/ and are not part of the package, which is a single assembly.
A scheduled workflow opens an issue as soon as obs-websocket publishes a new version, with the differences and the outcome of
trying the update; python tools/protocol/update.py --latest applies it. The tests in OBSClient.Tests/Conformance call every
request method and batch builder and compare what is sent, field by field, with a snapshot of the definition, and verify the
events, responses and enum values too.
See CONTRIBUTING.md for how to build, test, and contribute, and CHANGELOG.md for release history.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on OBSClient:
| Package | Downloads |
|---|---|
|
Unfucked.OBS
Fix egregiously broken or missing functionality in OBSClient. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 4.0.0 | 94 | 11/11/2026 | |
| 4.0.0-rc.3 | 100 | 11/9/2026 | |
| 4.0.0-rc.2 | 53 | 11/8/2026 | |
| 4.0.0-rc.1 | 66 | 11/3/2026 | |
| 4.0.0-beta2 | 98 | 10/29/2026 | |
| 4.0.0-beta1 | 104 | 10/27/2026 | |
| 3.0.1 | 1,172 | 5/9/2026 | |
| 3.0.0 | 570 | 10/14/2025 | |
| 2.1.1 | 5,274 | 4/11/2024 | |
| 2.0.2 | 361 | 2/19/2024 | |
| 2.0.2-preview.2 | 177 | 2/19/2024 | |
| 2.0.1 | 340 | 2/17/2024 | |
| 2.0.0 | 513 | 11/4/2023 | |
| 2.0.0-preview.2 | 234 | 11/2/2023 | |
| 2.0.0-preview.1 | 198 | 10/27/2023 | |
| 1.3.0 | 348 | 10/23/2023 | |
| 1.2.1 | 335 | 10/15/2023 | |
| 1.2.0 | 6,430 | 1/24/2023 |