Engines available

EngineQualityBrowser supportWhere it runs
offBrowser-native suppression onlyAllBrowser
krispHighChrome, Edge, Firefox, SafariBundled in the widget
deepfilterVery high (adjustable strength)Chrome, Edge (needs SharedArrayBuffer)DeepFilterNet3 WASM, loaded from CDN

Setting defaults via the appearance API

Audio preferences are per-user, stored in browser localStorage. The character’s widget_appearance controls the default that applies before a user picks their own, and whether the settings drawer is exposed at all.

Hide the user-facing settings drawer

curl -X PATCH https://api.oshara.ai/api/ai-characters/support-bot/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "widget_appearance": {
      "show_audio_settings": false
    }
  }'
Users still get the engine you chose — they just can’t change it.

Audio preferences schema

These are the fields the widget reads from localStorage (and the same fields a custom UI should write):
FieldTypeDefaultDescription
noiseFilter"off" | "krisp" | "deepfilter""off"Active NC engine
deepFilterStrength0–10050Aggressiveness for deepfilter (higher = more removed, +latency)
echoCancellationbooleantrueBrowser-native AEC
noiseSuppressionbooleantrueBrowser-native NS — always on when noiseFilter is "off"
autoGainControlbooleanfalseBrowser-native AGC
voiceIsolationbooleanfalseHardware voice isolation (Chromium/Edge 116+)
headphonesModebooleanfalseDisables half-duplex ducking when user wears headphones
Full schema: Widget → Audio Settings.

Self-hosting the DeepFilterNet3 model

By default the WASM, ONNX, and ESM module load from https://cdn.mezon.ai. Host them yourself for CSP compliance or offline installs:

Step 1 — Download the three files

From https://cdn.mezon.ai:
  • df_bg.wasm
  • DeepFilterNet3_onnx.tar.gz
  • the mezonai-deepfilter ESM module (from https://esm.sh/mezonai-deepfilter)

Step 2 — Serve them from your CDN

Host at matching filenames on https://cdn.yoursite.com/.

Step 3 — Point the widget at your CDN

The widget reads these via data-* attributes on the script tag:
<script
  src="https://api.oshara.ai/widget.js"
  data-agent="support-bot"
  data-deepfilter-wasm-url="https://cdn.yoursite.com/df_bg.wasm"
  data-deepfilter-onnx-url="https://cdn.yoursite.com/DeepFilterNet3_onnx.tar.gz"
  data-deepfilter-module-url="https://cdn.yoursite.com/deepfilternet3.esm.js">
</script>
For a custom UI (no widget), pass the same URLs to your DeepFilterNet3 loader directly.

Implementing NC in a custom UI

If you’re building your own voice UI on top of the LiveKit SDK, integrate the same engines yourself:

Krisp

npm install @livekit/krisp-noise-filter
import { KrispNoiseFilter, isKrispNoiseFilterSupported } from "@livekit/krisp-noise-filter";
import { Room } from "livekit-client";

const room = new Room();
await room.connect(livekitUrl, token);

if (isKrispNoiseFilterSupported()) {
  const audioTrack = room.localParticipant.getTrackPublication("microphone").track;
  await audioTrack.setProcessor(KrispNoiseFilter());
}

DeepFilterNet3

import { DeepFilterProcessor } from "mezonai-deepfilter";

const processor = new DeepFilterProcessor({
  wasmUrl:   "https://cdn.yoursite.com/df_bg.wasm",
  onnxUrl:   "https://cdn.yoursite.com/DeepFilterNet3_onnx.tar.gz",
  strength:  50, // 0–100
});

await audioTrack.setProcessor(processor);

Switching engines at runtime

async function setNoiseFilter(track, mode, strength = 50) {
  await track.stopProcessor();

  if (mode === "krisp" && isKrispNoiseFilterSupported()) {
    await track.setProcessor(KrispNoiseFilter());
  } else if (mode === "deepfilter") {
    await track.setProcessor(new DeepFilterProcessor({ strength, /* urls */ }));
  }
  // mode === "off" → no processor, browser-native NS only
}

Persisting user preferences

Store the user’s choice in localStorage keyed by agent slug so they don’t have to set it every session:
const key = `oshara-audio-prefs:${agentSlug}`;
localStorage.setItem(key, JSON.stringify({
  noiseFilter:        "deepfilter",
  deepFilterStrength: 60,
  echoCancellation:   true,
  noiseSuppression:   true,
}));
This matches the storage layout the widget itself uses, so users who later switch to the widget keep their settings.

Browser compatibility

FeatureChromeFirefoxSafariEdge
Krisp
DeepFilterNet3partial
setSinkId (speaker pick)
Voice isolation
DeepFilterNet3 needs SharedArrayBuffer, which requires these response headers on your page:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp