Use a Lua Script to Import Your Twitch Streaming Overlay Designs into OBS Studio

More Backgrounds Please
27 min readApr 10, 2021

Hi, welcome back to another tutorial for using Lua scripting with OBS Studio!

If you haven’t already checked out my previous tutorial “How to a create simple custom user interface inside OBS Studio with a Lua Script,” I highly suggest checking that tutorial out before diving into this one just to cover the initial basics of how to create the general layout for what we will be creating today.

TL;DR

Please forgive this long post as I tried my best to condense the things I learned while trying to be thorough in explaining what the code here does.

Please find the following links useful if you want to quickly use a template code to work with and a cheat sheet for what you will learn today to avoid this long read.

The completed final code you would make with this tutorial:

Lua Scripting Cheat Sheet that covers topics in this tutorial:

Who I Am

Just your neighborhood friendly amateur scripter/coder/designer/something guy.

Who Is This Tutorial For

This tutorial is great for:

  • Coders or developers who want to dabble with scripting in OBS Studio
  • Graphic and Motion Designers who have made streaming packages for OBS Studio or StreamLabs and want to incorporate a cool “importer” with their designs that will incorporate their own logo and brand
  • Twitch, Facebook, or Youtube Content Creators who want to learn more about scripting for OBS Studio
  • And anyone into streaming!

Note: This tutorial is for those who have some experience with coding, but for those who are new to coding I will try my best to go over what I did as we go along, as well as go over some of the ins and outs of the OBS Lua library which is what we will use to make our script do what we want.

Streaming is Here to Stay

Streaming has become incredibly popular in 2020 and 2021. According to https://muchneeded.com/twitch-statistics/, on Twitch alone about 46 billion minutes of live video is watched per month, 3.3 million unique Twitch Streamers per month, and 15 million unique daily visitors on the platform as well.

So many people are streaming, or either watching other people stream! With all this happening, I can’t imagine the sort of pressure James or Sam from down the block feel when they decide they also want to start live streaming on their favorite platform. They might be asking themselves where do I start or what do I use to make my streams better?

My goal is to try to tap into this mindset and show fellow coders, designers, and streamers how to use code to simplify some mundane tasks, like setting up your stream with scenes and overlays inside OBS Studio for the first time.

The value proposition for scripting inside OBS Studio

Not an actual representation of you or your customer

Say you are like me, a designer making content for streamers and you have a bunch of overlays you made. Your amazing design then gets purchased and now your customer, who wants to set up their stream with your designed package, needs to set up everything on their end manually. This could be a bit of a tedious task and will leave your customer guessing on how to setup their stream. This can be especially true when you provided them with so many various overlays.

In my packages alone I include on average:

  • 27 icons
  • 8 notification alerts
  • 5 animated screens
  • 2 camera overlays
  • 2 stinger transitions
  • 4 support bars

Here are some image examples of what I include in my overlay packs:

Some general examples of what I typically include in my stream packages for streamers to buy

Meanwhile, the customer probably really just wants a nice design that is easy to use and worth their money to stream with so that they can go have fun with friends and viewers online. Well with your new script, they can easily import a template you created for them inside OBS Studio, eliminating some of that initial guess work, and adding more value to your product, making you stand out as a designer. Incorporating some minor technical know-how can make your customers come back looking for more content, which is a win-win in my book. I have a few customers already telling me how much they appreciate the amount of content I provide for them and how easily they can setup everything.

But not all streamers use OBS Studio…

Now, you may be saying not all streamers use OBS Studio, which is true. A lot of streamers might use Stream Labs or StreamElements.com. Take this lua scripting tutorial as a way to be introduced to the power of code with your streams and the beginning of more possibilities.

The great part of this is that you can customize your layout and design the way you want to market your brand your way, leaving a more memorable experience to your customer. But if you are designing for Streamlabs, I would encourage you to take advantage of exporting your layouts into a .overlay file within the StreamLabs application and sharing that as your go-to easy importer file for your customers.

What You Will Learn

Through this tutorial I hope that you will be able to learn various things. Here are some to name a few:

  • Create easy to use functions that add overlay, texts, and images for us with the use of buttons
  • The use of key-value pairs within OBS Studio when defining our settings for our imported sources
  • The difference between scene items, scenes, and sources in OBS
  • A further understanding of the OBS Lua Library

What We Will Make Today

So as promised from my last tutorial, today I will go over how to make our buttons inside our interface actually do something. In the Gif below I have an example of what my script does (the designs come from a package I made on Etsy):

  1. It creates a simple user interface inside the Scripts Window for OBS Studio
  2. It has buttons that create new Scenes and imports new “Sources” for me quickly and easily. (Sources can be images, text, or video, and more)

As you can see in the gif above, by clicking on one button called “Import All Scenes,” I am able to quickly tell OBS:

“Hey, I have these overlays in this folder, please take them and make these specific scenes with my overlays and texts in them to make my template.”

It can really be that simple. In the Script Window for my interface on the right you can see the following:

  • A preview image of the stream package design
  • My small business logo
  • A customer support link and a message to the customer
  • Links to fonts that the customer will need to download
  • Buttons that the customer can use to import my designs as a starting point for their setup

Let’s get started

If you haven’t already played around with the initial template code I provided in my previous tutorial, I have left it here ready for you to use from my Github: https://github.com/Chriscodinglife/get-started-with-lua/blob/main/getting_start_with_lua_script.lua

As of this writing, this tutorial covers some lua scripting techniques with OBS Studio 26.1.1.

Import your starter script inside OBS Studio. It should look like the following:

What is exactly going on? We basically created two sections of our script. On our top section we have our Description. Here in the Description section we can add Images, Texts, and URL Links to our interface.

In the Lower section we have our buttons, or our properties section. If you recall from the previous tutorial, we created a properties object by calling “obs.obs_properties_create()” and added our “button()” function to our properties object. The properties object is basically settings that OBS Studio stores for the user which can then be changed or edited by the user.

Here in our properties is where we are going to add several of our buttons that will be attached to functions that do specific things. In this case we want them to import designs, texts, or images, that either we or someone else has created.

Just to go over, to create a properties object we can do this:

local properties = obs.obs_properties_create()
  1. Let’s change up our code a bit. I am going to first convert my logo into base64 data and import that into the icon1 local variable. You can convert your own logo or image here: https://www.base64-image.de/

2. I want to also personalize some of the text in my description. We can do this by editing the HTML in the description variable.

  • I will change the <h2> header tag text to “MBP Stream Importer” and change the URL link, that points to Google, instead to my Etsy store and change the text surrounding that link to match the title of my store. Here’s how I made that look.
local description = [[
<center><h2>MBP Stream Importer</h2></center>
<p>
<center><img width=60 height=60 src=']] .. icon1 .. [['/></center>
<p>
<center>For customer support with your stream package
<br>visit us on our <a href="https://www.etsy.com/shop/MoreBackgrounds">Etsy Store</a></center>
<hr/>
</p>
]]

3. Feel free to add any more headers, text, links, or images. This is your space and time to customize your interface to the way you like.

4. Now let’s add some more functions. Here is a breakdown of what we want to happen with our code:

  • We want a function that will create a scene named “Welcome” and “Starting Soon”
  • We want to import specific sources into our Welcome and Starting Soon Scene
  • We want a function that also checks if there are any scenes already created with the same name
  • Finally we want a function that will add all of our scenes with one press (in this case our two scenes Welcome and Starting Soon.)

Here is also a flowchart of how our various button functions should run:

The scene_exists function

Let’s first make a function that checks if a specific scene was already created. Here I called my function “scene_exists()” that accepts a parameter called “scene_name.”

function scene_exists(scene_name)local scene_names = obs.obs_frontend_get_scene_names()
local value = 0
for i, name in ipairs(scene_names) do
if string.find(scene_names[i], scene_name) then
value = value + 1
end
end
return value

end

The OBS Lua library has a method called “obs_frontend_get_scene_names().” This retrieves a list of all the available scenes in our current OBS session by names. You can see this in the above image example on line 22.

local scene_names = obs.obs_frontend_get_scene_names()

With this list, what the script will do is:

  • Compare each name of each scene to a name we want to compare it to. So let’s say we want to see if there are any scenes with a similar name “Welcome.” It will go through our list and count how many times there is a scene with a similar name. You can see this in our “for loop” on line 25.
  • The function will then return to us how many times it counted scenes with a similar name with the variable “value” on line 31.

We use Lua’s built in ipairs and string.find methods for the for loop. Ipairs returns two values, so we iterate through our scene_names list using variables i and name (name isn’t neccessary to use, an underscore would also be fine.) Using i as the index, we use string.find to find any instance where scene_names[i] matches text to our inputted parameter scene_name. If it finds a match it adds to our value variable by 1.

The create_scene function

Now let’s create a function that will actually create a new scene for us. I will name this function “create_scene()” that will accept a parameter called “scene_name” like our previous function.

function create_scene(scene_name)if scene_exists(scene_name) >= 1 then
scene_name = scene_name .. " " .. scene_exists(scene_name)
end
local new_scene = obs.obs_scene_create(scene_name)obs.obs_frontend_set_current_scene(obs.obs_scene_get_source(new_scene))
local current_scene = obs.obs_frontend_get_current_scene()
local scene = obs.obs_scene_from_source(current_scene)
obs.obs_scene_release(new_scene)
obs.obs_scene_release(scene)
return new_scene, sceneend

This function will create a new scene for us based on how many times a similar scene with the name we want exists, with the help of the scene_exists() function we created earlier.

Since our scene_exists() function returns the number of counted times a similar name of a scene is found, we use that to append the value it returns to the scene_name variable we create on line 38. Basically it is saying if we find more than one instance of a scene with a name similar to what we want to call it, we will add a number to the end of the name so that OBS Studio does not freak out over creating a new scene.

OBS Studio hates repeating scene names, so that is why we put this check in place.

if scene_exists(scene_name) >= 1 then
scene_name = scene_name .. " " .. scene_exists(scene_name)
end

Here we can see the OBS Lua library at work. To create a new scene we call “obs.obs_scene_create()” that takes in a name of our choice.

local new_scene = obs.obs_scene_create(scene_name)
  • OBS is smart enough at this point to create our scene but doesn’t select our scene as our current scene in our OBS Session. We select our new scene as the new current scene we want to see in our Stream Window with the code in line 43 in the example above.
obs.obs_frontend_set_currentscene(obs.obs_scene_get_source(new_scene))

We want a positive feedback for our customer so that when they create a new scene, that scene will be the scene they see in their stream preview window. The “obs_frontend_set_currentscene()” method in conjunction with the obs.obs_scene_get_source() method allows us to do this.

Now on lines 44 and 45 you can see we create two new objects called current_scene and scene.

local current_scene = obs.obs_frontend_get_current_scene()
local scene = obs.obs_scene_from_source(current_scene)

obs_frontend_get_current_scene() returns a reference to the currently active scene, meanwhile obs_scene_from_source() scene returns the context of a scene, according to the OBS Documentation.

We need to create these two objects in order to help us import overlays, texts, and images into each of our scenes later, as well as our new_scene object on line 41.

Because Lua does not memory manage our code for us, we need to release the two scene objects we created.

obs.obs_scene_release(new_scene)
obs.obs_scene_release(scene)

Finally, we need to return our new_scene, and our scene object from our function so that we can use them elsewhere, as you can see on line 50.

return new_scene, scene

The create_loop_overlay function

Now that we have our scene functions out of the way, let us get to something more interesting which is creating a function that will import our overlays for us.

Now an overlay, for our shared definition, can be anything from a 1920 x 1080 animated background, or a 480 x 360 Camera Overlay, or a 700 x 250 Support Bar. I mention the sizes because these can become important in determining your scale and position of your overlays later.

Since my designs are animated and looping, I want to tell OBS to do the following:

  • Create a new overlay object for me
  • Add this overlay to a specific scene
  • Make sure that the overlay loops inside OBS
  • Set the position and scale of my overlay.

We can do this by adding a new function called “create_loop_overlay()” under our create_scene function:

function create_loop_overlay(file_location, name, new_scene, scene, xpos, ypos, xscale, yscale)local pos = obs.vec2()
local scale = obs.vec2()
local overlay_settings = obs.obs_data_create()
obs.obs_data_set_string(overlay_settings, "local_file", script_path() .. file_location)
obs.obs_data_set_bool(overlay_settings, "looping", true)
local overlay_source = obs.obs_source_create("ffmpeg_source", name, overlay_settings, nil)
obs.obs_scene_add(new_scene, overlay_source)
local overlay_sceneitem = obs.obs_scene_find_source(scene, name)
local overlay_location = pos
local overlay_scale = scale
if overlay_sceneitem then
overlay_location.x = xpos
overlay_location.y = ypos
overlay_scale.x = xscale
overlay_scale.y = yscale
obs.obs_sceneitem_set_pos(overlay_sceneitem, overlay_location)
obs.obs_sceneitem_set_scale(overlay_sceneitem, overlay_scale)
end
obs.obs_source_update(overlay_source, overlay_settings)
obs.obs_data_release(overlay_settings)
obs.obs_source_release(overlay_source)
end

Now this might be a lot to take in but lets break it down one by one. Once we get passed how to create this, making texts and images will come easy. First let us go over the types of sources we create in OBS.

Here is a list of the types of sources we can create. Credit must given to upgradeQ over on Github for this awesome find.

Today we will only be covering how to make Media, Image, and Text (GDI+) sources. Since I design my animated stream overlays as .webm or .mp4 files, I will need to tell OBS that the type of source I wish to create is a “ffmpeg_source”, which you can see on line 62 in the example code above.

In our create_loop_overlay() function I have several parameters that I will pass into it:

  • file_location = the directory of my animated design
  • name = the custom chosen name I will give to this source
  • new_scene = our scene object we created in our create_scene function
  • scene = our 2nd scene object we created in our create_scene function
  • xpos = the x position of our overlay on our Stream preview window
  • ypos = the y position of our overlay on our Stream preview window
  • xscale = the x scale of our overlay
  • yscale = the y scale of our overlay

Inside OBS, whenever you add a source, there are settings attached to it that you can manipulate for your stream. OBS Studio calls these settings “Properties,” but for our sake in our code we will call them settings. Editing our settings in our code is the equivalent of editing the properties in the OBS Studio UI. To access the properties of a source you have added inside OBS, double click your source, or right click your source and then click Properties on the pop up menu.

To edit the scale and transform settings of our source we can highlight and press Ctrl+E on the keyboard, or right click and go to Transform > Edit Transform Option on the pop up menu.

Essentially to create our ffmpeg_source with our function we need to do the following:

  • Create a data object that will represent the settings of our new overlay source.
  • Define certain settings for our new source using OBS Lua libraries data setting tools
  • Create the new source with our specific settings
  • Add the new source to the scene we want to add it to
  • Find our new source that was created in our scene and edit its position and scale

Now let us go over the function’s inner workings:

We first create a pos and scale variable based on the obs library’s vec2 method. We will use this to define our position and scale later.

local pos = obs.vec2()
local scale = obs.vec2()

To create the data object for our settings of our source we call obs_data_create() on line 59

local overlay_settings = obs.obs_data_create()

Then in order to tell OBS the location of our overlay file on our computer, we set the string key option “local_file” to a file path.

obs.obs_data_set_string(overlay_settings, "local_file", script_path() .. file_location)

As you can see, obs_data_set_string takes in three inputs:

  • A data object = in this case overlay_settings
  • A string = in this case we want to define the setting for “local_file” for our source
  • A path = Here we pass script_path(). script_path() is useful within the OBS Lua library in returning the path directory of the current location of our script on our computer, which I use here to help point the script to neighboring file in the same directory/folder of the script. script_path() is then appended by our parameter “file_location” using the two periods “. .”

Next, we want to tell OBS that we want our animated overlay to loop. So we set this by using obs_data_set_bool.

obs.obs_data_set_bool(overlay_settings, “looping”, true)

Now we create the actual source object. Before I continue, let me make clear that inside OBS Studio, a Scene can also be a Source. The source we are creating now can also be considered a SceneItem and a Source (confusing no?)

A sceneitem is different than a scene because a sceneitem is a source located within a Scene. But a source inside a scene and the scene itself are both considered sources as well.

local overlay_source = obs.obs_source_create("ffmpeg_source", name, overlay_settings, nil)

Above, you can see that we pass the type of source we wish to create which is “ffmpeg_source” as the first parameter for obs_source_create, we then pass the name parameter, then the overlay_settings data object, and then finally nil because we have no need for using hotkeys with our source.

Now that we created our source we need to explicitly tell OBS that we want to actually add it to our scene. So we do obs_scene_add.

obs.obs_scene_add(new_scene, overlay_source)

obs_scene_add takes a scene object, in this case the scene we created “new_scene”, and then the new source object which is “overlay_source.”

Now that we told OBS to actually add our new ffmpeg_source overlay to our current scene, we want to then set the position and the scale of our overlay.

The position is based on pixels on x and y coordinates, with 0,0 starting from the top left of the OBS Preview window.

Scale is based on a scalar value of your original overlay size, 1 being 100% of the actual size of your object, 0.5 being half of your object’s size, and 0.25 being a quarter, and so on.

To set the position and scale, we need to go back to the concept of sceneitems. Since we already created the source, we need to now reference the actual source inside our scene as a sceneitem, otherwise we will not be able to edit the position or scale of our object. So we first define our sceneitem:

local overlay_sceneitem = obs.obs_scene_find_source(scene,name)

You might have noticed that we pass into the obs_scene_find_source method two things which is the actual scene object, and the name that we gave our overlay object.

OBS finds sceneitems based on its given name.

So if we create a source that will be our “background” for our “Welcome” scene and call it “background”, when we need to define its position and scale, we need to find it based on its name “background.”

(I could go into referenced source objects (basically duplicates of a given source) but I will keep that for another day.)

After defining our scene item we then define variables that will represent the position and scale of our overlay. Remember that we created a pos and scale vec2 objects earlier at the start of our create_loop_overlay function.

local overlay_location = pos
local overlay_scale = scale

Now as a safety measure, we make sure that we actually have a sceneitem object, and if we do, we set our x and y scale and position to the xpos, ypos, xscale, and yscale parameters that we will pass into our create_loop_overlay function. You can see this in line 68 to 75.

if overlay_sceneitem then
overlay_location.x = xpos
overlay_location.y = ypos
overlay_scale.x = xscale
overlay_scale.y = yscale
obs.obs_sceneitem_set_pos(overlay_sceneitem, overlay_location)
obs.obs_sceneitem_set_scale(overlay_sceneitem, overlay_scale)
end

And then finally we update the settings of our object, and release our data and source objects in line 77 and 79.

obs.obs_source_update(overlay_source, overlay_settings)
obs.obs_data_release(overlay_settings)
obs.obs_source_release(overlay_source)

Now with the hard part out of the way, we should have an easier time going through the rest of the code.

The create_image function

We will now make a create_image function that will do something very similar to our create_loop_overlay function. The only difference is that since an image is static, we will not have a need to define a looping setting for our image. Everything else pretty much stays the same. We will add this function after our create_loop_overlay function.

function create_image(file_location, name, new_scene, scene, xpos, ypos, xscale, yscale)local pos = obs.vec2()
local scale = obs.vec2()
local image_settings = obs.obs_data_create()
obs.obs_data_set_string(image_settings, "file", script_path() .. file_location)
local image_source = obs.obs_source_create("image_source", name, image_settings, nil)
obs.obs_scene_add(new_scene, image_source)
local image_sceneitem = obs.obs_scene_find_source(scene, name)
local image_location = pos
local image_scale = scale
if image_sceneitem then
image_location.x = xpos
image_location.y = ypos
image_scale.x = xscale
image_scale.y = yscale
obs.obs_sceneitem_set_pos(image_sceneitem, image_location)
obs.obs_sceneitem_set_scale(image_sceneitem, image_scale)
end
obs.obs_source_update(image_source, image_settings)
obs.obs_data_release(image_settings)
obs.obs_source_release(image_source)
end

With images out of the way let’s jump right into creating texts.

The create_text function

A lot of what we did for creating images and looping overlays will be incorporated into how we create texts. The difference with creating texts is that there are a lot of data options available to us when we create our data object for our settings. I will explain myself as we go. For now let us now add under our create_image function our create_text function.

function create_text(face, size, style, text, align, name, new_scene, scene, x, y)local pos = obs.vec2()
local scale = obs.vec2()
local text_settings = obs.obs_data_create()
local text_settings = obs.obs_data_create()
local text_font_object = obs.obs_data_create_from_json('{}')
obs.obs_data_set_string(text_font_object, "face", face)
obs.obs_data_set_int(text_font_object, "flags", 0)
obs.obs_data_set_int(text_font_object, "size", size)
obs.obs_data_set_string(text_font_object, "style", style)
obs.obs_data_set_obj(text_settings, "font", text_font_object)
obs.obs_data_set_string(text_settings, "text", text)
obs.obs_data_set_string(text_settings, "align", align)
local text_source = obs.obs_source_create("text_gdiplus", name, text_settings, nil)
obs.obs_scene_add(new_scene, text_source)
local text_sceneitem = obs.obs_scene_find_source(scene, name)
local text_location = pos
if text_sceneitem then
text_location.x = x
text_location.y = y
obs.obs_sceneitem_set_pos(text_sceneitem, text_location)
end
obs.obs_source_update(text_source, text_settings)
obs.obs_data_release(text_settings)
obs.obs_data_release(text_font_object)
obs.obs_source_release(text_source)
end

Everything stays the same except lines 118 through 125 in the image above. The key differences is that we create a new object called text_font_object that is made from the method “obs_data_create_from_json.”

local text_font_object = obs.obs_data_create_from_json('{}')

Key-Value Pairs in OBS Studio Properties

If you are wondering why this json method is here, it is because text inside OBS studio is interesting to play with. How OBS stores the style, size, and font (or face) of text is through the use of a key-array value inside of the settings array. For those who have experience with databases or json, you will understand what I mean, but for those who don’t, do not worry as I will try to make more sense of this.

Before we would only create the settings object for our overlay, as shown in the code below

local overlay_settings = obs.obs_data_create()

Essentially what we were really doing with the overlay_settings data object was creating an array of our pre-defined options that we fed to OBS. OBS then took these options as the value for each of the various keys within the “settings” key for our overlay properties. So what happens is that we are actually defining the “local_file” and the “looping” key when we add our looping overlays.

So since the text style, size, and face are stored as one single value, we use the obs_data_create_from_json(‘{}’) to create a value array for us. We store our syle, size, and face inside this array that we will then use to set the value for the font key.

local text_font_object = obs.obs_data_create_from_json('{}')
obs.obs_data_set_string(text_font_object, "face", face)
obs.obs_data_set_int(text_font_object, "flags", 0)
obs.obs_data_set_int(text_font_object, "size", size)
obs.obs_data_set_string(text_font_object, "style", style)
obs.obs_data_set_obj(text_settings, "font", text_font_object)
obs.obs_data_set_string(text_settings, "text", text)
obs.obs_data_set_string(text_settings, "align", align)

As you can see, face, flags, size, and style all are set by passing in the text_font_object for obs_data_set_string and obs_data_set_int in lines 119 through 122.

Then since value array is treated as a whole object, we then use obs.obs_data_set_obj on line 123 to define the font key for our text_settings data object.

Then we continue through lines 124 to 125 by setting the actual text we want to display on OBS Studio, and the alignment we want to define for our text.

We then finish up by creating the text source on line 126, and then on line 127 we add the text to our scene. Everything from setting from the position to the scale is now the same.

The only difference here when we finish is that we have to make sure to also release our json data object that we used to define our font key earlier.

obs.obs_source_update(text_source, text_settings) obs.obs_data_release(text_settings)
obs.obs_data_release(text_font_object)
obs.obs_source_release(text_source)

Most of the hard work out of the way

Most of the hard work has now been completed. Let us get to actually writing down code that uses our new create_loop_overlay, create_image, and create_text functions. These will be our work horses for the remainder of our code.

Let’s rename our button function to something more cohesive to our tutorial. I am going to make two functions, so I will rename the button function to “create_welcome_scene()” and then create a second function underneath the create_welcome_scene function as “create_starting_soon_scene(), like so:

function create_welcome_scene()endfunction create_starting_soon_scene()end

Now we need some content. For this, I am going to use two looping overlays I created, one in .mp4 format, the other in .webm, and an icon I created in .png format. The text I will create within OBS with our code. Feel free to use any images or videos you have to follow along with this tutorial.

Remember that we defined the file paths in relation to the location of our script by using script_path(). I have my resources located in two folders, one called “Screens” and another “Icons” on my desktop, with my Script on the desktop as well.

Let us start filling up our create_welcome_scene function. OBS by default adds new sources to a scene one after the other, so the order of how you use your functions will matter if you want a certain element to appear over the other. I will start by using our create_scene function and by giving it a name by creating a string variable of scene_name with “Welcome”. And then I will call the create_loop_overlay function and pass in the path of my first looping background file, along with the other necessary parameters for the function.

function create_welcome_scene() scene_name = "Welcome"
new_scene, scene = create_scene(scene_name)
create_looping_background("Screens/1920x1080x60_Blank_Motion_Screen.mp4", scene_name .. " BG", new_scene, scene, 0, 0, 1, 1)end

Remember that our create_scene function returns two things which were the new_scene object, and our scene object, which is what we will need to pass into our create_loop_overlay. Also, our create_loop overlay will require the following:

  • the file path = in my case “Screens/1920x1080x60_Blank_Motion_Screen.mp4”
  • the name of your scene = in this case what I passed was the string variable “scene_name” appended by a space and the text “BG” to represent background
  • the new_scene object
  • the scene object
  • the x position = since I know my main looping background is 1920 by 1080 and my stream is set to the same resolution, I will leave the x position and y position to 0, 0
  • y position = 0
  • X Scale = Again since the scale is represented in a scalar value of 0 to 1, 1 being 100% of my original file size, I will leave the X Scale to 1
  • Y Scale = 1

Let us make sure our script is working. Down in our properties function, let us change up the button object we created. Here is how yours should look now if you have not changed it:

I am going to rename the button object, as well as change the call back function to the create_welcome_scene function, like so:

function script_properties()local properties = obs.obs_properties_create()local welcome_button = obs.obs_properties_add_button(properties, "welcome_button", "Import Welcome Scene", create_welcome_scene)

return properties
end

Let us go into OBS Studio and refresh our Script and test that our button works.

Great! Now we know that our create_scene functions works properly, as well as the create_loop_overlay function works as it should too. You can also click multiple times on your button to see that it creates new scenes with an appended number at the end of the scene name.

Now let us add text to our welcome scene. Let us call the create_text function. I will use a custom font installed on my machine but feel free to use any font you like on your computer with the settings you like, as well as your size, and style.

Remember that our create_text() function takes in the following parameters:

  • Face (or font) = in my case “Great Sejagad”
  • Size = 156
  • Style = “Regular”
  • Text = “Welcome to RetroWave”
  • Align = “left”
  • Name = “Welcome Text”
  • New Scene Object
  • Scene Object
  • X position
  • Y position
create_text("Great Sejagad", 256, "Regular", "Welcome to RetroWave", "left", "Welcome Text", new_scene, scene, 156, 409)

And then let us check that it is working by going back into OBS Studio and Clicking our “Import Welcome Scene” Button.

Everything is working great! Let us now add some content to our second scene which will be made with our “create_starting_soon_scene” function. I will add the same background again, but this time instead of text, I will add another looping overlay, as well as my icon .png image. Also I make sure that I add a new button for my properties settings object in order to be able to add my new starting soon scene. Also, make sure that you change the scene_name variable to “Starting Soon” to match the scene’s context: It should look like this:

function create_starting_soon_scene()scene_name = "Starting Soon"
new_scene, scene = create_scene(scene_name)
create_loop_overlay("Screens/1920x1080x60_Blank_Motion_Screen.mp4", scene_name .. " BG", new_scene, scene, 0, 0, 1, 1)create_loop_overlay("Screens/1920x1080x60_Stream_Starting_Soon.webm", scene_name .. " Soon Animation", new_scene, scene, 0, 0, 1, 1)create_image("Icons/50x50_Facebook_Icon.png", scene_name .. "Facebook Icon", new_scene, scene, 960, 800, 1.5, 1.5)endfunction script_properties()local properties = obs.obs_properties_create()local welcome_button = obs.obs_properties_add_button(properties, "welcome_button", "Import Welcome Scene", create_welcome_scene)local soon_button = obs.obs_properties_add_button(properties, "soon_button", "Import Starting Soon Scene", create_starting_soon_scene)

return properties
end

As you can see above on line 164, for the create_image function, I change the x and y position to somewhere on the lower part of my preview in OBS, as well as increase the scale by half since my icon is a very small image. Also, since I have a new loop overlay on line 162, I make sure to give this overlay a new name that makes it unique from my main background.

We can now test our buttons:

Our script works!

Starting to finish up: Let’s add one more function, the Create All Scenes Function

My kind and patient readers

You probably by now are fed up with this really long and detailed tutorial. Please bare with me a little more since we are almost closing up.

We will simply add two more things, which is an import_all_scenes function, and a new button for the user to be able to import all the available scenes in the script. It should look like this:

function import_all_scenes()create_welcome_scene()
create_starting_soon_scene()
local scene_names = obs.obs_frontend_get_scene_names()
local scene_list = obs.obs_frontend_get_scenes()

for i, name in ipairs(scene_names) do
if scene_names[i] == "Welcome" then
scene = scene_list[i]
obs.obs_frontend_set_current_scene(scene)
break
end
end

for i, scene in ipairs(scene_list) do
obs.obs_source_release(scene)
end
endfunction script_properties()local properties = obs.obs_properties_create()local import_all_button = obs.obs_properties_add_button(properties, "import_all_button", "Import All Scenes", import_all_scenes)local welcome_button = obs.obs_properties_add_button(properties, "welcome_button", "Import Welcome Scene", create_welcome_scene)local soon_button = obs.obs_properties_add_button(properties, "soon_button", "Import Starting Soon Scene", create_starting_soon_scene)

return properties
end

What the import_all_scenes function does is:

  • It calls the create_welcome_scene and the create_starting_soon_scene functions
  • It then creates two lists called scene_names, and scene_list. We use scene_names to find the “Welcome” scene by its name and index, and then we use the index value i to find the actual source object for the welcome scene in the scene_list. We then set the current scene to display in OBS Studio by calling on line 179 “obs.obs_frontend_set_current_scene()” and passing the source object for the Welcome Scene into it
  • Our two first functions (create_welcome_scene, and create_starting_soon_scene) that were called released our scene and source objects for us, but we will need to now release all the source objects created when we created the “scene_list” object. So we use a for loop that iterates through the scene_list and releases each source object

And below for our properties settings object, we went ahead and added a new button to allow us to import all our scenes.

Let’s test that it works…

And with that we have finally got to completing our script! With your new knowledge you can now incorporate your script with your Twitch streaming designs or packages that you offer or make for fellow streamers. There are more possibilities to what I showed here today, so definitely explore and take advantage of this growing resource. Just some key points to take away:

  • Remember to keep your files organized and near to your script to find them easily
  • Remember to always release your objects or sources because OBS Studio can crash if you don’t manage your memory properly
  • You can always export your Scene Collection under the Scene Collection menu inside OBS Studio and view the settings you created in .json format, as a way to understand what you did

Recap: What We Learned

Thanks for sticking by to the end! I understand that this might have been a pretty long tutorial but I hope you were able to learn a few things today.

  • Today we covered how to make easy to use function that can creat and import our overlays, images, and texts for us inside OBS Studio
  • We also went over some general concepts of how OBS Studio uses key-value pairs to define certain settings for different types of sources
  • I also went into some explanations of the OBS Lua Library and what sources, scenes, and sceneitems are

If you are interested in learning more about scripting with Lua or Python with OBS Studio, please check out the OBS Getting Started Wiki here:

Also, for more gritty detail of the OBS Lua library, check out the OBS Documentation here:

--

--