> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overlayed.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Launch & Close Events

> React to the game launching and closing from your overlay.

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.

<Info>
  This guide covers the launch/close workflow specifically. For the event reference, see [Overlay
  Events](/essentials/overlay-events).
</Info>

## 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](#rejecting-a-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.

<Warning>
  `gameLaunch` and `gameReady` only fire for games you've subscribed to. You must either pass a game
  [module](/packages/overlayed-options#modules) (e.g. `Siege.module()`) or enable
  [universal mode](/packages/overlayed-options#universal) in your `overlayed()` config - with neither, these events
  never fire.
</Warning>

## Reacting to Launch and Close

<Steps>
  <Step title="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.

    <Tabs>
      <Tab title="Game modules">
        ```typescript overlayed.ts theme={null}
        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()],
        });
        ```
      </Tab>

      <Tab title="Universal mode">
        ```typescript overlayed.ts theme={null}
        import { overlayed } from "@overlayed/app";
        import electron from "electron";

        export const overlay = overlayed({
        	electron,
        	applicationId: "YOUR_APPLICATION_ID",
        	universal: true,
        });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="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.

    ```typescript main.ts theme={null}
    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();
    });
    ```
  </Step>

  <Step title="Tear down on gameClose">
    When the game exits, destroy your in-game windows and reset session state so the next launch starts clean.

    ```typescript main.ts theme={null}
    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.
  </Step>
</Steps>

## 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](/windows/introduction), calling `readyForGameEvents()`, and starting your session logic.

<Warning>
  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.
</Warning>

`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:

```typescript theme={null}
overlay.on("gameLaunch", ({ game, reject }) => {
	if (userSettings.disabledGames.includes(game)) {
		reject();
		return;
	}
});
```

<Accordion title="What reject() does">
  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](#faq) below.
</Accordion>

## 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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
if (overlay.hasAnyActiveGames()) {
	// At least one subscribed game is past gameReady and still running
}
```

<Tip>
  `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](/guides/delaying-overlay-init).
</Tip>

## FAQ

<AccordionGroup>
  <Accordion title="Why isn't gameLaunch firing?">
    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](/logging/introduction).
  </Accordion>

  <Accordion title="Does gameLaunch fire if the game was already running when my app started?">
    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.
  </Accordion>

  <Accordion title="Why is there such a long delay between gameLaunch and gameReady?">
    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.
  </Accordion>

  <Accordion title="Does gameClose fire for launches I rejected?">
    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.
  </Accordion>

  <Accordion title="What happens if the game runs as administrator?">
    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](/essentials/issue-handling) - and prompt the user to run your
    app as administrator.
  </Accordion>

  <Accordion title="How does gameReady relate to readyForGameEvents()?">
    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](/essentials/game-events).
  </Accordion>

  <Accordion title="Do I need to remove listeners when the app quits?">
    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.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Overlay Events Reference" icon="bolt" href="/essentials/overlay-events">
    The reference for gameLaunch, gameReady, and gameClose.
  </Card>

  <Card title="Game Events & State" icon="gamepad" href="/guides/game-events-state">
    Listen to in-game events once the game is ready.
  </Card>

  <Card title="Creating Windows" icon="window" href="/windows/introduction">
    In-game vs. out-of-game windows and how to create them.
  </Card>

  <Card title="Delaying Overlay Initialization" icon="hourglass" href="/guides/delaying-overlay-init">
    Wait to initialize your overlay until the game is ready.
  </Card>
</CardGroup>
