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.

  • Thanks so much, I will be merging this to my stage views.

  • I use your code to control OBS and project song lyrics every Sunday. Thanks so much for your work. I use "titles" or "notes" and recently tried to have OBS change based on the "type" which I understand is the plugiin in use. I can't make it work. I created a scene called "lyrics" and used a browser view as the source. I expected the lyrics to become visible when I selected an item that is a song in OpenLP, but no. What am I doing wrong, or not understanding correctly? I would like to extend this to activating scenes for scripture and presentations once I understand the concept.

    Thanks again.

  • It’s not called "type" but "plugin". The plugin names are

    • songs
    • bibles
    • media
    • presentations
    • images
    • custom

    You can see all the available information at http://localhost:4316/api/service/list when OpenLP is running.

  • @wildo5053 @fiveofsix The stage.js file is only partly updated to api version 2 and currently does not find the "plugin" name. It should find the plugin name around line 74, but is not set at this point. I can only assume the issue is the differences between the two api versions.

  • @wildo5053 @fiveofsix So the issue was a typo...mind you this is NOT my work, but I have no idea how it happened, just know you can fix it with a simple text edit.

    1. open the stage.js file.
    2. Find line 72, `OpenLP.currentPlugin = data.results.items[idx]["pluggin"];
    3. Remove the extra g so it says `OpenLP.currentPlugin = data.results.items[idx]["plugin"];`
    4. save the file.

    Now it should work as expected!

    The file is still a mix of API version 1 and 2, but it is functional!

  • @wildo5053 While mucking around in the code it became clear that if you have a part of a scene in OBS that is named lyrics, when the slide you are displaying is from the songs plugin then the part of the scene named lyrics is changed to visible.....This would allow you to have a scene with 2 video inputs, a video stream of the church alter area and the lyrics part that could be your croma touched words from OpenLP. If I understand this the words would only appear when you are displaying slides from the songs plugin.

  • Before lockdown I experimented briefly with OpenLP using it for a couple of services and a gospel concert we put on.

    During lockdown we've been presenting services on our church site weekly that simulate our traditional live service. These comprise a combination of audio and video recordings done by members of our church (prayers, readings and sermon) mixed in with YouTube videos of the songs the worship leader would have chosen to include in their service.

    Web stats suggest the services have been viewed by three times the number of people who were regularly attending our church so as we come out of lockdown our deacons want to try live streaming our service and am now wondering if a combination of OBS and OpenLP can be used to achieve this.

    So my question...

    I am completely new to OBS (and very new and out of practice with OpenLP!) and was assuming that for Songs I would add a scene in OBS that would use a "Window Capture" of my OpenLP screen and that this would either allow me to show a full lyric screen as part of the live service or to add the lyrics as a "sub-title" to whatever camera/video/image was being output to the live stream.

    However, this topic seems to suggest that it might be better to drive the service with OpenLP rather then OBS.

    Why choose to do things that way round?

    What am I missing?

    Is both controlling the lyrics in real time while operating what is output to the live stream too much for one guy to do by himself?

  • @NorfolkGreg I like you had to "Produce" a service when the Pandemic first hit. And like you I had to learn to live stream as we came back into the church to worship. Having used OpenLP a lot more than OBS at first it was a two person process to get the live stream to work...one running OpenLP to advance through the service items and one to operate OBS. When I got involved with the updating of the OBS script things changed! Now we have one person using a "Clicker" to move through the OpenLP service. OBS is completely controlled by OpenLP.

    It takes a bit to get everything setup, making and naming the scenes you want to use in OBS, but once you do the most we do in OBS anymore is adjust the audio....Our Praise team leader plays the organ/keyboard/piano and as the spirt moves him he tends to vary the volume!

    We use two computers to keep the processing time on OBS as fast as possible. One computer just runs OpenLP and the projectors. The other runs OBS and the live stream process. The two computers are connected to the same switch that is connected directly to the main switch that connects to our internet "modem". This gives us the best performance we can get for streaming. We have used the WiFi to stream, but at times the WiFi can get bogged down and cause issues.

    I had an issue early on where OBS would stop changing scenes after the Sermon. I now open the custom stage view in a separate browser window, mostly so I can watch it. I also modified the custom stage view to "Reset" itself after 10 min on the same slide....Our Pastor does NOT use slide during his sermon and adding the reset made everything work through out the service!

    I would suggest, based on my use, that using OpenLP to control OBS is a viable option for live streaming your services.

  • OK! I'm convinced! I'll follow your guidance, but I've fallen at the first hurdle.

    I'm trying out an install on my desktop at home, using the PortableApps versions on OBS and OpenLP. I plan to use the same memory stick on the new to me "Amazon Renewed" laptop due to arrive on Saturday. That's because I felt it may be a good idea to hand around a common installation on a set of memory sticks amongst our various volunteers once I have things working, rather than get them to do what could turn out to be a somewhat complex installation on their own machines.

    (We don't have a church machine for lyric presentation and each volunteer has their own installation of MediaShout currently. It's expected to be replaced with OpenLP when the currently licensed version ceases to work. My prayer is that in going for streaming we can introduce a single system for all our volunteers with a common set of graphics etc as graphic design is not everyone's forté!)

    When I use the Windows obs-websocket installer, should I choose the default:


    or is another folder wiser on a portable installation or simply not possible?

  • @NorfolkGreg I have not used the portable version of OBS...my best guess is you would use the default as the installer should know where OBS is located!

  • Thanks! I began to think that when I read more fulling the instructions for installing with the ZIP file. I confess I was taken aback by the {pf} placeholder. I guess I have been away from Windows to long after a five year period with Linux Mint.

    I'll explore more tomorrow morning when my brain is a little less addled.

  • Well, after a bit of relearning OpenLP (I hadn't touched it since running the Christmas 2019 morning service), I do now have a working copy of OpenLPPortable on a memory stick with assorted Bibles, songs, my customised themes and my Christmas service working as expected. I eventually realised that I needed to re-run the First Time Wizard to get the Remote item to appear on the Configure OpenLP dialogue! I even have the Stage view working on my Android phone. So I come to the bit that runs:


    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.


    Unfortunately, there isn't! I guess you'll realise that my copy of OBS is OBSPortable installed on the same USB stick.

    I guess the script expects to find things where conventionally installed programs are found and, of course, in my case they aren't!

    Are there other mistakes I could have made?

    If it is the script that needs tweaking, I'll need some fairly tight hand holding. While I'm competent with HTML/CSS but I'm extremely flaky on JavaScript, PHP almost everything else.

  • Trying to track down my non-working implementation, I see some incompatibilities!

    For example, the guidances on setting up stages in the manual say create a directory "stages", but the stage.html file has references to /stage/ in lines 29 and 30 and these are the lines that reference the .js files!. Seems a likely candidate to stop things working! :-)

    <script type="text/javascript" src="/stage/obs/stage.js"></script>

    <script type="text/javascript" src="/stage/obs/obs-websocket.js"></script>

    It's bed time! I'll try adding an "s" to those paths tomorrow! hopefully it will make a difference!

  • Well, I've done a few experiments and not got further forward.

    I'm still guessing that there are some wrong file paths in the stage.html file, so have kept a renamed original but not found one in my copy that provides the "“WebSockets Server Settings” item in the “Tools” menu of OBS. Here's my file structure. OBSPortable is in a directory of its own under the PortableApps.

    Any thoughts on whether it is the folder references in stage.html that need to be changed. Of course, ideally, I don't want to hard code the F: drive, as the whole thing needs to be transferable on other machines.

  • Oops! Clearly I was confused and don't understand the code in the files in the stages/obs folder at all!

    I've just discovered that changes the paths in stage.html stop the http://localhost:4316/stage/obs screen from working.

  • Cracked it! At least I think I have!

    The solution was to find the correct directory into which to copy the websocket plugin. I had been misled by the install instructions on the websocket Releases page. It says:


    Using the obs-websocket-4.9.0-Windows.zip archive : copy the contents of the archive to the root of your OBS Studio installation folder (either C:\Program Files\obs-studio or C:\Program Files (x86)\obs-studio).


    However, with an app installed under the PortableApps format, you'll find any app folder has three sub-folders, "App", "Data" and "Other". In the case of OBS, you'll find within the App folder that there is already an "obs-studio" sub-folder. In spite of the guidance in the readme.txt you find there saying "There is normally no need to directly access or alter any of the files within these directories", simply copy the contents of the websocket ZIP file there.

    I'll report further once I have confirmed it does all it should as I've now got to the point where I am supposed to set up some scenes in OBS, something I haven't tackled before!

  • This is really helpful - especially as we're all streaming everything now :)

    Has anyone had any success adapting this script for 2.9.2?

    My Javascript is limited and I haven't worked with websockets before. It seems that OpenLP is now using websockets for the remote (on port 4317) and OBS is listening by default on port 4444. What is the best way forward - one combined stage.js or two separate scripts?

    I think using OpenLP to seamlessly manage OBS is going to be hugely helpful for churches whose volunteers can manage OpenLP but for whom OBS is a bridge too far ...


  • I probably showing my ignorance about where the port numbers fit into the streaming process and where any conflicts might show up, so I'm posting this as I'm anxious to learn!

    As reported above, I'm using the current PortableApps versions of OBS and OpenLP and still haven't actually streamed anything as our media team discovered that the church's Internet upload speed is under 1Mbps so we didn't get that far but...

    In spite of having the 4317 and 4444 default port numbers in each app, we certainly are getting OpenLP to successfully alter scenes in OBS so that as you move to a song item in the OpenLP Service Manager that requires lyric sub-titles to appear with the pre-defined camera setting in OBS then that transition took place.

    So I'm not sure if there's any need to change the default ports for OpenLP 2.9.2 when everything seems to work under OpenLP 2.4.6.

    What am I missing? Or is it Michael Bishop worrying unnecessarily?

Sign In or Register to comment.