---
title: "mix-websocket guides"
description: "Send and receive frames, handle errors, and configure the tunnel for mix-websocket."
schemaType: "TechArticle"
section: "Developers"
lastUpdated: "2026-06-05"
---

# Reference

## Sending and receiving

```ts
ws.addEventListener('message', (e) => {
  if (typeof e.data === 'string') {
    console.log('text frame:', e.data);
  } else {
    // ArrayBuffer for binary frames
    console.log('binary frame:', e.data.byteLength, 'bytes');
  }
});

// Send a text frame
await ws.send('hello');

// Send a binary frame
await ws.send(new Uint8Array([0x01, 0x02, 0x03]));
```

`send()` rejects if called when `readyState` is not `OPEN`. Use `await ws.opened()` to gate the first send, and check `ws.readyState` if you keep references around for later use.

## Error handling

The `error` event carries a non-standard `.message` field with the underlying cause (the standard `Event` carries no payload). Use it for diagnostics:

```ts
ws.addEventListener('error', (e) => {
  const msg = (e as Event & { message?: string }).message;
  console.error('mix-websocket failure:', msg);
});
```

If the upgrade fails before `open` fires, `MixWebSocket` transitions to `CLOSED` and dispatches `error`. `opened()` resolves either on `open` or on the failure event, so callers don't hang.

## Configuration

`MixWebSocket` has no per-instance configuration beyond `url` and `protocols`. Tunnel-level options live on `setupMixTunnel`, the same shared call that mix-fetch and mix-dns use:

```ts
await setupMixTunnel({
  preferredIpr: '...',
  disableCoverTraffic: true,
});
```

See [`SetupMixTunnelOpts`](/developers/mix-tunnel/api/interfaces/SetupMixTunnelOpts) for the full surface.
