Skip to content
Void Monster Docs

Type to search guides and API reference.

    OBS Utils/Developer docs

    API

    Legacy documentation

    OBS Utils exposes an API for registering custom overlay types and OBS Remote event types so other modules can extend it without forking.

    const api = game.modules.get('obs-utils').api;

    A typed reference is generated from the source — see the auto-generated API reference for the full surface. Treat anything not documented on this page as internal.

    All user-facing strings passed to the API (registerOverlayType’s readableName, registerOBSRemoteEventType’s name, condition-field labels) are resolved with game.i18n.localize() at render time. Pass an i18n key for translatable strings, or a literal string for a single-locale module.

    A custom overlay type combines a Svelte renderer with a set of components and, optionally, an editor for the Stream Composer.

    import MyOverlay from './MyOverlay.svelte';
    import MyOverlayEditor from './MyOverlayEditor.svelte';
    Hooks.once('obs-utils.init', () => {
    const api = game.modules.get('obs-utils').api;
    const myType = new api.OverlayType(MyOverlay);
    myType.registerOverlayEditor(MyOverlayEditor);
    myType.perActor = true; // renders once per selected actor (default)
    api.registerOverlayType('my-key', 'mymod.overlays.myType.name', myType);
    });

    The obs-utils.init hook fires once the API is attached but before the world is ready, so register types there to ensure they’re available when the editor opens.

    For overlays that render exactly once globally (not per-actor), register the Svelte class directly:

    api.registerUniqueOverlaySvelte5(MyGlobalOverlay);

    The class is mounted once into the .overlay-renderer subtree on /stream, the Full Preview, and the editor.

    Custom event types appear in the OBS Remote → Events dropdown alongside the built-ins. They can declare a schema of condition fields the editor renders as inputs.

    const api = game.modules.get('obs-utils').api;
    api.registerOBSRemoteEventType({
    key: 'mymod.onLowHP',
    name: 'mymod.events.lowHP.name', // localised at render time
    icon: 'fas fa-heart-crack',
    conditionFields: [
    {
    key: 'threshold',
    label: 'mymod.events.lowHP.threshold',
    type: 'number',
    default: 5,
    },
    {
    key: 'actorName',
    label: 'mymod.events.lowHP.actorName',
    type: 'string',
    default: '',
    },
    ],
    matcher: (conditions, context) => {
    // Called for each configured instance of this event type when the event
    // fires. Return true to run the instance's actions, false to skip.
    if (conditions.actorName && conditions.actorName !== context.actor?.name) return false;
    return context.hp <= conditions.threshold;
    },
    });

    To fire the event, call:

    api.triggerOBSRemoteEvent('mymod.onLowHP', { actor, hp });

    Only the OBS client actually applies the actions — triggerOBSRemoteEvent is a no-op on non-OBS pages. Pass any context object you need; the matcher receives it as the second argument.

    type Editor input
    'string' <input type="text">
    'number' <input type="number">
    'boolean' <input type="checkbox">

    Event types with at least one conditionFields entry let users configure multiple instances (each with its own conditions + actions). No-condition events collapse to a single implicit instance — the Events tab just shows the action list.

    The list below covers selected settings that are useful to read from third-party modules. It is not exhaustive.

    game.settings.get('obs-utils', 'streamOverlays'); // OverlayData[]
    game.settings.get('obs-utils', 'overlayActors'); // string[] of actor IDs
    game.settings.get('obs-utils', 'overlayActorsModified'); // boolean — true once the GM has manually edited the actor list
    game.settings.get('obs-utils', 'globalOverlayCSS'); // string
    game.settings.get('obs-utils', 'actorOverlayCSS'); // Record<actorId, css>
    game.settings.get('obs-utils', 'activeGMUserId'); // string (current active GM)
    game.settings.get('obs-utils', 'cameraTrackingMode'); // 'raw' | 'smooth' | 'dragRelease'
    game.settings.get('obs-utils', 'cameraSmoothing'); // number, 0–1500 ms
    game.settings.get('obs-utils', 'cameraEasing'); // string (easing function name)

    For reactive consumers inside obs-utils Svelte components, prefer settings.getStore(key) / settings.getReadableStore(key) — both expose Svelte stores that mirror the setting and propagate updates from socket-driven changes.

    Overlay triggers are named Foundry events that animation tracks can subscribe to — they drive track transitions without tight coupling between the firing module and the overlay system.

    api.registerOverlayTrigger({
    key: 'mymod.onSpellCast',
    name: 'mymod.triggers.spellCast.name',
    icon: 'fas fa-wand-sparkles',
    payloadSchema: [
    { key: 'actor', type: 'Actor', label: 'mymod.triggers.spellCast.actor', display: true, filter: true },
    { key: 'spellName', type: 'string', label: 'mymod.triggers.spellCast.spell', display: true, filter: true },
    ],
    });

    To fire the trigger from your module:

    api.fireOverlayTrigger('mymod.onSpellCast', { actor, spellName });

    fireOverlayTrigger dispatches via Hooks.callAll('obs-utils.overlayTrigger', key, payload) — any number of overlay renderers can react without direct coupling.

    Third-party modules can add tabs to the Director window:

    import MyTab from './MyTab.svelte';
    api.registerDirectorTab({
    key: 'mymod.weatherTab',
    label: 'mymod.director.weather.label',
    icon: 'fas fa-cloud',
    component: MyTab, // receives { disabled } prop
    order: 100, // built-ins use 10/20/30; module tabs default to 100
    });
    const state = api.getDirectorState(); // DirectorState snapshot

    Subscribe to changes via the obs-utils.director.stateChanged hook:

    Hooks.on('obs-utils.director.stateChanged', (next, prev) => {
    // next: DirectorState, prev: DirectorState | undefined
    });
    // Broadcast the preset — claims active GM, swaps tracking mode, broadcasts to all OBS clients.
    api.playPreset(preset); // void
    // Local-only preview (no broadcast, no state changes). Returns a SequenceController.
    const controller = api.previewPreset(preset);
    controller.pause();
    controller.resume();
    controller.scrub(timeMs);
    controller.stop();
    // Selected actors (the array of actor IDs the overlay tiling works from).
    api.getSelectedActors(); // string[] | undefined
    await api.setSelectedActors(['actorId1', ...]);
    // Flat AV data (last writer wins).
    api.setAVData(actorValueArray);
    // Grouped AV data — group labels are i18n keys, localised at flatten time.
    api.setAVDataGrouped(groups);

    System companion modules can register a starter overlay set that replaces the generic default in the overlay editor’s template menu:

    api.registerStarterOverlays(overlaysArray); // last writer wins
    // Build a canvas overlay that emulates the 4.x roll overlay from a legacy flat config.
    // Returns OverlayData; does not touch settings.
    const overlay = api.buildLegacyRollOverlayCanvas(legacyConfig);
    api.isOBS(); // boolean — true on OBS-mode pages
    // Returns the OBSWebSocket client if allowWebsocketAPI is enabled, otherwise undefined.
    api.getOBSWebsocketClient();

    An OverlayType instance wires together a renderer, component types, and optional editors.

    const myType = new api.OverlayType(MyOverlaySvelte);
    myType.perActor = true; // render once per selected actor (default: true)
    // Register a renderable component type.
    myType.registerComponent('my-key', 'mymod.components.foo.name', FooComponent);
    // Register an editor for a component type (shown in the Stream Composer).
    myType.registerComponentEditor('my-key', FooEditor);
    // Register image-slot handlers so the bundle walker can extract and rewrite image refs.
    myType.registerComponentImageSlots('my-key', {
    extract: (data) => [/* image paths from data */],
    rewrite: (data, pathMap) => /* new data string with paths replaced */,
    });
    // Register a custom editor UI for the overlay itself (replaces the default).
    myType.registerOverlayEditor(MyOverlayEditor);
    api.registerOverlayType('my-key', 'mymod.overlays.myType.name', myType);