4.2. Visual aids — tutorial-mode overlays, mouse trail, narration, key HUD

Tutorial-mode overlays — highlight rectangles, mouse-cursor sprite + trail, narration text, key HUD, click flash — painted over the live ImGui surface to guide the viewer through scripted recordings. Every overlay is opt-in and keyed off the boost registry’s widget path, so highlighting MAIN_WIN/SETTINGS_TAB/RPS resolves to the exact rect the renderer drew. Painters are registered as end-of-frame hooks so the host’s render loop is unchanged.

Auto-highlight (imgui_auto_highlight_on_command) makes every accepted imgui_* command auto-flash its target — useful when recording with imgui_playwright so each programmatic action is visually narrated without explicit imgui_highlight calls.

4.2.1. Constants

g_keycap_trace_max = 6

g_keycap_trace_max:int const

g_keycap_trace_fallback_lifetime_s = 1f

g_keycap_trace_fallback_lifetime_s:float const

4.2.2. Structures

HighlightArgs

struct HighlightArgs

MouseTrailArgs

struct MouseTrailArgs

NarrateArgs

struct NarrateArgs

AutoHighlightArgs

struct AutoHighlightArgs

CursorSpriteArgs

struct CursorSpriteArgs

KeyHudArgs

struct KeyHudArgs

FocusRectArgs

struct FocusRectArgs

MemDebugArgs

struct MemDebugArgs

4.2.3. Highlight overlay

highlight(target: string; frames: int = highlight_default_frames; color: uint = highlight_color )

Flash a colored rect around the named widget’s bbox for frames ticks. target is a registry path or "0x<hex_id>".

Arguments:
  • target : string

  • frames : int

  • color : uint

imgui_auto_highlight(input: JsonValue? ): JsonValue?

[live_command] toggle for the imgui_auto_highlight_on_command flag. Arg: enabled.

Arguments:
imgui_highlight(input: JsonValue? ): JsonValue?

[live_command] wrapper around highlight. Args: target, frames, color.

Arguments:

4.2.4. Mouse trail

imgui_mouse_trail(input: JsonValue? ): JsonValue?

[live_command] wrapper around mouse_trail. Args: enabled, fade, color.

Arguments:
mouse_trail(enabled: bool; fade: float = -1f; color: uint = 0x0 )

Toggle the fading mouse-trail overlay; non-default fade / color override the module globals. Disabling clears the trail buffer.

Arguments:
  • enabled : bool

  • fade : float

  • color : uint

4.2.5. Narration

compute_connector_end(target_bbox: float4; banner_center: float2 ): float2

Mirror of compute_connector_start on the target side: closest point on the target’s bbox boundary to banner_center. Same clamp-onto-rect trick — terminates the line on the target’s edge (or corner, in offset cases) instead of inside it.

Arguments:
  • target_bbox : float4

  • banner_center : float2

compute_connector_start(anchor: float2; box_size: float2; widget_center: float2 ): float2

Closest point on the post-it’s boundary to widget_center. Coincides with the corresponding edge midpoint when widget_center is directly outside one side of the post-it; for offset/corner cases (post-it viewport-clamped sideways relative to the target), the line walks to the nearest corner so the arrow points naturally at the target.

Arguments:
  • anchor : float2

  • box_size : float2

  • widget_center : float2

compute_narrate_anchor(has_target: bool; widget_bbox: float4; box_size: float2; viewport: float2; edge_margin: float; gap: float; shadow: float2; hud_zone_h: float; other_bboxes: array<float4>; avoid_bboxes: array<float4> ): float2

Pick a post-it anchor (top-left corner) by scoring 4 viewport-clamped candidates (right/left/below/above). score_anchor adds 1 per incidental widget bbox (other_bboxes) the banner overlaps, and 1000 if it overlaps the anchor target OR any avoid_bboxes member (the step’s other “main” elements) — so a multi-target step’s box never covers a related control — lowest-score candidate wins, ties broken by iteration order (right > left > below > above). If the banner is larger than the viewport’s placement region even when clamped, returns a bottom-left safety anchor — only fires when the caller passed text that genuinely cannot fit.

Arguments:
  • has_target : bool

  • widget_bbox : float4

  • box_size : float2

  • viewport : float2

  • edge_margin : float

  • gap : float

  • shadow : float2

  • hud_zone_h : float

  • other_bboxes : array<float4>

  • avoid_bboxes : array<float4>

imgui_narrate(input: JsonValue? ): JsonValue?

[live_command] wrapper around narrate. Args: text, target (single anchor) OR targets (list — first is the anchor, all hard-avoided), frames, duration_s (wall-clock seconds; wins over frames).

Arguments:

4.2.5.1. narrate

narrate(text: string; target: string = ""; frames: int = narrate_default_frames; duration_s: float = -1f )

Pop a callout box with text. By default it lingers frames render ticks; pass duration_s > 0 to hold it for that many wall-clock seconds instead — rate-independent, required for recordings where render fps and capture fps diverge. If target resolves to a widget bbox the box anchors next to it with a connector line; otherwise it floats top-left.

Arguments:
  • text : string

  • target : string

  • frames : int

  • duration_s : float

narrate(text: string; targets: array<string>; frames: int = narrate_default_frames; duration_s: float = -1f )

4.2.6. HUD

imgui_focus_rect(input: JsonValue? ): JsonValue?

[live_command] toggle for the focus rectangle around io.active_widget — shows where typing lands. Arg: enabled.

Arguments:
imgui_key_hud(input: JsonValue? ): JsonValue?

[live_command] toggle for the bottom-center keycap HUD + Ctrl/Shift/Alt/Cmd modifier strip. Args: enabled, show_modifiers, keycap_fade_s (seconds a keycap lingers - bump it so a chord stays readable while a voice line names it).

Arguments:

4.2.7. Cursor sprite

cursor_sprite(enabled: bool )

Toggle the in-frame cursor sprite. Drawn on the ForegroundDrawList so APNG recordings show a visible pointer even when the OS hardware cursor isn’t captured.

Arguments:
  • enabled : bool

imgui_cursor_sprite(input: JsonValue? ): JsonValue?

[live_command] wrapper around cursor_sprite. Arg: enabled.

Arguments:

4.2.8. Override hooks

apply_synth_io_override()

Per-frame synth IO entry point for hand-rolled (non-harness) update loops — call between the backend’s *_NewFrame() and ImGui’s NewFrame(). Drains the COMPLETE synth pipeline: advances L1 input coroutines (imgui_click press/release) then drives imgui_synth_tick (timelines + held-state re-assert). Mirrors the harness’s advance_coroutines() (in harness_begin_frame) + imgui_synth_tick() (in harness_new_frame), so coroutine-based synth clicks work without the harness — a demo calls this one function and never needs advance_coroutines itself.

4.2.9. Memory debugging

imgui_memory_debug(input: JsonValue? ): JsonValue?

[live_command] toggle for imgui_debug_memory — per-frame GC-pressure logging. Arg: enabled.

Arguments:

4.2.10. Color utilities

rgba(r: int; g: int; b: int; a: int ): uint

Pack R/G/B/A bytes into ImGui’s IM_COL32 word order. Useful when configuring the tunable color globals by channel rather than hex literal.

Arguments:
  • r : int

  • g : int

  • b : int

  • a : int

4.2.11. Serve / lifecycle

visual_aids_init()

[init] — registers the foreground-drawlist paint hook and the post-command auto-highlight hook. Runs once at module load.