No internet connection
  1. Home
  2. How to

How to remote control OBS

By Christian Scheuer @chrscheuer2020-04-10 16:01:42.035Z2020-04-10 21:09:10.094Z

Prerelease information: This requires SoundFlow 3.6

It's possible to remote control the screen recording/broadcasting software OBS.

First, install the newest version of "obs-websocket" from here:
https://github.com/Palakis/obs-websocket/releases

Make sure to choose the mac pkg installer. For a direct link to the 4.7.0 installer that I used to test, click here:
https://github.com/Palakis/obs-websocket/releases/download/4.7.0/obs-websocket-4.7.0-macOS.pkg

Now run the installer. By default it sets up a websocket server to listen on port 4444, but you can change the port in OBS, in the menu Tools -> WebSockets Server Settings. For now just keep it on port 4444.

Now we're ready to run some code.

Here's a script that selects the scene "Scene 2":

function getConnection(port, alwaysReconnect) {
    /** @type {AxWebSocketClientConnection} */
    var connection = globalState.obsConnection;
    if (connection && alwaysReconnect) {
        connection.close();
        globalState.obsConnection = null;
        connection = null;
    }
    if (!connection) {
        connection = sf.net.wsOpenClient({
            url: `ws://127.0.0.1:${port}/ws`,
        }).connection;
        globalState.obsConnection = connection;
    }
    return connection;
}

function sendMessage(msg) {
    var connection = getConnection(4444);
    msg['message-id'] = globalState.obsMessageCounter === undefined ? (globalState.obsMessageCounter = 1) : (++globalState.obsMessageCounter);
    connection.sendJson({
        json: msg
    });
}

function selectScene(sceneName) {
    sendMessage({
        'request-type': 'SetCurrentScene',
        'scene-name': sceneName,
    })
}

selectScene("Scene 2");

The actual message is sent in selectScene function.

For a list of all the request types that can be sent, see this reference:
https://github.com/Palakis/obs-websocket/blob/4.x-current/docs/generated/protocol.md#requests

  • 20 replies

There are 20 replies. Estimated reading time: 10 minutes

  1. Another example could be:

    function startStopRecording() {
        sendMessage({ 'request-type': 'StartStopRecording' });
    }
    
    1. Andrew Scheps @Andrew_Scheps
        2020-04-10 19:47:50.505Z

        I've got it all working sending messages, but I can't quite figure out how to get info back from OBS. The following code gives me an error that sceneName is undefined. It should contain the results of the GetCurrentScene request shouldn't it? I'm assuming it's in a format that needs to be converted. I've tried String() and JSON.stringify() but still the same error.

        const OBS = require('package:ck8uk78nd000hma105p2rdzgz');
        
        var sceneName = OBS.sendMessage({
                'request-type': 'GetCurrentScene',
        })
        
        log(sceneName.name)
        
        1. Hi Andrew.

          Currently the script doesn't send back results from messages. If we want that, due to the async nature of websockets, we'll need to use callbacks - I can give you an example.

          1. Andrew Scheps @Andrew_Scheps
              2020-04-10 21:12:42.642Z

              Ok, no worries, I just got excited trying things. I don't really need it.

              1. Just for completeness, here is an example that also listens for responses:

                function getConnection(port, alwaysReconnect) {
                    /** @type {AxWebSocketClientConnection} */
                    var connection = globalState.obsConnection;
                    var messageHandlers = globalState.obsMessageHandlers = (globalState.obsMessageHandlers || {});
                    if (connection && alwaysReconnect) {
                        connection.close();
                        globalState.obsConnection = null;
                        connection = null;
                    }
                    if (!connection) {
                        connection = sf.net.wsOpenClient({
                            url: `ws://127.0.0.1:${port}/ws`,
                            onMessage: msg => {
                                var msgObj = msg.asJson();
                                var msgId = msgObj['message-id'];
                                var handler = messageHandlers[msgId];
                                if (handler) {
                                    delete messageHandlers[msgId];
                                    handler(msgObj);
                                }
                            }
                        }).connection;
                        globalState.obsConnection = connection;
                    }
                    return connection;
                }
                
                function sendMessage(msg, callback) {
                    var connection = getConnection(4444);
                    var msgId = globalState.obsMessageCounter === undefined ? (globalState.obsMessageCounter = 1) : (++globalState.obsMessageCounter);
                    msg['message-id'] = 'm' + msgId;
                    if (callback) {
                        globalState.obsMessageHandlers['m' + msgId] = callback;
                    }
                    connection.sendJson({
                        json: msg
                    });
                }
                
                function getSceneName(callback) {
                    sendMessage({
                        'request-type': 'GetCurrentScene',
                    }, msg => {
                        callback(msg['name']);
                    });
                }
                
                getSceneName(name => {
                    log('name: ' + name);
                });
                
                1. Andrew Scheps @Andrew_Scheps
                    2020-04-12 16:37:37.404Z

                    For some reason this script has stopped working today. Did you change anything in the web socket code? The one way commands are still working.

                    1. Hm I don't think I changed anything related to this, but we're on fairly untested ground so I may have to do some more debugging here to ensure the stability. I'll be working on stability testing this throughout the week so I'm sure I'll be sending you some more builds to test with :)

          2. In reply tochrscheuer:
            Andrew Scheps @Andrew_Scheps
              2020-04-10 21:19:54.525Z

              Just posted a bug with OBS and SoundFlow stopping talking to each other.

              1. In reply tochrscheuer:

                is the
                sf.net.wsOpenClient
                also listening to messages coming in in general or only responses from messages send?

                I might be using it wrong, but I'm experimenting with Vienna Instrument Pro (VIP), and can see it creates the websocket so VIP is able to be controlled and send it's data, but I only see this message in my log:

                when running

                sf.net.wsOpenClient({
                    url: `ws://10.0.111.1:8080/`, onMessage: msg => {
                        log(msg)
                    }
                }).connection
                

                In terms of sending messages to the socket / VIP I guess I might need to change/transform the string message into bytes or something else (was looking through socket.io-client which I'm currently using for sending messages to VIP and tried to follow the path of information, but was a bit above my level of experience).
                At least this doesn't work:

                sf.net.wsOpenClient({ url: `ws://10.0.111.1:8080/`}).connection.sendText({text: 'SETB'})
                
                1. Let's have a look at this together some time, where we can compare with our nodejs code together. I can't remember exactly how this was set up originally.

                  1. Sure thing. That would be really cool.
                    It's pretty awesome to see how it instantly connects/creates a websocket. I just need to get that sweet data from it :P

                    1. In reply tochrscheuer:

                      Yes, finally figured out how to get the info from the Vienna server. Just had to add .asString() to the function.
                      It's so smooth compared having to run a webserver in terminal 😁 Now I just need to run over all commands in the script.

                      sf.net.wsOpenClient({
                         url: `ws://${url}:${port}/`,
                         onMessage: msg => { log(msg.asString() },
                      }).connection;
                      
                    2. In reply tochrscheuer:

                      Gave it another (long) shot to see if I could figure out how to send messages to the WebSocket.

                      Tried a whole bunch of things, also with nodejs (which I might understand a little bit better now) and the terminal as well.
                      No luck. But it also seems that even with the old nodejs, it creates some errors and ultimately doesn't connect, so maybe it's something with Monterey and dependencies.

                      The way that the Vienna Player (and the nodejs) seems to send is via socket-io. This is just an outtake of those lines of the script related to sending:

                      const io = require("socket.io-client");
                      var wsURL = 'ws://127.0.0.1:8080'
                      wsock = io.connect(wsURL, { 'reconnection limit': 3000, 'max reconnection attempts': Infinity });
                      wsock.send('SETB')
                      

                      Can't figure out if what is being send is text or json if I would have to use the build in SF function. Attached the code if that could give some hints.

                      Would love help one day to figure it out :)

                      1. I later found out that socket.io is its own protocol that uses WebSockets but adds some stuff on top of it. So I'm not sure if SF can do socket.io connections.

                        1. Alright, that could maybe explain some things :)
                          So maybe I will have to make a solution with something external from SF.

                          1. Yea I think that's the easiest now.

                            1. So awesome. Got it to work with socket.io 0.9.6 which is 10 years old, haha.
                              Thank you!

                  2. D
                    In reply tochrscheuer:
                    DeLaVanta Tabor @DeLaVanta_Tabor
                      2022-05-18 02:41:31.020Z

                      Am I reading this correctly that in order for me to control OBS with the StreamDeck, I need to use the StreamDeck App or get a second deck to assign specially to OBS utilizing the StreamDeck OS application, which upon setting up SF was instructed to quit that app and stop it from opening on launch?

                      1. Hi @DeLaVanta_Tabor,

                        No, you can just install the OBS package from our Store :) It should have instructions. If you need help setting it up, please use the Script Help feature (bit.ly/sfscripthelp)