Log inGet started

Input macro (asset type)

Updated 22 August 2026

The engine deliberately does NOT ship per-feature input recipes (walk, sprint, drive). Authors record or hand-write the macro they need; the playback primitive is type-agnostic.

When to use one

  • You want to script an input sequence for testing (e.g. "walk forward 3 seconds, jump, look around").
  • You're capturing a demo / repro of a user interaction sequence.
  • You want deterministic input playback across runs.

If you need per-frame programmatic input (more than a fixed sequence allows), drive the sim toolbox's key / click tools directly from a script. If you need real input handling, that's the input API (man input).

Where it lives

  • Source: /zero/source/.../<name>.inputMacro/
  • Identity: <name> (the .inputMacro suffix strips).
  • Folder shape:
    • events.json — JSON array of { t, op, arg } event tuples. Required.
    • README.md — instance documentation. Required.
    • .metadata — agent-editable tags + free-form fields. Required.

How to create one

Two paths:

-- Record a live sequence: recordMacro captures real input for
-- `duration` seconds and returns the recorded events; saveMacro
-- persists them as the asset.
local events = tools.use("sim", "recordMacro", { duration = 5 })
tools.use("sim", "saveMacro", "<name>", events)

-- Hand-author an empty macro scaffold and fill events in:
asset.create("inputMacro", "<name>")

The record path is preferred for sequences any human could perform; hand-authoring is for synthetic or precisely-timed sequences.

How it operates

  1. Event format. events.json is a JSON array of triples:
    {
      "t": 0.5,
      "op": "keyDown",
      "arg": "KeyW"
    }
    
    • t: seconds since macro start.
    • op: event op — keyDown, keyUp, mouseDown, mouseUp, mouseMove, scroll, lockPointer, tap, click.
    • arg: op-specific argument — a key code ("KeyW", "Space", "ShiftLeft", "Digit1", "ArrowUp"), a mouse button index (0 = LMB, 1 = RMB, 2 = MMB), or a delta table ({ dx = 200, dy = 0 } for mouseMove).
  2. Playback. tools.use("sim", "macro", "<name>") schedules every event at its t offset relative to playback start; events are sorted on play. The same tool takes an inline event list or an AssetRef<inputMacro> instead of a name.
  3. Determinism. Macros are deterministic relative to engine state at playback start — same start state + same macro = same end state.
  4. Recording. tools.use("sim", "recordMacro", { duration = n }) polls live input state and returns the recorded events; tools.use("sim", "saveMacro", "<name>", events) writes them to /zero/source/<name>.inputMacro/events.json and returns the macro's AssetRef.

Discovery

  • asset.list("inputMacro") — every registered macro.
  • asset.inspect("<name>") — event count, duration, source, this type README.
  • cat /zero/source/<name>.inputMacro — same summary.
  • tools.use("sim", "listMacros") — every registered .inputMacro.

Authoring conventions

  • Start macros from a known state (call layers.load("base") before playback) so determinism holds across runs.
  • Keep macros focused on one user-facing action sequence. Longer macros are harder to compose and debug; chain shorter ones via tools.use("sim", "macro", "a") then tools.use("sim", "macro", "b").
  • Document the expected start state and end state in the macro's instance README — what scene / mode / focus the playback assumes.

Common pitfalls

  • Non-deterministic start state. Playing a macro from a different starting scene produces different outcomes. Always lead with a scene load.
  • Frame-rate dependence. Times are in seconds; the dispatcher schedules events relative to wall time, not frames. Effects that depend on per-frame logic may diverge across hardware.
  • mouseMove argument. It carries a { dx, dy } delta, the same shape the recorder emits. Check the recorded values match.
  • .tool — for agent-callable input wrappers that may invoke macros as part of larger flows.
  • .scene — macros pair with scenes for deterministic test setups.
  • asset-type
  • reference