Native Scene UI
scene_* is Keybind's native, real-time UI primitive: a topmost, non-activating window that renders a KSL-described list of elements — rectangles, text, images, swatches, controls — at up to 60fps, with clicks/hover/keys reported back to your script by polling. No HTML, no WebView: it's GDI+ under the hood, driven entirely declaratively.
When to use it
- **
scene_*** — a native loupe/lens, a HUD, a color picker, a floating toolbar, a settings panel:
anything that wants a real-time render loop, pixel-level control, or to read a frozen screen buffer (screenbuf_*) for a magnifier. This is the primitive behind Keybind's own color-picker card + lens.
- `overlay_page()` — reach for the HTML/WebView path instead when you want document-style
layout, rich text, or web components — it's the escape hatch for anything that doesn't need a native render loop.
- **
overlay_*/hud_show** — the simplest fixed-vocabulary overlay calls (a text label, a rect, a
HUD banner) are still the fastest one-liner when you don't need a whole element tree.
The lifecycle: open → set → poll → close
let s = scene_open({ anchor: "cursor", gap_above: 0.05, w: 260, h: 140, radius: 10,
theme: { accent: "#6C8CFF" } })
if s == null { throw "could not open the scene window" }
loop {
scene_set(s, [
{ type: "text", value: "Quick Settings", size: 16, weight: "bold", x: 12, y: 10 },
{ type: "input", id: "name", value: "", placeholder: "Your name", x: 12, y: 40, w: 220, h: 32 },
{ type: "row", id: "save", label: "Save", x: 12, y: 84, w: 220, h: 32, hover_fill: "#333" },
])
let ev = scene_poll_event(s)
if ev.type == "input" { log_line("typing: {ev.value}") }
if ev.type == "click" and ev.target == "save" { break }
if ev.type == "dismiss" { break }
sleep(16)
}
scene_close(s)Every scene follows this shape: scene_open creates the window and returns a handle (or null on failure — always check it); scene_set pushes the whole element tree each time your data changes (or scene_update(s, id, patch) to patch just one element cheaply); scene_poll_event drains one event per call, so poll it in a loop with a short sleep; scene_close always tears the window down when you're done.
Element vocabulary
rect, text, image, swatch, line, ellipse, and magnify (reads a screenbuf_* frozen buffer for lens/loupe effects) cover drawing. group is the layout container — set layout: "row", "column", or "stack" and it positions its children for you (padding, gap, align, justify, auto-size) instead of hand-computed coordinates.
Controls and events
checkbox, toggle, slider, input (an editable text field), and row/button are stateful, native controls — they track their own live value, support Tab-to-focus, and report changes via scene_poll_event: click, change (checkbox/toggle), input (slider drag/arrow-key, or text edits), submit (Enter in a text field), scroll (a scrollable group), move (a drag-to-move panel), hover, key, and dismiss.
Theming and motion
A scene-wide theme (accent color, text color, muted color, corner radius, font) cascades to every element that doesn't set its own value explicitly. Set anim: true (the default) and Keybind eases the window's fade-in/out, a toggle's knob slide, and hover fills instead of snapping them.
Drag and scroll
Set draggable: true on scene_open to let the user reposition the whole panel by its background; mark any group scroll: true with a fixed height to get a scrollable, mouse-wheel-driven viewport with a proportional scrollbar.
Building panels faster
The bundled modules/scene-kit helper module wraps common panel patterns — buttons, forms, and themed containers — as builder functions on top of scene_*, so you don't have to hand-author every element map for a typical settings panel or picker.
Gated by the display capability — the same one screen_freeze/screenbuf_*/color_pick_screen already use, so a script using scene_* on its own doesn't need a new capability grant.
> Full element reference, every option, and worked examples: see lib-scene in the Function > Library, or the KSL spec that ships with the app.