Skip to content

Graphics Atlas Quickstart

paper edited this page Apr 13, 2026 · 4 revisions

This page explains how to create an HTML atlas and use it in HlaeObsTools.

Graphics Overview

The graphics feature renders HTML atlases in a separate producer process and exposes them to HLAE through shared D3D11 textures.

Run GfxProducerService.exe on the game PC to use atlases.

Current flow:

  1. HlaeObsTools connects to GfxProducerService over WebSocket.
  2. HlaeObsTools asks the producer to create one or more atlases.
  3. GfxProducerService loads the atlas HTML in CEF offscreen.
  4. CEF paints frames into a shared GPU texture.
  5. The producer returns the shared texture handle to HlaeObsTools.
  6. HlaeObsTools uses that handle with the HLAE graphics pipeline.
  7. When GSI updates arrive, HlaeObsTools forwards raw GSI JSON plus optional extras to the producer.
  8. The producer injects that data into every active atlas.

What An Atlas Is

An atlas is a single HTML page rendered by the graphics producer into a texture.

Inside that atlas you can define one or more regions, and then use those regions in HlaeObsTools graphics instances.

1. Create Atlas Files

Create a folder for your atlas anywhere on disk.

Example:

E:\MyAtlases\scoreboard\
  index.html
  style.css
  app.js

Your HTML can be any normal web UI. HlaeObsTools will render it through the graphics producer.

Minimal example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My Atlas</title>
  <link rel="stylesheet" href="./style.css" />
</head>
<body>
  <div id="root">Hello atlas</div>
  <script src="./app.js"></script>
</body>
</html>

2. Add The Atlas In HlaeObsTools

In the Graphics dock, create a new atlas and configure:

  • atlas name
  • width
  • height
  • HTML path

Important:

  • HtmlPath may point either to your local atlas index.html or to an http:// / https:// URL
  • width and height define the render resolution of the atlas texture and the offscreen browser viewport size
  • the atlas name is what instances use to reference the atlas

3. Define Regions

An atlas can expose one or more named regions.

Each region is defined by:

  • region name
  • u0, v0, u1, v1
  • default width / height

Those UV values define which part of the atlas texture a graphics instance should display.

Common patterns:

  • one full-screen region covering the entire atlas (e.g. "scoreboard")
  • multiple named slots or panels inside one atlas (e.g. "slot1", "slot2", ...)

4. Create Graphics Instances

After the atlas exists, create graphics instances in HlaeObsTools that use:

  • source type: atlas
  • atlas: your atlas name
  • region: the region name you defined

That is what actually places the atlas region into the HLAE graphics system.

NOTE:
You can also use source type: image
In that case, the instance uses an image from resources/AfxHookSource2/images instead of an atlas region.

5. Receive GSI Data In JS

Your atlas receives raw GSI updates automatically.

The easiest way is to listen for:

window.addEventListener("gsi:update", (e) => {
  const { gsi, heartbeat, extras } = e.detail || {};
  if (!gsi) return;

  // update your DOM here
});

You can also define:

window.gsiUpdate = (gsi, heartbeat, extras) => {
  // update your DOM here
};

6. Use gsi And extras

gsi is the raw CS2 GSI payload.

extras currently contains additional derived data tracked by HlaeObsTools.

Current useful extras:

{
  playerDamageStats: {
    [steamId]: {
      adr: number,
      totalDamage: number
    }
  }
}

Example:

function getAdr(extras, steamId) {
  const adr = extras?.playerDamageStats?.[steamId]?.adr;
  return typeof adr === "number" ? Math.round(adr) : null;
}

function getTotalDamage(extras, steamId) {
  const totalDamage = extras?.playerDamageStats?.[steamId]?.totalDamage;
  return typeof totalDamage === "number" ? totalDamage : null;
}

7. Handle Triggers

Atlases can react to animation in/out triggers sent by HlaeObsTools.

Triggers are sent to:

  • an atlas, by atlas name
  • with a target string that is the region name

The target is what your JS switches on, which is the region name used by the graphics instance:

window.hotTrigger = (action, target) => {
  if (action === "animIn" && target === "scoreboard") {
    // show animation
  }

  if (action === "animOut" && target === "scoreboard") {
    // hide animation
  }
};

8. Notify When A Trigger Finished

If your atlas runs an animation and HlaeObsTools should know when it is done to automatically hide the instance, call:

window.hotNotify.triggerDone("animOut", "scoreboard");

Example Pattern

window.addEventListener("gsi:update", (e) => {
  const { gsi, extras } = e.detail || {};
  if (!gsi?.allplayers) return;

  // update UI from gsi / extras
});

window.hotTrigger = (action, target) => {
  if (target !== "scoreboard") return;

  if (action === "animIn") {
    document.body.classList.add("visible");
  }

  if (action === "animOut") {
    document.body.classList.remove("visible");

    setTimeout(() => {
      if (window.hotNotify?.triggerDone) {
        window.hotNotify.triggerDone("animOut", "scoreboard");
      }
    }, 300);
  }
};

Practical Tips