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:
using ObsWebSocket.Core;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.Configure<ObsWebSocketClientOptions>(
builder.Configuration.GetSection("Obs"));
builder.Services.AddObsWebSocketClient();
builder.Services.AddHostedService<Worker>();
await builder.Build().RunAsync();Worker.cs:
using ObsWebSocket.Core;
using ObsWebSocket.Core.Events.Generated;
public sealed class Worker(ObsWebSocketClient client) : IHostedService
{
public async Task StartAsync(CancellationToken ct)
{
client.CurrentProgramSceneChanged += OnSceneChanged;
await client.ConnectAsync(ct);
var version = await client.GetVersionAsync(cancellationToken: ct);
Console.WriteLine($"Connected to OBS {version?.ObsVersion}");
}
public async Task StopAsync(CancellationToken ct)
{
client.CurrentProgramSceneChanged -= OnSceneChanged;
if (client.IsConnected) await client.DisconnectAsync();
}
private static void OnSceneChanged(object? _, CurrentProgramSceneChangedEventArgs e) =>
Console.WriteLine($"Scene changed: {e.EventData.SceneName}");
}// One-liner helper
await client.SetInputTextAsync("NewsTicker", "Breaking: Live now!", ct);
// Or use a typed settings object to update multiple properties at once
var settings = new TextGdiPlusInputSettings(Text: "Breaking: Live now!", WordWrap: true);
await client.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.
var status = await client.GetReplayBufferStatusAsync(cancellationToken: ct);
if (status.OutputActive == true)
{
await client.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 properties
var current = await client.GetInputSettingsAsync<BrowserSourceSettings>("StreamOverlay", ct);
Console.WriteLine($"Current URL: {current?.Url}");
await client.SetInputSettingsAsync(
"StreamOverlay",
new BrowserSourceSettings(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))]
internal partial class MyContext : JsonSerializerContext { }
internal sealed record OverlaySettings(
[property: JsonPropertyName("url")] string? Url = null,
[property: JsonPropertyName("css")] string? Css = null
);
await client.SetInputSettingsAsync(
"StreamOverlay",
new OverlaySettings(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.