---
sidebar_position: 9
---

# P2P and Media API

P2P APIs send plugin data to connected peers. Media APIs play audio and contribute custom streams.

## Required Capabilities

| Method | Capability |
| --- | --- |
| `p2p.connectedPeers()` | `p2p.data` |
| `p2p.broadcastData(eventName, payload)` | `p2p.data` |
| `p2p.sendData(peerId, eventName, payload)` | `p2p.data` |
| `media.playAudioClip(request)` | `media.playAudio` |
| `media.addCustomAudioStream(request)` | `media.addAudioStream` |
| `media.addCustomVideoStream(request)` | `media.addVideoStream` |
| `media.setInputVolume(volume)` | `audio.volume` |
| `media.setOutputVolume(volume)` | `audio.volume` |

## Connected Peers

```js
export function activate(context) {
  const peerIds = context.api.p2p.connectedPeers();

  context.api.logger.info('Connected peers', { peerIds });
}
```

## Broadcast Data

```js
export function activate(context) {
  context.api.p2p.broadcastData('soundboard:played', {
    soundId: 'airhorn-short',
    label: 'Airhorn',
    playedBy: 'Alice',
    playedAt: 1777473600000
  });
}
```

## Send Data to One Peer

```js
export function activate(context) {
  context.api.p2p.sendData('peer-muse-laptop', 'private-tool:ping', {
    requestId: 'ping-20260429-001',
    message: 'Are you receiving plugin data?'
  });
}
```

## Play an Audio Clip

```js
export async function activate(context) {
  await context.api.media.playAudioClip({
    url: 'https://cdn.example.com/metoyou/sounds/chime.wav',
    volume: 0.65
  });
}
```

## Add a Custom Audio Stream

```js
export async function activate(context) {
  const audioContext = new AudioContext();
  const oscillator = audioContext.createOscillator();
  const gain = audioContext.createGain();
  const destination = audioContext.createMediaStreamDestination();

  oscillator.type = 'sine';
  oscillator.frequency.value = 440;
  gain.gain.value = 0.03;
  oscillator.connect(gain);
  gain.connect(destination);
  oscillator.start();

  await context.api.media.addCustomAudioStream({
    label: 'Tuning tone',
    stream: destination.stream
  });

  setTimeout(async () => {
    oscillator.stop();
    await audioContext.close();
  }, 1000);
}
```

## Add a Custom Video Stream

```js
export async function activate(context) {
  const canvas = document.createElement('canvas');
  canvas.width = 1280;
  canvas.height = 720;
  const ctx = canvas.getContext('2d');

  ctx.fillStyle = '#111827';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#ffffff';
  ctx.font = '48px sans-serif';
  ctx.fillText('Plugin camera scene', 80, 120);

  const stream = canvas.captureStream(15);

  await context.api.media.addCustomVideoStream({
    label: 'Plugin camera scene',
    stream
  });
}
```

## Set Volumes

```js
export function activate(context) {
  context.api.media.setInputVolume(0.85);
  context.api.media.setOutputVolume(0.75);
}
```

Use media APIs with visible controls and clear user consent. Unexpected audio or video is a poor user experience.