# Contracts

> The shared contracts every surface agrees on: parameter descriptors and their normalization, timed events, and the module face props.

Canonical: https://maruaudio.com/docs/audio-contracts
Documentation index: https://maruaudio.com/llms.txt

Contracts are the small, dependency-free types that the module, the browser
runtime, the native shell and the interface all share. They do no audio work.
Their value is that every surface reads them the same way. They are exported
from `maruaudio/contracts`, and most authors meet them through the
[Audio SDK](https://maruaudio.com/docs/audio-sdk.md) rather than importing them directly.

This page explains the ideas. The exported types in your installed version are
the exact reference.

## Parameters

A module's parameter schema is an ordered list of descriptors. Each descriptor
has a dotted `path`, which is also its stable automation id, plus a label, a
group, a type (`float`, `enum` or `bool`) and, where relevant, a range, step,
default, units, options and a response curve (`taper`).

Order matters to hosts: it is the order parameters are registered in. Treat a
schema's paths and order as part of the module's identity.

A descriptor can also carry optional hints, such as a default MIDI controller
or a role a host may act on (for example, where to write the host tempo). These
never change what the parameter means.

## One normalization for every surface

Hosts and controllers speak in positions from 0 to 1, while your DSP speaks in
engine values such as hertz or decibels. `normalize` and `denormalize` convert
between the two, applying the parameter's range, step and taper:

```ts
import { denormalize, normalize } from "maruaudio/contracts"

const value = denormalize(descriptor, 0.5)
const position = normalize(descriptor, value)
```

Every surface uses this one conversion: host automation, MIDI CC, faces and the
native plugin shell, where the same algorithm is generated for C. A knob, a
controller and an automation lane therefore land on the same value. Shape a
parameter's feel with its taper, not with a face-level curve.

## Events

Events are timed by sample frame within the current render block, never by
seconds, so every host computes the same timing. They cover parameter changes,
note on and off with optional note identity, and per-note expression.

`sortEvents` puts events in delivery order: by frame, with parameter changes
before notes on the same frame, and expression after the note it applies to. A
note that starts on the frame its cutoff changes hears the new cutoff.

## Faces

A face receives `AudioModuleFaceProps`: parameter values and setters, host
facts and optional host capabilities such as generation or license state. It
presents and edits; it never generates audio or opens devices. See
[Module UI](https://maruaudio.com/docs/ui.md).

## Further reading

- [Audio SDK](https://maruaudio.com/docs/audio-sdk.md) for declarations and Processors.
- [Constraints for plugin developers](https://maruaudio.com/docs/constraints-for-plugin-developers.md)
  for the rules these contracts support.
- [Keeping audio consistent everywhere](https://maruaudio.com/docs/parity-and-determinism.md) for how
  targets are checked against each other.
