Skip to content

Rendering

Epitaph exposes rendering helpers through the render API. These helpers are context-sensitive.

  • 2D operations must run inside on_render_2d
  • 3D operations must run inside on_render_3d

Epitaph tracks the active render event internally and will reject context-specific calls in the wrong callback.

All callbacks are lambdas defined inside the script hash. Top-level def methods do not work.

script = {
name: "My Script",
description: "What it does.",
category: "RENDER",
on_load: lambda do
# register settings here
end,
on_render_2d: lambda do |_event|
# 2D render code here
end,
on_render_3d: lambda do |_event|
# 3D render code here
end
}

Wrong — top-level def methods are ignored:

def on_render_2d(event) # does NOT work
...
end
render.color(r, g, b, a) # => color object

All four channels are required. Values are integers 0–255.

white = render.color(255, 255, 255, 255)
dimmed = render.color(20, 20, 20, 70)

Draws a filled rectangle using absolute max coordinates (not width/height).

render.fill(x1, y1, x2, y2, color)

Draws a rectangle outline using x, y, width, height (not max coordinates).

render.border(x, y, width, height, color)
render.text(text, x, y, color, shadow)
  • text — String
  • x, y — Integer pixel position
  • color — color object from render.color
  • shadow — Boolean (optional, defaults to false)
render.text("Hello", 8, 8, white, true)

Returns the pixel width of the string using the default font.

width = render.measure_text(text) # => Float

Returns a screen-space box for the entity, or nil if off-screen or unavailable.

box = render.entity_screen_box(entity_or_id, padding) # => box or nil
  • entity_or_id — entity table or Integer entity ID
  • padding — Float (extra margin, e.g. 0.03)

Returns nil when the entity is off-screen or unavailable.

Box fields:

FieldTypeDescription
on_screenBooleanAlways true when non-nil
xIntegerLeft edge (rounded)
yIntegerTop edge (rounded)
widthIntegerWidth (rounded)
heightIntegerHeight (rounded)
min_xFloatLeft edge (precise)
min_yFloatTop edge (precise)
max_xFloatRight edge (precise)
max_yFloatBottom edge (precise)

Always check box.nil? and box.width > 0 before drawing.

All 3D methods must run inside on_render_3d. The optional through_blocks argument disables depth testing (default false).

render.line_3d(x1, y1, z1, x2, y2, z2, color, through_blocks, line_width)
render.box_3d(x1, y1, z1, x2, y2, z2, color, through_blocks, line_width)
render.filled_box_3d(x1, y1, z1, x2, y2, z2, color, through_blocks)
render.circle_3d(x, y, z, radius, color, through_blocks, line_width)

through_blocks and line_width are optional. line_width defaults to 1.0.

render.is_2d_context # => Boolean
render.is_3d_context # => Boolean

render.world_to_screen(x, y, z) projects a world position to screen space. Returns a table or nil:

pt = render.world_to_screen(entity.x, entity.y, entity.z)
# pt.x, pt.y => Float screen coords
# pt.depth => Float 0.0–1.0 depth value
# pt.on_screen => Boolean (depth > 0 && depth < 1)

Custom font rendering requires a size argument (font scale). Available font names: poppins, inter, arial, impact, bangers, jello.

render.font_text(font_name, text, x, y, size, color, shadow)
render.measure_font_text(font_name, text, size) # => Float width
render.font_height(font_name, size) # => Float height
render.list_fonts # => Array of String
render.font_text("poppins", "Hello", 8.0, 8.0, 14.0, white, false)
w = render.measure_font_text("poppins", "Hello", 14.0)
script = {
name: "Ping Hud",
description: "Shows FPS and ping",
category: "RENDER",
on_render_2d: lambda do |_event|
text = "FPS: #{client.get_fps} | Ping: #{client.get_ping}"
white = render.color(255, 255, 255, 255)
render.text(text, 8, 8, white, true)
end
}

` uby script = { name: “Arraylist Demo”, description: “Shows another module with its info suffix”, category: “RENDER”,

on_render_2d: lambda do |_event| feature = modules.get(“Aim Assist”) next unless feature && feature.is_enabled

info = feature.get_info
label = info.nil? || info.empty? ? feature.get_name : "#{feature.get_name} #{info}"
white = render.color(255, 255, 255, 255)
render.text(label, 8, 8, white, true)

end } `

script = {
name: "Player ESP",
description: "2D player boxes.",
category: "RENDER",
on_load: lambda do
mod.register_double_setting("Max Distance", 64.0, 1.0, 256.0, 1.0)
mod.register_boolean_setting("Draw Self", false)
end,
on_render_2d: lambda do |_event|
return unless player.is_present && world.is_present
max_distance = mod.get_setting_value("Max Distance") || 64.0
draw_self = mod.get_setting_value("Draw Self") || false
outline = render.color(255, 255, 255, 255)
fill = render.color(20, 20, 20, 70)
text_color = render.color(255, 255, 255, 255)
self_id = player.get_id
world.get_players.each do |entity|
next if entity.nil?
next if !draw_self && entity.id == self_id
next unless entity.alive
next if entity.removed
next if entity.get_distance > max_distance
box = render.entity_screen_box(entity.id, 0.03)
next if box.nil? || !box.on_screen
next if box.width <= 0 || box.height <= 0
render.fill(box.min_x, box.min_y, box.max_x, box.max_y, fill)
render.border(box.min_x, box.min_y, box.width, box.height, outline)
name = entity.name
name_width = render.measure_text(name)
render.text(name, (box.min_x + box.width / 2.0 - name_width / 2.0).round, (box.min_y - 10.0).round, text_color, true)
end
end
}
script = {
name: "Block Highlight",
description: "Highlights the targeted block.",
category: "RENDER",
on_render_3d: lambda do |_event|
return unless targeting.is_block
block = targeting.get_block
color = render.color(255, 64, 64, 180)
render.box_3d(block.x, block.y, block.z, block.x + 1, block.y + 1, block.z + 1, color)
end
}