Tips and tricks for Lua scripts


New Member
Hey @John_ , sorry for the delay, I was on a different project the past 3 weeks. Yes, in fact there is such a lot to do with these typemaps! Help is welcome!

First of all, obviously, is to have a compiling environment. I'm doing everything in Windows, with CMake, VSCode as IDE, Visual Studio Community 2019 only as compiler (my settings of CMake in VS2019 never worked properly), plus the QT and SWIG dependencies delivered as archives.

Second is to understand the concept of "typemap" by SWIG: typemaps in general of different kinds, then for Python and for Lua.

It is a lot of documentation! And it is very challenging to understand because it is about code generation in a mix of C, Python/Lua plus the typemap definition syntax.

My very basic understanding is that:
  1. SWIG reads OBS header files and generates code, as defined in the files obspython.i and obslua.i, where there is a list of included files and the definition of the typemaps
  2. For each C function encountered in some header file, SWIG will generate one C wrapper function that binds the original C function to the scripting language
  3. The purpose of the C wrapper function is to implement the conversions from/to Python/Lua, i.e. it will first check the Python/Lua parameters consistency, then convert to C structures, then call the original C function, then converts the C-encoded data back to Python/Lua and finally return the data if necessary.
  4. SWIG generates such C wrapper functions by assembling C code defined in the typemaps at the different stages of the C wrapper function. This is why there are several kinds of typemaps (in, typecheck, out, arginit...), e.g. typemap(in, .....) is used to convert function arguments from the target language to C
  5. There is a bunch of pre-defined fields that you can use in a typemap, which are expanded with function-specific values during code generation, e.g. $1will be replaced by the name of the variable used by SWIG as argument when calling the original C function, $inputis the Python/Lua object passed by the scripting language.
  6. The way typemaps are applied can be controlled. In general a typemap can be applied to all arguments of all functions, or can be limited to the arguments with a particular name, and a particular C function signature can be re-written to force the use of particular typemaps (this is what I did with the function gs_stagesurface_map above)
  7. Typemaps can be applied on a sequence of arguments too
  8. SWIG generates as well C wrapper functions as getters and setters for members of a struct
  9. There are tons of additional definitions in SWIG.

Finally, with that in mind (it is a lot to learn at the beginning, but once the basic typemap concept is understood, the rest seems logical), tweaking OBS bindings is done in just a few lines of code in the .i files.

For some reason, the typemap feature was never used in OBS and for example the frontend functions, which need non-classical type conversions, were written manually out of the SWIG scope. As a result, these functions do not appear in the file (and do not appear if you are trying to use auto-completion).

At the end, the remaining work is to:
  • Identify the functions that can be improved with typemaps (see my previous posts), due to memory leaks or functions that are just not useable currently
  • Find a good way to improve with a typemap, that ideally does not break any existing script
My implementation of the bindings for gs_stagesurface_map works somehow, but is not free of memory leaks. Anyway the use is limited because the transfer of data back to RAM can be slow depending on the graphic card.

I would better look at the bindings for accessing the buffer of a texture_data member of the gs_image_file structure. In my opinion this is really a missing feature in OBS scripting.


Hi, I am really new to LUA, I would like to add a checkbox to enable or disable the reading of a file and a reset button to remove the selected path. I have been unable to do so. How should I do it?

An example of how it should look:


Thanks in advance. Excellent work! ;)


New Member
Hello, I would suggest the Source Shake tutorial for an introduction to scripting and an example of editable properties.

It looks like this in the tutorial:
-- Called to display the properties GUI
function script_properties()
  props = obs.obs_properties_create()
  obs.obs_properties_add_text(props, "source_name", "Source name", obs.OBS_TEXT_DEFAULT)
  obs.obs_properties_add_float_slider(props, "frequency", "Shake frequency", 0.1, 20, 0.1)
  obs.obs_properties_add_int_slider(props, "amplitude", "Shake amplitude", 0, 90, 1)
  return props

For a checkbox you will need obs.obs_properties_add_bool, and for a button obs.obs_properties_add_button.

There is an example of a button with a callback function in the same tutorial.


I did post about LuaJIT FFI usage in the past Turns out it is not thread safe. The volmeter thread callback, for some reason, seg faults on GNU/Linux and that also true for Microsoft Windows. Is there a way to construct a C thread callback which calls Lua often and not crashes OBS Studio ? It just works on Python.


New Member
I would really like to see a script example for creating a dock window to put UI elements into. Some scripts really need their own window, rather than having to display next to the list of scripts. Would be nice to be able to create a dock window, put buttons and other UI controls in there so the user can put the UI for the script where they want.


New Member
Thank you all for these write ups they are super helpful! I do have a question though, how would I go about moving a source by 10 pixels. Essentially I would like to press a hotkey and it moves a source 10 pixels to the left.
Thank you all for these write ups they are super helpful! I do have a question though, how would I go about moving a source by 10 pixels. Essentially I would like to press a hotkey and it moves a source 10 pixels to the left.
The Source Shake tutorial explains a lot of the overall structure you would need. But instead of calling obs_sceneitem_set_rot to rotate a source, you would call obs_sceneitem_get_pos to get the current position, tweak it by 10, then call obs_sceneitem_set_pos


New Member
Hello, I started working on an import overlay script. I wanted to know if is possible and how it's done. How can I create a function to create a nested scene, look through the scenes I have, and import that X scene to the Y scene. Thank you! bfxdev


New Member
I came up with this code to create a nested scene source inside another source

function create_scene_source(source, name, new_scene, scene, xpos, ypos, xscale, yscale)
local pos = obs.vec2()
local scale = obs.vec2()
local sc_settings = obs.obs_data_create()
obs.obs_data_set_string(sc_settings, "add_existing", source)
local sc_source = obs.obs_source_create("scene", "(" .. source .. ")", sc_settings, nil)
obs.obs_scene_add(new_scene, sc_source)
local sc_sceneitem = obs.obs_scene_find_source(scene, name)
local sc_location = pos
local sc_scale = scale
if sc_sceneitem then
sc_location.x = xpos
sc_location.y = ypos
sc_scale.x = xscale
sc_scale.y = yscale
obs.obs_sceneitem_set_pos(sc_sceneitem, sc_location)
obs.obs_sceneitem_set_scale(sc_sceneitem, sc_scale)
obs.obs_source_update(sc_source, sc_settings)

This creates scene item but it doesn't pick another scene it creates a new one with this name. If someone could help me thank you!