Engines available
| Engine | Quality | Browser support | Where it runs |
|---|---|---|---|
off | Browser-native suppression only | All | Browser |
krisp | High | Chrome, Edge, Firefox, Safari | Bundled in the widget |
deepfilter | Very 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):
| Field | Type | Default | Description |
|---|---|---|---|
noiseFilter | "off" | "krisp" | "deepfilter" | "off" | Active NC engine |
deepFilterStrength | 0–100 | 50 | Aggressiveness for deepfilter (higher = more removed, +latency) |
echoCancellation | boolean | true | Browser-native AEC |
noiseSuppression | boolean | true | Browser-native NS — always on when noiseFilter is "off" |
autoGainControl | boolean | false | Browser-native AGC |
voiceIsolation | boolean | false | Hardware voice isolation (Chromium/Edge 116+) |
headphonesMode | boolean | false | Disables 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.wasmDeepFilterNet3_onnx.tar.gz- the
mezonai-deepfilterESM module (fromhttps://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-filterimport { 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
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Krisp | ✓ | ✓ | ✓ | ✓ |
| DeepFilterNet3 | ✓ | partial | ✗ | ✓ |
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