Log inGet started

Input macro (asset type)

Updated October 8, 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 inputSim 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.
    • .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("inputSim", "recordMacro", { duration = 5 })
tools.use("inputSim", "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: the name of an inputSim verb that drives input — the keyboard and mouse verbs (keyDown, keyUp, key, mouseDown, mouseUp, mouseMove, mouseMoveBy, click, scroll, lockPointer), the pad verbs (pad, padOff, padDown, padUp, padPress, padStick, padTrigger) and the touch verbs (touch, touchDown, touchMove, touchUp, stick, tapButton, drag, pinch).
    • arg: that verb's arguments — the value itself when the verb takes one (a key code such as "KeyW", a mouse button index where 0 = LMB, 1 = RMB, 2 = MMB, a canonical pad button name such as "south"), or a table keyed by the verb's parameter names when it takes several ({ dx = 200, dy = 0 } for mouseMove, { stick = "left", x = 0, y = -1 } for padStick, { x = 714, y = 984, id = 1 } for touchDown).
  2. Playback. tools.use("inputSim", "macro", "<name>") schedules every event at its t offset relative to playback start; events are played in order of t, and events sharing one t play in the order the list writes them. 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("inputSim", "recordMacro", { duration = n }) polls live input state and returns the recorded events; tools.use("inputSim", "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.
  • ls /zero/source/<name>.inputMacro lists the files the folder holds.
  • tools.use("inputSim", "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("inputSim", "macro", "a") then tools.use("inputSim", "macro", "b").
  • Keep the expected start state and end state in mind when you name a macro: 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.

In the Inspector

Events: how many events the macro records, when the last one fires, and the first 50 events in order (time, op, argument). Replaying it stays in Operations.

  • asset-type
  • reference