Skip to content

Custom Draw

The Draw API provides immediate-mode 2D drawing primitives. All draw commands are cleared each frame, making it ideal for debug visualization, custom UI overlays, and dynamic graphics.

Draw Callbacks

Register a draw callback to execute drawing commands each frame. The engine calls your function automatically during the render pass:

import { registerDrawCallback, Draw } from 'esengine';
registerDrawCallback('my-debug', (elapsed) => {
Draw.line({ x: 0, y: 0 }, { x: 100, y: 100 }, { r: 1, g: 0, b: 0, a: 1 }, 2);
});

Remove callbacks when no longer needed:

import { unregisterDrawCallback, clearDrawCallbacks } from 'esengine';
unregisterDrawCallback('my-debug');
clearDrawCallbacks();

Drawing Primitives

Lines

Draw.line(
{ x: 0, y: 0 }, // from
{ x: 200, y: 150 }, // to
{ r: 1, g: 1, b: 0, a: 1 }, // yellow
2 // thickness
);

Rectangles

Draw.rect(
{ x: 100, y: 100 }, // center position
{ x: 80, y: 60 }, // size
{ r: 0, g: 0.5, b: 1, a: 0.5 }, // semi-transparent blue
true // filled (default)
);
Draw.rectOutline(
{ x: 100, y: 100 },
{ x: 80, y: 60 },
{ r: 1, g: 0, b: 0, a: 1 }, // red
2 // thickness
);

Circles

Draw.circle(
{ x: 200, y: 200 }, // center
50, // radius
{ r: 0, g: 1, b: 0, a: 1 }, // green
true, // filled
32 // segments
);
Draw.circleOutline(
{ x: 200, y: 200 },
50,
{ r: 1, g: 1, b: 1, a: 1 }, // white
1, // thickness
32 // segments
);

Textures

Draw.texture(
{ x: 300, y: 200 }, // position
{ x: 64, y: 64 }, // size
textureHandle,
{ r: 1, g: 1, b: 1, a: 1 } // tint (default: white)
);
Draw.textureRotated(
{ x: 300, y: 200 },
{ x: 64, y: 64 },
Math.PI / 4, // rotation in radians
textureHandle,
{ r: 1, g: 1, b: 1, a: 1 }
);

Primitives Reference

MethodParametersDescription
line(from, to, color, thickness?)Vec2, Vec2, Color, number=1Line segment
rect(position, size, color, filled?)Vec2, Vec2, Color, bool=trueRectangle
rectOutline(position, size, color, thickness?)Vec2, Vec2, Color, number=1Rectangle outline
circle(center, radius, color, filled?, segments?)Vec2, number, Color, bool=true, number=32Circle
circleOutline(center, radius, color, thickness?, segments?)Vec2, number, Color, number=1, number=32Circle outline
texture(position, size, textureHandle, tint?)Vec2, Vec2, number, Color=whiteTextured quad
textureRotated(position, size, rotation, textureHandle, tint?)Vec2, Vec2, number, number, Color=whiteRotated textured quad

Render State

Control how subsequent primitives are rendered:

Draw.setLayer(5); // render layer (higher = on top)
Draw.setDepth(0.5); // depth sorting within a layer
Draw.setBlendMode(BlendMode.Additive); // blend mode
Draw.setDepthTest(true); // enable depth testing
MethodDescription
setLayer(layer)Set render layer index
setDepth(depth)Set depth value for sorting
setBlendMode(mode)Set blend mode (Normal, Additive, Multiply, Screen, PremultipliedAlpha)
setDepthTest(enabled)Enable or disable depth testing

Custom Mesh Drawing

For advanced rendering, draw custom geometry with shaders or materials:

import { Geometry, Draw, Material } from 'esengine';
const quad = Geometry.createQuad(100, 100);
const transform = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]);
Draw.drawMesh(quad, shaderHandle, transform);
Draw.drawMeshWithMaterial(quad, materialHandle, transform);

drawMeshWithMaterial automatically applies the material’s blend mode, depth test, and uniforms.

See Geometry & Meshes for creating custom meshes and Materials & Shaders for shader setup.

Performance Stats

const drawCalls = Draw.getDrawCallCount();
const primitives = Draw.getPrimitiveCount();

Example: Debug Collision Boxes

import { registerDrawCallback, Draw } from 'esengine';
import { defineSystem, addSystem, Query, LocalTransform, Collider } from 'esengine';
const colliders: Array<{ x: number; y: number; w: number; h: number }> = [];
addSystem(defineSystem(
[Query(LocalTransform, Collider)],
(query) => {
colliders.length = 0;
for (const [entity, transform, collider] of query) {
colliders.push({
x: transform.position.x,
y: transform.position.y,
w: collider.width,
h: collider.height,
});
}
}
));
registerDrawCallback('debug-colliders', () => {
const green = { r: 0, g: 1, b: 0, a: 0.5 };
for (const c of colliders) {
Draw.rectOutline(
{ x: c.x, y: c.y },
{ x: c.w, y: c.h },
green,
1
);
}
});

Next Steps