Awesome Open Source
Awesome Open Source

OBS Studio Python Scripting Cheatsheet

  • obspython OBS Studio API.Obs scripts examples in /src
  • Each obs script example mostly will operate on existsing text source
  • It is possible to duplicate scripts and re-add them to OBS ( names must be different)

Table of content

Using classes

class Example:
    def __init__(self,source_name=None):
        self.source_name = source_name

    def update_text(self):
        source = obs.obs_get_source_by_name(self.source_name)
        if source is not None:
            data = str(next(datacycle))
            settings = obs.obs_data_create()
            obs.obs_data_set_string(settings, "text", data)
            obs.obs_source_update(source, settings)

Full example

Or more compact:

class _G:
    source_name = ''
    data = None
    flag = False

G = _G()

def script_update(settings):
    G.source_name = ...
    if G.flag:

with statement

Automatically release .

def source_auto_release(source_name):
    source = obs.obs_get_source_by_name(source_name)
        yield source 
# usage
with source_auto_release(self.source_name) as source:
    if source is not None:
        data = str(next(datacycle))
        with data_auto_release() as settings:
            obs.obs_data_set_string(settings, "text", data)
            obs.obs_source_update(source, settings)

Full example
See also :

Passing arguments to callbacks

from functools import partial
flag = obs.obs_data_get_bool(settings,"_obs_bool")
eg.update_text = partial(eg.update_text,flag_func=flag)

Full example


obs.obs_properties_add_button(props, "button1", "Refresh1:",callback) img
obs.obs_properties_add_bool(props,"_bool","_bool:") img
obs.obs_properties_add_int(props,"_int","_int:",1,100,1) img
obs.obs_properties_add_int_slider(props,"_slider","_slider:",1,100,1) img
obs.obs_properties_add_text(props, "_text", "_text:", obs.OBS_TEXT_DEFAULT) img
obs.obs_properties_add_color(props,"_color","_color:") img
obs.obs_properties_add_font(props,"_font","_font:") img
obs.obs_properties_add_font(props,"_font","_font:") img
bool_p = obs.obs_properties_add_bool(props, "_obs_bool", "Yes/No"); obs.obs_property_set_long_description(bool_p, "Check if yes,else uncheck") img

See also :

Property modification

def callback(props, prop, *args, **kwargs):  # pass settings implicitly
    p = obs.obs_properties_get(props, "button")
    n = next(counter)
    obs.obs_property_set_description(p, f"refresh pressed {n} times")
    return True
def script_properties():
    props = obs.obs_properties_create()
    b = obs.obs_properties_add_button(
        props, "button", "refresh pressed 0 times", refresh_pressed
    obs.obs_property_set_modified_callback(b, callback)
    return props

Full example
See also :

Additional input

def callback(props, prop, settings):
    _number = obs.obs_data_get_int(settings, "_int")
    _text_value = obs.obs_data_get_string(settings, "_text")
    text_property = obs.obs_properties_get(props, "_text")
    if _number > 50: = _text_value + str(_number)
        obs.obs_property_set_visible(text_property, True)
        return True
    else: = ""
        obs.obs_property_set_visible(text_property, False)
        return True

def script_properties():  # ui

    number = obs.obs_properties_add_int(props, "_int", "Number", 1, 100, 1)
    text_value = obs.obs_properties_add_text(
        props, "_text", "Additional input:", obs.OBS_TEXT_DEFAULT
    obs.obs_property_set_visible(text_value, False)
    obs.obs_property_set_modified_callback(number, callback)

Full example

Note: properties share similar structure , in Python, Lua, C. Example C See also :


  • obs_data_get_string
  • obs_data_get_int
  • obs_data_get_double
  • obs_data_get_bool
  • obs_data_get_obj
  • obs_data_get_array

Save settings as json

p = Path(__file__).absolute()  # current script path
file = p.parent / "saved_settings.json"
    content = obs.obs_data_get_json(Data._settings_)
    with open(file, "w") as f:
except Exception as e:
    print(e, "cannot write to file")

Full example
See also :

Source's and filters with identifier string

To identify with obs_source_get_unversioned_id , or creating source/filter.


Name Source type identifier string
Browser browser_source
Color Source color_source
Display Capture monitor_capture
Game Capture game_capture
Image image_source
Image Slide Show slideshow
Media Source ffmpeg_source
Text (GDI+) text_gdiplus
Window Capture window_capture


Name Source type identifier string
Compressor compressor_filter
Expander expander_filter
Gain gain_filter
Invert Polarity invert_polarity_filter
Limiter limiter_filter
Noise Gate noise_gate_filter
Noise Suppression noise_suppress_filter
VST 2.x Plug-in vst_filter
Video Delay (Async) async_delay_filter
Chroma Key chroma_key_filter
Color Correction color_filter
Color Key color_key_filter
Crop/Pad crop_filter
Image Mask/Blend mask_filter
Luma Key luma_key_filter
Render Delay gpu_delay
Scaling/Aspect Ratio scale_filter
Scroll scroll_filter
Sharpen sharpness_filter

Add source

Create source and add it to current scene

obs.obs_data_set_string(settings, "text", "The quick brown fox jumps over the lazy dog")
source = obs.obs_source_create_private("text_gdiplus", "test_py", settings)
obs.obs_scene_add(scene, source)

Full example
See also :

Move source

Get current scene , get source name, move source to location

def __init__(self):
    pos = obs.vec2()
    self.location = pos
def move_text_source(self):
    current_scene = obs.obs_frontend_get_current_scene()
    source = obs.obs_get_source_by_name("test_py")
    scene = obs.obs_scene_from_source(current_scene)
    scene_item = obs.obs_scene_find_source(scene, "test_py")
    if scene_item:
        dx, dy = 10, 10
        print("old values", self.location.x)
            scene_item, self.location
        )  # update to last position if its changed from OBS
        self.location.x += dx
        self.location.y += dy
        print("new values", self.location.x)
        obs.obs_sceneitem_set_pos(scene_item, self.location)

Full example

Add filter to source

Filters are sources,they are not listed in obspython module, you need to know its id from obs_source_info

obs.obs_data_set_int(settings, "opacity", 50)
source_color = obs.obs_source_create_private(
    "color_filter", "opacity to 50", settings
obs.obs_source_filter_add(source, source_color)

Full example
See also :
Color correction source

Toggle sceneitem visibility

def toggle(self):
    current_scene = obs.obs_scene_from_source(obs.obs_frontend_get_current_scene())
    scene_item = obs.obs_scene_find_source(current_scene, self.source_name)
    boolean = not obs.obs_sceneitem_visible(scene_item)
    obs.obs_sceneitem_set_visible(scene_item, boolean)

Full example

Set current scene

def set_current_scene(self):
    scenes = obs.obs_frontend_get_scenes()
    for scene in scenes:
        name = obs.obs_source_get_name(scene)
        if name == self.scene_name:
scenes = obs.obs_frontend_get_scenes() # Dropdown menu UI
for scene in scenes:
    name = obs.obs_source_get_name(scene)
    obs.obs_property_list_add_string(p, name, name) 

Full example

Get set order in scene

def get_order(scene_items=None):
    order = list()
    for i, s in enumerate(scene_items):
        source = obs.obs_sceneitem_get_source(s)
        name = obs.obs_source_get_name(source)
        order.append({"index": i, "name": name, "scene_item": s})
    return order

def reorder():
    current_scene = obs.obs_frontend_get_current_scene()
    with scene_ar(current_scene) as scene:
        with scene_enum(scene) as scene_items:
            order = get_order(scene_items)
            # change second index with pre last
            order[1]["index"], order[-2]["index"] = (
            for s in sorted(order, key=lambda i: i["index"]):
                obs.obs_sceneitem_set_order_position(s["scene_item"], s["index"])

Full example

Add scene with sources to current scene

def add_random_text_source(scene):
    r = " random text # " + str(randint(0, 10))
    with data_ar() as settings:
        obs.obs_data_set_string(settings, "text", f"random text value {r}")
        with source_create_ar("text_ft2_source", f"random text{r}", settings) as source:
            pos = obs.vec2()
            pos.x = randint(0, 1920)
            pos.y = randint(0, 1080)
            scene_item = obs.obs_scene_add(scene, source)
            obs.obs_sceneitem_set_pos(scene_item, pos)

def add_scene_with_sources():
    current_scene_source = obs.obs_frontend_get_current_scene()
    with scene_from_source_ar(current_scene_source) as scene_source:
        with scene_create_ar("_nested_scene") as _scene:
            py_scene_source = obs.obs_scene_get_source(_scene)

            with scene_from_source_ar(py_scene_source) as scene:

            # add created scene to current scene ( nested scene)
            _scene_source = obs.obs_scene_get_source(scene)
            obs.obs_scene_add(scene_source, _scene_source)

Note: sometimes OBS crashes if one of such scenes has been deleted.


def on_event(event):
        raise Exception("Triggered when the current scene has changed.")

def script_load(settings):

Full example
See also:

Program state

Those functions return true or false :

  • obs.obs_frontend_preview_program_mode_active()
  • obs.obs_frontend_replay_buffer_active()
  • obs.obs_frontend_recording_active()
  • obs.obs_frontend_recording_paused()
  • obs.obs_frontend_streaming_active()


Signals , callbacks , differences from C

Core signals

sh = obs.obs_get_signal_handler()
def callback(calldata):
    source = obs.calldata_source(cd,"source")

source_create, source_destroy, source_remove, source_save, source_load, source_activate, source_deactivate, source_show, source_hide, source_rename, source_volume, source_transition_start, source_transition_video_stop, source_transition_stop, channel_change, master_volume, hotkey_layout_change, hotkey_register, hotkey_unregister, hotkey_bindings_changed

Scene signals

def connect_cur_scene():
    source = obs.obs_frontend_get_current_scene()
    sh = obs.obs_source_get_signal_handler(source)
    obs.signal_handler_connect(sh, "item_add", callback)

def callback(calldata):
    scene_item = obs.calldata_sceneitem(calldata, "item")
    #scene = obs.calldata_source(cd,"scene") # bad utf symbols 
    scene =  obs.obs_sceneitem_get_scene(scene_item)
    name = obs.obs_source_get_name
    source = obs.obs_sceneitem_get_source
    scene_source = obs.obs_scene_get_source
    scene_name = name(scene_source(scene))
    scene_item_name = name(source(scene_item))
    print(f"item {scene_item_name} has been added to scene {scene_name}")

item_add, item_remove, reorder, refresh, item_visible, item_locked, item_select, item_deselect, item_transform

Source signals

sh = obs.obs_source_get_signal_handler(some_source)
def callback(calldata):
    source = obs.calldata_source(cd,"source")
    print("on source show",obs.obs_source_get_name(source))

destroy, remove, save, load, activate, deactivate, show, hide, mute, push_to_mute_changed, push_to_mute_delay, push_to_talk_changed, push_to_talk_delay, enable, rename, volume, update_properties, update_flags, audio_sync, audio_mixers, filter_add, filter_remove, reorder_filters, transition_start, transition_video_stop, transition_stop, media_started, media_ended, media_pause, media_play, media_restart, media_stopped, media_next, media_previous

Output signals

def connect_to_rec():
    sh = obs.obs_output_get_signal_handler(obs.obs_frontend_get_recording_output())
    obs.signal_handler_connect(sh, "pause", callback)

def callback(calldata):
    #out = obs.calldata_ptr(calldata, "output") # bad type
    print('output paused')

start, stop, pause, unpause, starting, stopping, activate, deactivate, reconnect, reconnect_success

Timing (sequential primitives)

def script_update(settings):
    eg.source_name = obs.obs_data_get_string(settings, "source")
    if eg.source_name != "":
        obs.timer_add(eg.update_text, 1 * 1000)

Full example
Note: each time script updated it's removed first
See also :
Version with globals and only one timer allowed.


def callback(pressed):
    if pressed:

def busy_thread():
    while True:
        if not data.thread_paused:
            data.status = "active"
            # print to stdoud crashes OBS on exit
            data.status = "inactive"

print('Press the "~" to toggle on/off')
hook("OBS_KEY_ASCIITILDE", "id_", callback)
obs.timer_add(lambda: print(data.status), 500)
t = threading.Thread(target=busy_thread)


This hotkey example will create hotkeys in settings , but you need to bind it manually.

class Hotkey:
    def __init__(self, callback, obs_settings, _id):
        self.obs_data = obs_settings
        self.hotkey_id = obs.OBS_INVALID_HOTKEY_ID
        self.hotkey_saved_key = None
        self.callback = callback
        self._id = _id



class h:
    htk_copy = None  # this attribute will hold instance of Hotkey

h1 = h()
h2 = h()
def script_load(settings):
    h1.htk_copy = Hotkey(cb1, settings, "h1_id")
    h2.htk_copy = Hotkey(cb2, settings, "h2_id")

def script_save(settings):

This hotkey example will create hotkeys on fly from json settings , but you need to know internal id.

ID = "htk_id"
JSON_DATA = '{"%s":[{"key":"OBS_KEY_1"}]}' % ID

def on_obs_key_1(pressed):
    if pressed:
        raise Exception("hotkey 1 pressed")

def script_load(settings):
    s = obs.obs_data_create_from_json(JSON_DATA)
    a = obs.obs_data_get_array(s, ID)
    h = obs.obs_hotkey_register_frontend(ID, ID, on_obs_key_1)
    obs.obs_hotkey_load(h, a)

Here is how send hotkey to OBS

def send_hotkey(obs_htk_id, key_modifiers=None):
    if key_modifiers:
        shift = key_modifiers.get("shift")
        control = key_modifiers.get("control")
        alt = key_modifiers.get("alt")
        command = key_modifiers.get("command")
    combo = obs.obs_key_combination()
    combo.modifiers = modifiers
    combo.key = obs.obs_key_from_name(obs_htk_id)
    obs.obs_hotkey_inject_event(combo, False)
    obs.obs_hotkey_inject_event(combo, True)
    obs.obs_hotkey_inject_event(combo, False)

See also:

Play sound

def play_sound():
    mediaSource = obs.obs_source_create_private(
        "ffmpeg_source", "Global Media Source", None
    s = obs.obs_data_create()
    obs.obs_data_set_string(s, "local_file", script_path() + "alert.mp3")
    obs.obs_source_update(mediaSource, s)

Read and write private data from scripts or plugins

Write in one script

def send_to_private_data(data_type, field, result):
    settings = obs.obs_data_create()
    set = getattr(obs, f"obs_data_set_{data_type}")
    set(settings, field, result)

def write_private_data():
    result = "private value from " + str(__file__) + " " + str(randint(1, 10))
    send_to_private_data("string", "__private__", result)

Read from another

def p_data_ar(data_type, field):
    settings = obs.obs_get_private_data()
    get = getattr(obs, f"obs_data_get_{data_type}")
        yield get(settings, field)

def print_private_data():
    with p_data_ar("string", "__private__") as value:

Lua is also supported

local obs = obslua
local settings = obs.obs_data_create()
obs.obs_data_set_int(settings,"__private__", 7)


There is no stdin therefore you can't use pdb , options are:

  • using print
  • using pycharm remote debugging (localhost)
  • using vscode attach to the process:
    • Load python extension
    • open script file , pip install debugpy , place debugpy.breakpoint() somewhere
      • Run (F5) select configuration ( Attach using Process ID)
      • select obs (on windows obs64.exe)
      • View select Debug Console (ctrl+shift+y)
  • Example debugpy obs


Docs and code examples

Generated contains all variables and functions available in obspython formatted with markdown. Table consist of links to appropriate search terms in OBS Studio repository, and obswebsocket,links to scripts in obspython and obslua with each script within github code search.gs_* and matrix_* functions exluded from that table.
Full example
Note : starting from 2020.12.17 Github Code Search no longer works as it was, see also this thread



Contributions are welcome!

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
python (50,968
api (1,618
example (239
timer (82
filters (54
obs-studio (26
obs (18
hotkey (17

Find Open Source By Browsing 7,000 Topics Across 59 Categories