overlayed() initializes everything the moment you call it - which is usually at import time, since the
standard pattern is export const overlay = overlayed({ ... }) in a shared module. Sometimes that’s too early: you
want the overlay to stay dormant until Electron is ready, until the user has logged in, or until they’ve opted into
the overlay in your settings. This guide walks through splitting setup from initialization with init: false.
This guide covers the delayed-init workflow. For the option itself, see Delayed
Initialization and the
init option reference.How Delayed Initialization Works
Four things to know before writing any code:overlayed()configures;init()activates. Normallyoverlayed()callsinit()for you. Withinit: false, it validates your config and returns the overlay object without starting anything - no game process monitoring, no keybind listening, no windows, no events - until your code callsoverlay.init().- Before
init(), the overlay object is locked. The only member you can touch isoverlay.init. Accessing anything else (overlay.windows,overlay.on, evenoverlay.initialized) throwsoverlayed was called before initialized: <property>. init()is synchronous and one-shot. It takes no arguments and returns nothing - when the call returns, the overlay is fully initialized andoverlay.initializedistrue. Calling it a second time logs a warning and does nothing.- Delaying doesn’t make you miss the game. Game detection starts at
init()and also picks up games that are already running at that point - see the launch events guide. AndgameReadysupports late subscription: a listener attached after a game is ready is invoked immediately with that game’s data.
Splitting Setup from Initialization
Pass init: false
Keep your normal
overlayed() setup - same modules, keybinds, and options - and add init: false. This stays
safe to run at import time.overlayed.ts
Call init() when your app is ready
Call
overlay.init() at the moment of your choosing. init is safe to pass around as a reference - it’s the
one member that works before initialization:main.ts
Do all overlay work after init()
Register listeners and create windows only after Even if the user launched the game before this code ran, you’re covered: already-running games are detected
once monitoring starts, and late
init() has run. Since init() is synchronous, “after” just
means later in the same function:main.ts
gameReady subscribers are called immediately for games that are already
ready.Choosing the Init Moment
Where you put theoverlay.init() call is the whole design decision. Two common patterns:
- On app ready
- Behind a user gate
Initialize as soon as Electron is up. This keeps the overlay’s startup out of module-evaluation time without
changing when it’s effectively available.
main.ts
Keeping Pre-Init Code Safe
The locked-object rule bites in two specific places: A layout that makes this hard to get wrong:overlayed.ts exports the un-initialized overlay, and a single
startOverlayFeatures() function owns all listener registration and window creation. Your entrypoint calls
overlay.init() and then startOverlayFeatures(), and nothing else ever touches overlay at module scope.
Waiting for the Game Instead
The description of this guide says “until the game is ready” - but note that you cannot literally wait for the game to launch before callinginit(), because game detection itself doesn’t start until init() runs. No gameLaunch
or gameReady will ever fire on an uninitialized overlay.
If what you actually want is “do nothing user-facing until the game is ready”, you usually don’t need init: false
at all. Initialize normally and lean on the built-in deferrals:
- Create windows in a
gameReadylistener - see Game Launch & Close Events. - Hold off on consuming game events until you’re ready: events queue per game module until you call
readyForGameEvents()- see Event Queuing.
init: false for gating the overlay on your app’s state (startup order, login, opt-in) rather than on the
game’s state.
FAQ
What happens if I never call init()?
What happens if I never call init()?
Nothing starts: no process monitoring, no keybinds, no windows - none of the overlay’s subsystems come up.
The first time any code accesses an overlay member other than
init, it throws
overlayed was called before initialized. There’s no timeout - the overlay waits indefinitely.What happens if init() is called twice?
What happens if init() is called twice?
The second call logs the warning
Overlayed already initialized and returns without doing anything. It won’t
throw and won’t re-run initialization, so calling init() from two competing code paths is safe - just
unnecessary.Is init() async? How do I know it finished?
Is init() async? How do I know it finished?
No -
init() is synchronous and returns void. There is nothing to await and no “initialized” event; when
the call returns, the overlay is ready and overlay.initialized reads true. Code on the next line can
safely create windows and register listeners.Can I pass different options to init()?
Can I pass different options to init()?
No.
init() takes no arguments - all configuration (modules, keybinds, universal mode, feature flags) is
fixed by the options you passed to overlayed(). If an option needs to depend on runtime state, resolve that
state before calling overlayed() itself.Do keybinds work before init()?
Do keybinds work before init()?
No. Keybind listening is part of initialization, like everything else - there’s no partial mode where some
subsystems run early. Windows, keybinds, game events, notices, and feature flags all become available together
when
init() runs.Will I miss game events that happen while init is delayed?
Will I miss game events that happen while init is delayed?
You won’t receive events from before
init() - detection wasn’t running, so none were produced for you. What
you don’t lose is the session: a game already running at init() time is still detected and goes through
the normal launch flow. After that, in-game events queue per module until you call readyForGameEvents(), so
your own startup code has slack too. See Event Queuing.I got "overlayed was called before initialized: X" - what does it mean?
I got "overlayed was called before initialized: X" - what does it mean?
Some code accessed
overlay.X before overlay.init() ran. The property name in the message tells you what
was touched - search for that access. It’s almost always module-level code: a top-level overlay.on(...), or
destructuring the overlay object at import time. Move the access into code that runs after init().Is "Overlayed metadata has not been initialized yet" the same thing?
Is "Overlayed metadata has not been initialized yet" the same thing?
No - despite the similar wording, that error is about your local development environment, not delayed init. It
means the
.overlayed metadata cache hasn’t been created yet; run overlayed init (the CLI command) to set
it up. See Local Development.Next Steps
Delayed Initialization Reference
The init option in the advanced reference.
Game Launch & Close Events
React to gameLaunch, gameReady, and gameClose - including late subscription.
Game Events
Event queuing and readyForGameEvents().
Overlayed Options
Every option you can pass to overlayed().

