esp module is a declarative layer for drawing overlays on
entities. You never touch on("render", ...) - instead you register
elements on an entity class, and each frame the engine calls
your render(e, ctx) function per entity to decide what to draw.
The user controls where and how elements are styled via the
in-game builder menu; your code only decides what they show and
when they are visible.
Overview
Classes
Pick which entities to draw on:
enemy, team, dropped, projectile.Elements
Register text, bars, and icons with a per-frame
render.Render returns
Shortcuts and descriptor objects your
render can return.Runtime control
Mutate element visibility, label, and preview on the fly.
Classes
A class is a group of entities the engine iterates for you. You attach elements to it and they are drawn on every matching entity.esp.class
kind. This makes the call idempotent - safe
to run on every script reload without duplicating elements.
"enemy" | "team" | "dropped" | "projectile"
required
Which entities to iterate every frame.
enemy- enemy player pawnsteam- teammate player pawns (not including local)dropped- dropped weapons and grenadesprojectile- in-flight grenades, molotovs, smokes
(e: Entity) => any
Runs once per entity per frame, before any element’s
render.
Its return value is passed as ctx to every element on that entity.
Use it to hoist shared work like HP parsing, alive checks, or
distance math so the element renders stay cheap.The render callback
Every element takes arender(e, ctx) function. It runs every frame
for every entity of the class.
Entity
The entity currently being drawn. This is the exact same proxy
documented in Entities - all schema fields, reserved
properties (
isValid, className, controller, …), helper
methods (getBoundingBox, isEnemy, …) and raw-buffer reads are
available.any
Whatever the class’s
context(e) returned for this entity on this
frame. If no context was supplied, ctx is undefined. Shared
across every element on the same entity, so do heavy/shared work
once in context rather than re-doing it in each render.Elements
A class has three element factories. Each returns anElement handle.
class.text
string
required
Stable ID. Used as the key in the user’s saved layout - keep it
constant across reloads or the user will lose their placement.
(e: Entity, ctx: any) => TextResult
required
Called every frame for every entity. See Text result
for accepted return shapes.
TextResult
Value shown in the builder menu before a real entity is available.
Accepts the same shapes as
render."top" | "bottom" | "left" | "right" | "center"
Initial slot for the element. The user can drag it elsewhere; this
is only the first-time default.
class.bar
class.text. The render return type is
a Bar result.
class.icon
null from render for now.
Render returns
Everyrender (and preview) return value is one of three things:
Fields you return override the user’s menu settings for that
frame. Fields you omit keep whatever the user configured.
There is no “clear override” call - stop returning the field and the
user’s value takes over on the next frame.
Text result
Bar result
Icon result
Reserved. Always returnnull.
Preview
preview is shown in the builder menu so the user can see the element
before a real entity exists. It accepts the same shapes as render.
Runtime control
Every.text / .bar / .icon call returns an Element handle for
live mutation.
element.destroy
destroy().
After destruction:
- getters return
""/false - setters are silently ignored
- no exceptions are ever thrown
Error handling
Exceptions insiderender and context are caught by the bridge so one
broken function never takes down the rest.