Skip to main content
An overlay’s lifecycle is driven by the game’s lifecycle: you create windows when the game starts, start listening for game events once it’s safe to, and tear everything down when the player exits. This guide walks through the three events that model that lifecycle - gameLaunch, gameReady, and gameClose - and the patterns for reacting to them.
This guide covers the launch/close workflow specifically. For the event reference, see Overlay Events.

The Game Session Lifecycle

Overlayed monitors game processes for you - there’s no polling or process-watching code to write. A single game session emits three events on the main overlay object, in order:
  1. gameLaunch - fires as soon as Overlayed detects the game’s process. The payload is { game, reject }. Nothing has been injected into the game yet; this is your chance to do lightweight prep or reject the launch entirely.
  2. gameReady - fires once Overlayed has finished preparing the game (injecting the render hook and any game modules). The payload is { game, inGameRenderingSupported }. From this point your overlay can render in-game and receive game events.
  3. gameClose - fires when the game’s process exits. The payload is { game }. Tear down in-game windows and reset any per-session state here.
gameLaunch and gameReady only fire for games you’ve subscribed to. You must either pass a game module (e.g. Siege.module()) or enable universal mode in your overlayed() config - with neither, these events never fire.

Reacting to Launch and Close

1

Subscribe to games

Tell Overlayed which games you care about in your overlayed() setup. Pass modules for specific games, or use universal mode to react to every supported game.
overlayed.ts
import { overlayed } from "@overlayed/app";
import * as Siege from "@overlayed/app/siege";
import electron from "electron";

export const overlay = overlayed({
	electron,
	applicationId: "YOUR_APPLICATION_ID",
	modules: [Siege.module()],
});
2

Create your window on gameReady

gameReady is the right moment to create in-game windows - the render hook is injected and the overlay can actually draw in the game.
main.ts
import { overlay } from "./overlayed.ts";
import { AppWindow } from "./appWindow.ts";

const appWindow = new AppWindow();

overlay.on("gameReady", ({ game, inGameRenderingSupported }) => {
	if (game !== "siege") {
		return;
	}

	appWindow.create();
});
3

Tear down on gameClose

When the game exits, destroy your in-game windows and reset session state so the next launch starts clean.
main.ts
overlay.on("gameClose", ({ game }) => {
	if (game !== "siege") {
		return;
	}

	appWindow.destroy();
});
gameClose fires once per game - always branch on game if you support more than one.

gameLaunch vs. gameReady

Both fire at the start of a session, but they’re not interchangeable:
  • gameLaunch fires at process detection. The game is likely still on its splash screen, and nothing has been injected. Use it for things that don’t depend on the game being playable: warming caches, fetching config for that game, analytics, or deciding to opt out.
  • gameReady fires after injection completes. Use it for anything user-facing: creating in-game windows, calling readyForGameEvents(), and starting your session logic.
Expect a real gap between the two. For freshly launched games, Overlayed currently waits ~45 seconds before injecting to give the game time to finish starting up - don’t design UX that assumes gameReady follows gameLaunch immediately.
gameLaunch listeners can be async, and Overlayed waits for all of them to settle before continuing the launch flow. Keep them fast - a slow listener delays injection (and therefore gameReady) for that game.

Rejecting a Launch

Call reject() from a gameLaunch listener to stop Overlayed from handling that game. This is the standard way to implement a per-game “disable overlay” setting:
overlay.on("gameLaunch", ({ game, reject }) => {
	if (userSettings.disabledGames.includes(game)) {
		reject();
		return;
	}
});
Calling reject() for a given game:
  • Prevents the overlay from rendering in that game (no injection happens)
  • Blocks that game’s events from being emitted
  • Prevents gameReady from being emitted for that game
It does not suppress gameClose - see the FAQ below.

Handling inGameRenderingSupported

gameReady tells you whether in-game rendering is available for this session. It’s false when the game doesn’t support render injection, when injection is disabled for that game, or when injection failed. Your overlay should degrade gracefully - typically by falling back to an out-of-game window:
overlay.on("gameReady", ({ game, inGameRenderingSupported }) => {
	if (inGameRenderingSupported) {
		hudWindow = overlay.windows.createInGameWindow({ width: 400, height: 300 });
	} else {
		// Regular desktop window the player can position next to the game
		hudWindow = overlay.windows.createWindow({ width: 400, height: 300 });
	}

	hudWindow.loadURL(siteUrl);
});
Even when inGameRenderingSupported is false, game events still work - only rendering inside the game is affected.

Tracking Session State

Multiple subscribed games can be running at once, so treat these events as per-game rather than global. Keying your state by game keeps close events from tearing down the wrong session:
const sessions = new Map<string, AppWindow>();

overlay.on("gameReady", ({ game }) => {
	const window = new AppWindow();
	window.create();
	sessions.set(game, window);
});

overlay.on("gameClose", ({ game }) => {
	sessions.get(game)?.destroy();
	sessions.delete(game);
});
For a quick “is any game running?” check - e.g. to decide whether to show a “waiting for game” screen - use:
if (overlay.hasAnyActiveGames()) {
	// At least one subscribed game is past gameReady and still running
}
gameReady supports late subscription: if you attach a listener after a game is already ready, the listener is invoked immediately with that game’s data. You don’t need to race your initialization against the game launching - see Delaying Overlay Initialization.

FAQ

Check these in order:
  1. Subscription: you must pass a game module or set universal: true in your overlayed() config. Without one of the two, gameLaunch and gameReady never fire.
  2. Game coverage: with modules (and no universal mode), only games covered by the modules you passed emit events.
  3. Logs: Overlayed logs every process it detects and why it was skipped. See Logging.
Yes. The launch flow runs when Overlayed detects the game’s process, including games that were already running when your overlay app started. For processes that have been running a while, the extra injection delay is skipped, so gameReady follows quickly.
For freshly launched games, Overlayed intentionally waits (~45 seconds) before injecting so the game can finish starting up. Treat gameLaunch as “the game is starting” and gameReady as “the overlay is usable” - and don’t block anything user-visible on the gap between them.
Yes. gameClose is tied to the game process exiting, not to a successful launch flow - it fires even for games whose launch you rejected. If you reject certain games, apply the same filtering in your gameClose handler.
If the game process is elevated but your app is not, Overlayed cannot inject into it. A fatal ELEVATION_MISMATCH error is reported and gameReady never fires for that session. You can observe this with overlay.on("fatal", ...) - see Issue Handling - and prompt the user to run your app as administrator.
They’re opposite directions of the same handshake. gameReady is Overlayed telling you the game is prepared. readyForGameEvents() is you telling Overlayed your overlay is ready to consume game events - until you call it, events are queued (up to 1000). A common pattern is calling it inside your gameReady listener. See Game Events.
No. Event listeners are torn down with the overlay when the app quits. Use overlay.off(event, callback) only when you want to stop listening mid-session.

Next Steps

Overlay Events Reference

The reference for gameLaunch, gameReady, and gameClose.

Game Events & State

Listen to in-game events once the game is ready.

Creating Windows

In-game vs. out-of-game windows and how to create them.

Delaying Overlay Initialization

Wait to initialize your overlay until the game is ready.