# Web MIDI API

> navigator.requestMIDIAccess() requests MIDI port access. This reference covers options, permission failures, device events, and a no-MIDI fallback.

The Web MIDI API gives a page access to MIDI input and output ports after permission is granted. `navigator.requestMIDIAccess()` returns a promise for a `MIDIAccess` object, whose `inputs` and `outputs` collections contain the available ports.

## Where it is supported

BCD records `requestMIDIAccess()` from Chrome 43 and Firefox 108 on desktop. Its Firefox entry requires a site-permission add-on; Firefox for Android and Safari are recorded as unsupported (BCD `api.Navigator.requestMIDIAccess`).

## Syntax

```js
navigator.requestMIDIAccess()
navigator.requestMIDIAccess(options)
```

The method is exposed in secure contexts. It may prompt for permission or apply a previously stored choice, and its fulfilled promise contains a `MIDIAccess` object.

## Parameters

The optional `options` object controls access beyond ordinary hardware MIDI ports.

| Member | Type | Default | Meaning |
|---|---|---|---|
| `sysex` | `boolean` | `false` | Request permission to send and receive system-exclusive messages. |
| `software` | `boolean` | `false` | Request access to installed software synthesizers. |

`MIDIAccess.inputs` and `MIDIAccess.outputs` are read-only maps of `MIDIInput` and `MIDIOutput` ports. Its `statechange` event reports a port connection or state change.

## Exceptions

The returned promise can reject with these `DOMException` names, as documented for [`requestMIDIAccess()`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMIDIAccess) (MDN).

| Exception | Condition |
|---|---|
| `AbortError` | Navigation closes the document or page. |
| `InvalidStateError` | The underlying MIDI system reports an error. |
| `NotSupportedError` | The system does not support the requested feature or options. |
| `NotAllowedError` | The user or system denies access, or Permissions Policy does not allow it. |

:::caution
Web MIDI access requires a secure context, can be controlled by the `midi` Permissions Policy, and requires an explicit user permission or a previously granted permission. The Web MIDI specification requires `requestMIDIAccess()` to reject with a `DOMException` named `NotAllowedError` when the user or security settings deny access, or when the document is not allowed to use the feature ([`requestMIDIAccess()` failure steps](https://webaudio.github.io/web-midi-api/#dom-navigator-requestmidiaccess), Web MIDI API).
:::

## Examples

Check for the method before requesting access. Keep a non-MIDI control available when the method is absent or the request fails.

### Reporting access with a keyboard fallback

This snippet creates a MIDI button, status text, and an on-screen keyboard. The note buttons remain available without MIDI access.

```js
const midiButton = document.body.appendChild(document.createElement("button"));
const status = document.body.appendChild(document.createElement("p"));
const keyboard = document.body.appendChild(document.createElement("div"));
midiButton.textContent = "Connect MIDI";
keyboard.setAttribute("role", "group");
keyboard.setAttribute("aria-label", "On-screen keyboard");

for (const note of ["C4", "D4", "E4", "F4", "G4"]) {
  const key = keyboard.appendChild(document.createElement("button"));
  key.textContent = note;
  key.addEventListener("click", () => {
    status.textContent = `On-screen note: ${note}`;
  });
}

if (!("requestMIDIAccess" in navigator)) {
  midiButton.disabled = true;
  status.textContent = "MIDI is unavailable. Use the on-screen keyboard.";
} else {
  midiButton.addEventListener("click", async () => {
    midiButton.disabled = true;
    try {
      const access = await navigator.requestMIDIAccess();
      status.textContent = `MIDI inputs: ${access.inputs.size}`;
    } catch (error) {
      status.textContent = `MIDI access failed: ${error.name}. Use the on-screen keyboard.`;
    }
  });
}
```

The unsupported and rejected paths both leave the note buttons enabled and disable the MIDI button.

### Reading messages from connected inputs

After permission is granted, attach a listener to each input in the returned map. The event's `data` value contains the MIDI message bytes.

```js
async function listenForMidi(onMessage) {
  if (!("requestMIDIAccess" in navigator)) return false;

  const access = await navigator.requestMIDIAccess();
  for (const input of access.inputs.values()) {
    input.addEventListener("midimessage", (event) => onMessage(event.data));
  }
  return true;
}

listenForMidi((bytes) => console.log(Array.from(bytes))).catch(console.error);
```

The function returns `false` when the API is absent; callers can use that value to enable an on-screen instrument.

## See also

- [WebHID API](/reference/capabilities/web-hid/), for devices that expose HID reports
- [WebUSB API](/reference/capabilities/web-usb/), for USB devices accessed through vendor interfaces
- [Web MIDI API](https://webaudio.github.io/web-midi-api/) (webaudio.github.io)
- [Navigator: requestMIDIAccess()](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMIDIAccess) (MDN)