---
title: "Input macro (asset type)"
description: "An input macro is a recorded or hand-authored sequence of input simulation events — keystrokes, mouse moves, button clicks, scroll wheel deltas — timestamped and played back via the sim toolbox…"
section: "Types"
slug: "types-inputmacro"
canonical: "https://origozero.ai/docs/types-inputmacro"
updated: "2026-08-22T07:41:56.027956327+00:00"
tags: ["asset-type", "reference"]
---

# Input macro (asset type)

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:

```luau
-- 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:
   ```jsonc
   {
     "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.

## Related types

- `.tool` — for agent-callable input wrappers that may invoke
  macros as part of larger flows.
- `.scene` — macros pair with scenes for deterministic test setups.
