---
title: "inputMap assetType"
description: "A control scheme, expressed as an asset. A <name>.inputMap/ folder whose init.luau returns the map record, and whose controls are the <name>.inputBinding/ child folders beside it — the same…"
section: "Types"
slug: "types-inputmap"
canonical: "https://origozero.ai/docs/types-inputmap"
updated: "2026-08-22T07:41:56.039631698+00:00"
tags: ["asset-type", "reference"]
---

# inputMap assetType

```
racer.inputMap/
  init.luau                    -- what the map is, and how it composes
  boost.inputBinding/
    init.luau
  steer.inputBinding/
    init.luau
```

```luau
-- racer.inputMap/init.luau
return {
    name = "racer",
    description = "Throttle, steering and boost.",

    -- The group these controls belong to, and the groups this map stands
    -- down while it is live. Declaring neither composes with everything.
    group = "vehicle",
    suppresses = { "player" },
}
```

Each control carries `kbm`, `gamepad` and `touch`; the `inputBinding`
assetType covers the record shape.

## Reading it

A controller activates the map it needs and subscribes to the controls it
drives. Nothing is live until something asks for it.

```luau
function awake()
    local map = self.inputMap:activate()
    map.boost:onPressed(function() self:boost() end)
    map.steer:onInput(function(v, dt, active)
        if not active then return end
        self:steer(v, dt)
    end)
end

function onDestroy()
    self.inputMap:deactivate()
end
```

A control the map does not declare reads as `nil`, so a typo raises at the
subscribe call. A controller written against more than one map tests for
an optional control first — `if map.crouch then map.crouch:onInput(...) end`.

Several maps can be live at once, and the player's surface is the union of
them. `group` / `suppresses` are how one map stands another down — a car
takes the player over by naming the `player` group, and walking resumes the
moment the car releases. `Zin.scheme.live()` reports each live map's
`group`, `suppresses` and `suppressedBy`.

## Ref methods

- `:activate()` — load every `<name>.inputBinding/` child, validate them,
  add them to the live binding set, and return the handles keyed by control
  name. Every fault across every child is reported in one error. Activating
  a map already live returns its existing handles, so two components
  sharing one map get one set of controls.
- `:deactivate()` — release this holder's claim. The last holder to release
  takes the controls out of the live set and disconnects everything
  subscribed through the map.
- `:controls()` — `{ name, label, kind, classes, problems }` per child,
  read without activating anything, so an authoring tool can show a map's
  whole surface and everything wrong with it before it goes live.
- `:record()` — the raw record its `init.luau` returns.
- `:effective()` — the materialized effective map, without activating it.
- `:bindingsFor(name, class)` — the bindings a named entry has for one
  device class in this map's effective form.

## Writing one

The `inputAuthor` toolbox writes a map and its controls together, filling
in canonical defaults for a control whose name it recognises:

```luau
tools.use("inputAuthor", "map", "racer", { "throttle", "steer", "boost" },
    { group = "vehicle", suppresses = { "player" } })
tools.use("inputAuthor", "check", "/zero/source/inputMaps/racer.inputMap")
```

`asset.create("inputMap", name, { record = ... })` bakes a record into both
shapes a map is read in — `init.luau` beside one `<name>.inputBinding/`
child per entry, each carrying all three device classes, a class the record
had nothing for written as `false` with the reason beside it. That is the
hook `Zin.map.bake(name)` drives to turn the active effective map into an
editable asset.

The engine's built-in schemes live at `@builtin::inputMaps.default`,
`.editor`, `.editorMeta`, `.vehicle` and `.flight`.
