Modern .NET client for OBS Studio WebSocket v5, with generated protocol types and DI-first integration.
net10.0net9.0
dotnet add package ObsWebSocket.Core- Strongly typed request/response DTOs generated from the obs-websocket protocol
- Strongly typed event args
- Async-first API with cancellation support
- DI helpers via
AddObsWebSocketClient() - JSON and MessagePack transports (configurable per environment)
- Reconnect, timeout, and event subscription options
- Typed settings helpers for inputs, filters, transitions, outputs, and stream service — works with built-in library types or your own AOT-safe source-generated types
OBS WebSocket v5 only (OBS Studio 28+). Enable the server via Tools → WebSocket Server Settings in OBS.
appsettings.json:
{
"Obs": {
"ServerUri": "ws://localhost:4455",
"Password": "",
"Format": "Json"
}
}Program.cs:
usingObsWebSocket.Core;HostApplicationBuilderbuilder=Host.CreateApplicationBuilder(args);builder.Services.Configure<ObsWebSocketClientOptions>(builder.Configuration.GetSection("Obs"));builder.Services.AddObsWebSocketClient();builder.Services.AddHostedService<Worker>();awaitbuilder.Build().RunAsync();Worker.cs:
usingObsWebSocket.Core;usingObsWebSocket.Core.Events.Generated;publicsealedclassWorker(ObsWebSocketClientclient):IHostedService{publicasyncTaskStartAsync(CancellationTokenct){client.CurrentProgramSceneChanged+=OnSceneChanged;awaitclient.ConnectAsync(ct);varversion=awaitclient.GetVersionAsync(cancellationToken:ct);Console.WriteLine($"Connected to OBS {version?.ObsVersion}");}publicasyncTaskStopAsync(CancellationTokenct){client.CurrentProgramSceneChanged-=OnSceneChanged;if(client.IsConnected)awaitclient.DisconnectAsync();}privatestaticvoidOnSceneChanged(object?_,CurrentProgramSceneChangedEventArgse)=>Console.WriteLine($"Scene changed: {e.EventData.SceneName}");}// One-liner helperawaitclient.SetInputTextAsync("NewsTicker","Breaking: Live now!",ct);// Or use a typed settings object to update multiple properties at oncevarsettings=newTextGdiPlusInputSettings(Text:"Breaking: Live now!",WordWrap:true);awaitclient.SetInputSettingsAsync("NewsTicker",settings,ct);TextGdiPlusInputSettings is a built-in library type. The same pattern applies to TextFreetype2InputSettings, BrowserSourceSettings, and filter settings types — all live in ObsWebSocket.Core.Protocol.Common.InputSettings and ObsWebSocket.Core.Protocol.Common.FilterSettings.
varstatus=awaitclient.GetReplayBufferStatusAsync(cancellationToken:ct);if(status.OutputActive==true){awaitclient.SaveReplayBufferAsync(cancellationToken:ct);Console.WriteLine("Replay saved.");}Use a library type for common properties, or define your own type to target exactly what you need:
// Library type — covers the standard browser source propertiesvarcurrent=awaitclient.GetInputSettingsAsync<BrowserSourceSettings>("StreamOverlay",ct);Console.WriteLine($"Current URL: {current?.Url}");awaitclient.SetInputSettingsAsync("StreamOverlay",newBrowserSourceSettings(Url:"https://myoverlay.example.com",Width:1920,Height:1080),ct);// Consumer type — define only the properties you care about, fully AOT-safe[JsonSerializable(typeof(OverlaySettings))]internalpartialclassMyContext:JsonSerializerContext{}internalsealedrecordOverlaySettings([property:JsonPropertyName("url")]string?Url=null,[property:JsonPropertyName("css")]string?Css=null);awaitclient.SetInputSettingsAsync("StreamOverlay",newOverlaySettings(Url:"https://myoverlay.example.com"),MyContext.Default.OverlaySettings,ct);Raw
JsonElementaccess is also available — all settings helpers have counterparts in the generated types underObsWebSocket.Core.Protocol.Requestsif you need full control.
All typed settings helpers have two overloads: an implicit one for library-registered types, and an explicit one accepting a JsonTypeInfo<T> for consumer-provided types. All are AOT-safe when using the explicit overload.
Settings read/write:
| Helper | Notes |
|---|---|
GetInputSettingsAsync<T> / SetInputSettingsAsync<T> | Input settings; Set supports overlay |
GetInputDefaultSettingsAsync<T> | Default settings for a given input kind |
GetSourceFilterSettingsAsync<T> / SetSourceFilterSettingsAsync<T> | Filter settings; Set supports overlay |
GetSourceFilterDefaultSettingsAsync<T> | Default settings for a given filter kind |
GetCurrentSceneTransitionSettingsAsync<T> / SetCurrentSceneTransitionSettingsAsync<T> | Transition settings |
GetOutputSettingsAsync<T> / SetOutputSettingsAsync<T> | Output settings |
GetStreamServiceSettingsAsync<T> / SetStreamServiceSettingsAsync<T> | Stream service settings |
Scene / source utilities:
SwitchSceneAndWaitAsync(scene, ct)— switch scene and wait for the event to confirmSetInputTextAsync(name, text, ct)— shorthand for updating text source contentCreateSourceFilterAsync<T>(source, filterName, kind, settings, ct)— add a typed filterSourceExistsAsync(name, ct)— check whether a source existsWaitForEventAsync<TEventArgs>(ct)— await the next occurrence of any typed OBS event
For direct low-level access, all generated request/response types are in:
ObsWebSocket.Core.Protocol.RequestsObsWebSocket.Core.Protocol.ResponsesObsWebSocket.Core.Events.Generated
CallBatchAsync accepts a List<BatchRequestItem>. Each item's RequestData should be null, a JsonElement built with Utf8JsonWriter, or a generated *RequestData DTO — anonymous types and reflection-based serialization are not AOT-safe here.
ObsWebSocket.Example is a host-based sample with configuration and DI.
- Interactive mode — command loop (
help,version,scene,batch-example,get-all-settings-types, etc.) - Transport validation mode — exercises JSON and MsgPack across scene/input/filter/settings APIs, then enters the interactive loop
- One-shot mode — pass a command as a process argument for CI/automation:
ObsWebSocket.Example run-transport-tests
appsettings.json:
{
"Obs": {
"ServerUri": "ws://localhost:4455",
"Password": "",
"Format": "Json"
},
"ExampleValidation": {
"RunValidationOnStartup": false,
"ValidationIterations": 1
}
}dotnet publish ObsWebSocket.Example/ObsWebSocket.Example.csproj -c Release -r win-x64 --self-contained trueContributions are welcome. See CONTRIBUTING.md.
MIT. See LICENSE.txt.