Tutorial: How to control OBS via OpenLP custom stage view

edited September 2020 in General Support
As the title already gives away, it’s possible to control OBS (in fact, almost everything in OBS) by changing slides in OpenLP. The way this works is by having a custom stage running in the background containing a JavaScript that sends WebSocket signals to OBS. But let’s go step by step.

You will need:

How to install the files

OBS Websocket Plugin

Just follow the instructions on the GitHub, specifically on the Releases page. (Search for “Install instructions”.)

OBS Custom Stage View

Create a custom stage view named “obs”, as described in the OpenLP manual. Put the files attached to this post (“stage.css”, “stage.html” and “stage.js”) inside of the folder.

OBS Websocket JavaScript API

Download the file “obs-websocket.js” from the GitHub – just look for the link “Download”. Put this file in the custom stage folder, too. Your OpenLP data folder should look something like this:

How to setup OpenLP

To activate the “Remote” plugin in OpenLP, check the OpenLP manual. To see, if it worked, open a service and put a song into Live Mode. Now try the default stage view at http://localhost:4316/stage. It should look like the one in the manual.
Then, check the custom stage view we just installed. It is located at http://localhost:4316/stage/obs. It should look like this, a large white background with the lyrics at the bottom:

How to setup OBS

Activate WebSocket

If the installation worked as it should, there is now a “WebSockets Server Settings” item in the “Tools” menu of OBS. There, activate the server, let the port stay at 4444, and don’t put in a password just now. It can be helpful to activate “Enable system tray alerts” for the moment, so you will get a notification, whenever the JavaScript is connecting to the WebSocket. Later, you can turn that off again.

Create a scene

You will probably have an OBS scene where the lyrics are displayed. For the OpenLP control to work, you have to have such a scene. Inside of it, you need to create a “Browser” item. Here are the settings for this item (they are in German, but you’ll probably get it anyway):

  • Local file: It’s not a local file.
  • URL: the same we had tested earlier
  • Width/Height: Just put in the dimensions you’re streaming in.
  • User defined FPS: yes
  • Reroute audio: There’s no audio.
  • FPS: I choose 5, because the Javascript updates every 500ms, so I think I don’t need to update the browser item all too often.
  • User defined CSS: Delete everything here, we’re taking care of that ourselves.
  • Deactivate when source is invisible: Now this is important! You will want to have the browser item active, even when it is not visible, and even when you’re in another scene that does not display lyrics. Our JavaScript is running inside this browser item, so if we deactivate it, the script is deactivated, too.
  • Refresh browser when scene gets active: Actually, I’m not sure about this one. I figured it’s probably good to have the browser refresh itself every now and then.
Note: As I said, the JavaScript is running inside of the browser item. There could be problems if you have multiple scenes with lyrics inside of them. If all of them use the same browser item, and all are active all the time, you will end up with multiple action triggers everytime you switch slides in OpenLP. This could have unintended consequences. A solution would be to have one custom stage view with the OBS JavaScript in one scene (and running in the background all the time), and have a second custom stage view without the OBS JavaScript in all the other scenes.

Try it out!

In theory it should already work. If you go to OpenLP and add to your service an item called, e.g. “scenechange:altar”, then the moment you activate it, OBS should switch to the scene called “altar”.

Well, this thing is getting rather long, so I will have to put the explanation of all this below.


  • How to customize the script (also: an explanation of how it all works)

    The custom stage view that we have installed is based on the default stage view provided with OpenLP. I tried to comment everything important inside of the files themselves. Nevertheless, I will now go briefly through all the changes I made, and at the end explain how to customize the script. This will involve a little programming. If you need help implementing a specific feature, just write a comment and I will get back to you.


    Here I just inserted one line, to include the obs-websocket.js file.
    <script type="text/javascript" src="/stage/obs/obs-websocket.js"></script>
    Also I needed to change which CSS and JS file it accessed. That was done by replacing “/files/” with “/stage/obs/” in these two lines.
    <script type="text/javascript" src="/stage/obs/stage.js"></script>
    <script type="text/javascript" src="/stage/obs/obs-websocket.js"></script>
    Then I deleted all the <div> elements I was not using, i.e. everything except #currentslide.


    The changes I made there are not for controlling OBS, rather it’s the way that I like my lyrics to look (bottom third with a nice font and semi-transparent background).
    Note the lines at the end, where I specify that the <div> elements with certain “data-plugin” attributes are not to be shown. The JavaScript will enter this information later.


    Here comes the interesting part. Again I deleted a whole bunch of lines I was not using:
    • the function “updateClock”
    • everything having to do with “nextslide” or “nextSong”
    • displaying notes
    • updating of the “verseorder“ information and keeping track of “currentTags” (I kept the “currentTag” though, in case someone wanted to use that)
    Then I inserted some lines to capture information I think might be useful if you wanted to control OBS. It’s found in these lines
    OpenLP.currentTitle = data.results.items[idx]["title"];
    OpenLP.currentNotes = data.results.items[idx]["notes"];
    OpenLP.currentPlugin = data.results.items[idx]["plugin"];
    And these:
    if(data.results.blank) {
      OpenLP.currentlyShowing = "blank";
    else if(data.results.theme) {
      OpenLP.currentlyShowing = "theme";
    else if(data.results.display) {
      OpenLP.currentlyShowing = "display";
    else {
      OpenLP.currentlyShowing = "slides";
    For cosmetic reasons I told the script to use forward slashes instead of new lines. If you prefer new lines, just change " / " back to "<br />".
    text = text.replace(/\n/g, " / ");
    The next line I need for the CSS to work correctly, i.e. only showing the <div> element, when the right kind of slide is selected.
    $("#currentslide").attr("data-plugin", OpenLP.currentPlugin);
    But the most important is, of course, the function “obsRemote”. In the file itself I comment on which variables you can use, and where to find the commands that the OBS Websocket API understands. Below this there are a few examples, which I will post here, too.
    OpenLP.obsHandler = new OBSWebSocket();
    OpenLP.obsHandler.connect({address: 'localhost:4444', password: ''}).then(() => {
      if(preg = /scenechange:(.+)/i.exec(OpenLP.currentTitle)) {
        OpenLP.obsHandler.send('SetCurrentScene', {'scene-name': preg[1]});
      if(preg = /scenechange:(.+)/i.exec(OpenLP.currentNotes)) {
        OpenLP.obsHandler.send('SetCurrentScene', {'scene-name': preg[1]});
      OpenLP.obsHandler.send('SetSceneItemProperties', {'item': 'lyrics', 'visible': (OpenLP.currentPlugin == "songs")});

    First, the script creates an OBSWebSocket object. The next is all one very large statement. The OBS Websocket JavaScript API uses something called “promises” where you put together a chain of commands. The first command is connecting it to the OBS Websocket. If you didn’t change the port and are running everything on the same machine “localhost:4444” is fine. If you would like to change the password in the OBS Websocket Server Settings, you have to change it here, too.
    After the connection is set up, all the other commands are executed. In this example there are three commands:
    1. It checks the title of the current slide (OpenLP.currentTitle), and if it encounters the string “scenechange:” followed by some word, OBS will switch to that scene.
    2. The same, but looking for “scenechange:” in the notes
    3. It switches the visibility of an OBS scene item called “lyrics” depending on if the currently displayed item is a song or not.
    You can use all of the following information as triggers for OBS
    • whether you are currently showing a slide, or have blanked the screen or just showing the theme without text
    • the type of the current item (song, bible, presentation etc.)
    • the title of the current item
    • the text inside the current slide
    • the tag of the current slide (verse, chorus, bridge etc.)
    I hope all of this is useful. It’s certainly something for people who have already done some programming. If you need help, try finding someone in your church with a little bit of experience in this area. They should be able to follow the steps. Or just write me a comment, and I will try to help as best I can remotely.
  • Also check out the obs-midi plugin (not by me). It’s a much more direct way of controlling OBS, by sending MIDI signals. It’s still in development, but it works and should be getting more stable with upcoming releases. (btw, you can also control OpenLP with MIDI).
  • Thanks for your great work.  I have no programming experience and it took a while to set it up but works well.  I made a few changes to font and color, choosing to display verses as multiple lines rather than one line.  I will look at the obs-midi plugin out of curiosity.  Thanks again

    Do you know if this will work with the new version of OBS that just came out?  
  • Hi, great to hear that it works! I would assume that it works with future versions of OBS. The OBS-Websocket-Plugin is widely used by many people, so they will work on keeping it working.
  • Hi Trying to get this to work but got to this part

    OBS Custom Stage View

    Create a custom stage view named “obs”, as described in the OpenLP manual. Put the files attached to this post (“stage.css”, “stage.html” and “stage.js”) inside of the folder.

    Where are the Files that are attacked to this post?

  • Scroll up in this discussion until you see the words "Try It Out!" and just below that is a zip file. Open the zip file and the OBS folder with files are there.

Sign In or Register to comment.