This guide covers the event-consumption workflow. For the subscription API reference, see
Game Events; for the per-game event lists, see
Supported Games - e.g. Rainbow Six Siege and
Universal.
How Game Events Work
Four things to know before writing any code:- Events arrive per game module. You subscribe to a game’s events by passing its module in your
overlayed()config, and listen on the matching property of the overlay object -overlay.siegeforSiege.module(). Theuniversalmodule is always available asoverlay.universal, whether or not you pass it. - Nothing is emitted until you call
readyForGameEvents(). Each module queues incoming events (up to 1,000) from startup. The first call to that module’sreadyForGameEvents()replays the queue to your listeners - in arrival order, synchronously - and switches the module to live delivery for the rest of the app’s run. - Every event is validated and fully typed. Payloads are validated with Arktype before
your listeners see them. The object your callback receives is an envelope -
{ game, type, creation_time, content }- and the per-game reference tables document the shape ofcontent. - Listeners run in your main process. Game events never arrive in a renderer directly. Keep your canonical game state in the main process and forward snapshots to windows over IPC.
From Events to Game State
The examples use Rainbow Six Siege, but the pattern is identical for any module: reset onmatch_started, build a
roster from player_joined/player_left, and patch it from the *_changed events.
Register the game module
Pass the module for each game whose events you want. This also subscribes you to that game’s lifecycle events -
see Game Launch & Close Events for how
gameLaunch, gameReady, and
gameClose fit in.overlayed.ts
Build state from events
Keep one plain state object in the main process and treat each listener as a reducer. The event types are
exported from the module package, so you can derive your state types from them instead of hand-writing shapes:Note that the game data is always under
gameState.ts
event.content - the reference tables on the
per-game pages document that inner shape. The envelope also carries event.game and
event.type, which matter once one listener handles several games or event types.Flush the queue with readyForGameEvents()
Once all listeners are attached, tell the module you’re ready:Any events that arrived while your app was starting up are replayed to your listeners immediately, then new
events flow as they happen.
gameState.ts
Specific Events vs. Listening to Everything
Every module exposes bothon for a single event type and onAny for all of the module’s events. For the same
event, on listeners are invoked before onAny listeners.
- Specific events
- All events (onAny)
Use
on when each event has its own handling. The callback is typed to that event, so event.content is
fully typed with no narrowing needed:overlay.siege.off(eventType, callback) and overlay.siege.offAny(callback) - which
take the same callback reference you registered. You can attach multiple listeners to the same event.
Getting State to Your Windows
Since events land in the main process, your windows never see them directly. Forward a snapshot over IPC whenever state changes - bothcreateWindow and createInGameWindow return (a wrapper around) an Electron BrowserWindow,
so the standard webContents.send flow applies:
main.ts
Event Queueing in Depth
The queue exists so your app’s startup never races the game: events that arrive while your windows and listeners are still being set up are held per module until you flush them.- The queue is per module. Each module you want events from needs its own
readyForGameEvents()call - flushingoverlay.siegedoes not flushoverlay.universal. - It holds up to 1,000 events per module. Once full, additional events are dropped until the queue is flushed,
and a warning is raised - observable via
overlay.on("warning", ...)(see Issue Handling). - Flushing is one-way and once per app run. After the first call, the module stays in live delivery for the
lifetime of the process - subsequent calls are no-ops, and the queue does not re-arm when the game closes and
relaunches. There’s no harm in calling it again, but you can’t “pause” events with it either; to stop reacting,
remove your listeners with
off/offAny.
Universal Events
Alongside per-game events, Overlayed emits a small set of universal events - such aslogged_in - for subscribed games. They arrive on overlay.universal, which exists even if you never passed the
universal module, and they need their own readyForGameEvents():
main.ts
FAQ
Why am I not receiving any events?
Why am I not receiving any events?
Check these in order:
- Module registered: the game’s module must be in your
overlayed()modulesarray -universal: truealone does not deliver game-specific events. - readyForGameEvents(): it must be called on that specific module. Until then, everything sits in the queue.
- Rejected launch: calling
reject()in agameLaunchlistener blocks that game’s events for the session - see Overlay Events. - Game support: some games support rendering and hotkeys but have no events - check Supported Games.
- Logs: every incoming event is logged internally, including events that were dropped for having no
listener. Run with
debug: trueand see Logging.
Do I call readyForGameEvents() again when the game relaunches?
Do I call readyForGameEvents() again when the game relaunches?
No. It’s once per module per app run. Queueing only covers your app’s startup window - after the first flush,
events are delivered live for the rest of the process, across game sessions. Handle relaunches by resetting
your state (on
gameClose and match_started), not by re-flushing.Can I register listeners before the game launches?
Can I register listeners before the game launches?
Yes - and you should. Listener registration is independent of the game’s lifecycle, and combined with queueing
it means the usual setup (register listeners, call
readyForGameEvents(), both at app startup) never misses an
event regardless of when the game starts.What happens if the queue fills up?
What happens if the queue fills up?
Each module queues at most 1,000 events. Once the buffer is full, further events are dropped until you flush,
and a warning is emitted that you can observe with
overlay.on("warning", ...). If you’re hitting the cap,
call readyForGameEvents() earlier in your startup.Can an event have missing or malformed fields?
Can an event have missing or malformed fields?
No. Every event is validated against its Arktype schema before your listeners run; events that fail validation
are dropped with a warning rather than delivered partially. One deliberate exception to the noise: events whose
type your installed package version doesn’t know about (e.g. a newer game module adds an event) are silently
ignored, so shipping apps don’t spam warnings when new events roll out.
Do game events work when in-game rendering isn't supported?
Do game events work when in-game rendering isn't supported?
Yes. When
gameReady reports inGameRenderingSupported: false, only rendering inside the game is affected -
events still flow. See
Handling inGameRenderingSupported.How do round phases work in Siege?
How do round phases work in Siege?
Siege emits
phase_changed with a phase string ("intro", "planning", "prep", "action", "results",
…) as the round moves along, and round_ended with a full summary - final round state per team, every damage
event, and each player’s final health and operator. The package also exports helpers like
isSiegeRoundStateVictory and isSiegeRoundStateCompleted plus constants such as SIEGE_ROUND_STATE_LABELS
for interpreting the numeric team states. See the Siege events reference.How do I debug what's actually being emitted?
How do I debug what's actually being emitted?
Two options that work well together: attach a
onAny listener per module that logs event.type and
event.content, and enable debug: true in your config so Overlayed’s internal logs - which include every
incoming event, validation failures, and “no handlers” drops - appear. See
Debug Mode.Next Steps
Game Events Reference
The subscription API: on, onAny, queueing, types, and constants.
Supported Games
Every supported game and its full event reference tables.
Game Launch & Close Events
The session lifecycle that brackets these events.
Issue Handling
Observe warnings, errors, and fatals from the overlay.

