# Noise Cancellation

Enable noise cancellation by installing `@stream-io/audio-filters-web`, powered by [krisp.ai](https://krisp.ai/).

## Best Practices

- Check `isSupported` before showing noise cancellation controls.
- Check `isReady` before allowing users to enable or toggle noise cancellation.
- Use `useMemo` when creating the `NoiseCancellation` instance to prevent recreating it on every render.
- Adjust suppression level based on your use case (voice vs music content).
- For NextJS, use lazy loading with Pages router or `"use client"` directive with App router.

## Compatibility

This plugin currently supports only modern desktop browsers:

- Chrome, Firefox and Edge
- Safari 17.4.1+

## Install the plugin

With your favourite package manager, run the following command:

```bash
yarn add @stream-io/audio-filters-web@beta
# or
npm install @stream-io/audio-filters-web@beta
```

## Integration

Our React SDK provides utility components and hooks that make the integration smoother.

- `NoiseCancellationProvider` - a context provider that takes a `noiseCancellation` instance as a prop
- `useNoiseCancellation()` - a hook that exposes the API that controls the NoiseCancellation behavior, including support and readiness state

```tsx
import { useMemo } from "react";
import { NoiseCancellation } from "@stream-io/audio-filters-web";
import {
  Call,
  NoiseCancellationProvider,
  StreamCall,
  StreamVideo,
  StreamVideoClient,
  useNoiseCancellation,
} from "@stream-io/video-react-sdk";

export const MyApp = () => {
  let client: StreamVideoClient; // ...
  let call: Call; // ...
  const noiseCancellation = useMemo(() => new NoiseCancellation(), []);
  return (
    <StreamVideo client={client}>
      <StreamCall call={call}>
        <NoiseCancellationProvider noiseCancellation={noiseCancellation}>
          <MyComponentTree>
            <MyToggleNoiseCancellationButton />
          </MyComponentTree>
        </NoiseCancellationProvider>
      </StreamCall>
    </StreamVideo>
  );
};

const MyToggleNoiseCancellationButton = () => {
  // isSupported can be `true`, `false` or `undefined`
  // (undefined is used while compatibility check is in progress)
  const { isSupported, isReady, isEnabled, setEnabled } =
    useNoiseCancellation();
  return (
    <button disabled={!isSupported} onClick={() => setEnabled(!isEnabled)}>
      {!isReady ? <LoadingIndicator /> : "Toggle Noise Cancellation"}
    </button>
  );
};
```

### Controlling noise suppression level

By default, the `noiseCancellation` instance will apply the maximum level of noise suppression.
However, in some cases, more granular control is necessary and can be achieved like this:

```ts
const noiseCancellation = new NoiseCancellation();
noiseCancellation.setSuppressionLevel(50); // 0-100, 0 is no suppression, 100 is maximum suppression

// or, by using the hook
const { setSuppressionLevel } = useNoiseCancellation();
setSuppressionLevel(50);
```

## Self-hosting model assets (CSP compatibility)

By default, `@stream-io/audio-filters-web` loads its noise-cancellation model at runtime from the [unpkg.com](https://unpkg.com/) CDN. The default base path resolves to:

```text
https://unpkg.com/@stream-io/audio-filters-web@<version>/src/krispai/models
```

This works out of the box, but it can be a problem if your application enforces a strict [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP). You would have to allowlist `unpkg.com` in your `connect-src` directive, or - preferably - avoid the third-party dependency altogether by hosting the model on your own domain and pointing the plugin to it.

### Step 1 - Copy the model into your app

The model ships inside the `@stream-io/audio-filters-web` package, in its `src/krispai/models` folder. Copy it into your app's public/static assets directory:

```bash
rm -rf public/nc-models
cp -R node_modules/@stream-io/audio-filters-web/src/krispai/models public/nc-models
```

The `rm -rf` keeps the command idempotent: without it, re-running `cp -R` nests the source under the existing folder (`public/nc-models/models/...`) and leaves stale files behind.

### Step 2 - Override `basePath`

Pass a `basePath` (pointing to the directory that serves the copied model files) when creating the `NoiseCancellation` instance. Note that the `public/` directory is served from the web root, so files in `public/nc-models` are exposed at `/nc-models` (no `/public` prefix):

```tsx
const noiseCancellation = useMemo(
  () =>
    new NoiseCancellation({
      basePath: "https://my.domain.com/nc-models",
    }),
  [],
);
```

A same-origin path works too (e.g. `basePath: "/nc-models"`).

### Keep the model in sync with the SDK

The hosted model must match the installed SDK version. To prevent version drift, copy it automatically on install with a `postinstall` script (or a step in your build) in your `package.json`.

```json
{
  "scripts": {
    "postinstall": "node -e \"require('fs').cpSync('node_modules/@stream-io/audio-filters-web/src/krispai/models', 'public/nc-models', { recursive: true })\""
  }
}
```

Once the model is self-hosted and `basePath` is set, the app no longer makes requests to `unpkg.com`, so you can remove it from your CSP allowlist.

## Integration in NextJS

Our `@stream-io/audio-filters-web` runs exclusively on the client and as such, it conflicts with frameworks that are capable
of doing Server Side Rendering (as NextJS).

### App router

Make sure you flag your component that creates the `NoiseCancellation` instance with `"use client"` directive:

```tsx title="app-component.tsx"
"use client";

import { useMemo } from "react";
import { NoiseCancellation } from "@stream-io/audio-filters-web";

export const MyComponent = () => {
  const noiseCancellation = useMemo(() => new NoiseCancellation(), []);
  // ...
};
```

### Pages router

When using the Pages router, we need to lazy-load the plugin as described below:

```tsx title="pages.tsx"
import { useEffect, useRef, useState } from "react";
import { NoiseCancellationProvider } from "@stream-io/video-react-sdk";
import type { INoiseCancellation } from "@stream-io/audio-filters-web";

export const MyComponent = () => {
  const [noiseCancellation, setNoiseCancellation] =
    useState<INoiseCancellation>();
  const loader = useRef<Promise<void>>();
  useEffect(() => {
    const load = (loader.current || Promise.resolve())
      .then(() => import("@stream-io/audio-filters-web"))
      .then(({ NoiseCancellation }) => {
        setNoiseCancellation(new NoiseCancellation());
      });
    return () => {
      loader.current = load.then(() => setNoiseCancellation(undefined));
    };
  }, []);

  return (
    <>
      // ...
      {noiseCancellation && (
        <NoiseCancellationProvider noiseCancellation={noiseCancellation}>
          <MyComponentTree />
        </NoiseCancellationProvider>
      )}
    </>
  );
};
```

---

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/v2/guides/noise-cancellation/](https://getstream.io/video/docs/react/v2/guides/noise-cancellation/).