> For the complete documentation index, see [llms.txt](https://docs.nursultan.fun/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nursultan.fun/examples/esp-through-walls.md).

# ESP through walls

Highlights players with boxes, draws tracers to them and labels their health. An example of splitting work correctly between the tick and the frame.

```kotlin
name("ESP")
description("Highlights players through walls")

key(Key.R)

val range = slider("Range", 64, 8, 128)
val boxes = checkBox("Boxes", true)
val tracers = checkBox("Tracers", true)
val names = checkBox("Labels", true)
val skipAllies = checkBox("Skip allies", true)
val color = colorPicker("Colour", Colors.rgba(255, 85, 85, 255))

var targets: List<Entity> = emptyList()

onEnable { targets = emptyList() }
onDisable { targets = emptyList() }

on<ClientTickEvent> {
    whenInGame {
        targets = world.entitiesNear(player.position(), range.value().toDouble()) { target ->
            target.isPlayer()
                && target.alive()
                && !target.isSelf()
                && !(skipAllies.value() && target.isAlly())
        }
    }
}

on<Render3DEvent> { e ->
    val r = e.render()
    for (target in targets) {
        if (boxes.value()) {
            r.entityBox(target, color.value(), true)
        }
        if (tracers.value()) {
            val middle = target.renderPosition().add(0.0, target.height() / 2.0, 0.0)
            r.tracer(middle, color.value(), true)
        }
    }
}

on<Render2DEvent> { e ->
    if (!names.value()) return@on
    val r = e.render()
    for (target in targets) {
        val living = target.asLiving() ?: continue
        val head = target.renderPosition().add(0.0, target.height() + 0.35, 0.0)
        val point = r.project(head)
        if (!point.visible()) continue

        val text = target.name() + "  " + living.health().toInt()
        val width = r.textWidth(text, 8f)
        val left = point.x() - width / 2f
        r.roundedRect(left - 3f, point.y() - 2f, width + 6f, 12f, 3f, Colors.rgba(0, 0, 0, 140))
        r.text(text, left, point.y(), 8f, Colors.WHITE)
    }
}
```

## Things worth noticing

**Finding targets once a tick, drawing every frame.** `world.entitiesNear` inside a render handler would walk the world 60+ times a second. The list is built in `ClientTickEvent` and kept in a variable.

**`renderPosition()`, not `position()`.** The first is already smoothed between ticks, so boxes and labels do not judder.

**Labels are HUD, not world.** Text is not drawn in 3D. The position above the head is turned into screen coordinates with `project`, and the drawing itself happens in `Render2DEvent`.

**`visible()` is not optional.** Without it, points behind you get drawn at random places on screen.

**The list is cleared on disable.** Otherwise `targets` keeps entities from an old world after you switch off.
