{"id":50271115,"url":"https://github.com/openclaw/libopus-wasm","last_synced_at":"2026-06-01T23:00:30.927Z","repository":{"id":360457075,"uuid":"1249699859","full_name":"openclaw/libopus-wasm","owner":"openclaw","description":"Small, modern WASM bindings for libopus raw packet encode/decode.","archived":false,"fork":false,"pushed_at":"2026-05-28T22:36:21.000Z","size":230,"stargazers_count":80,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-31T22:02:54.414Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://libopus-wasm.dev","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openclaw.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["moltbot"]}},"created_at":"2026-05-26T00:59:23.000Z","updated_at":"2026-05-31T17:12:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openclaw/libopus-wasm","commit_stats":null,"previous_names":["openclaw/libopus-wasm"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/openclaw/libopus-wasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Flibopus-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Flibopus-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Flibopus-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Flibopus-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openclaw","download_url":"https://codeload.github.com/openclaw/libopus-wasm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openclaw%2Flibopus-wasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33797128,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-27T18:00:23.469Z","updated_at":"2026-06-01T23:00:30.904Z","avatar_url":"https://github.com/openclaw.png","language":"JavaScript","funding_links":["https://github.com/sponsors/moltbot"],"categories":[],"sub_categories":[],"readme":"# libopus-wasm\n\n[![CI](https://github.com/openclaw/libopus-wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/openclaw/libopus-wasm/actions/workflows/ci.yml)\n[![Docs](https://img.shields.io/badge/docs-libopus--wasm.dev-6d4aff)](https://libopus-wasm.dev)\n\nSmall, modern WebAssembly bindings for [libopus](https://opus-codec.org/) raw\npacket encode/decode. One single-file ES module that runs unchanged in browsers\nand Node — no `locateFile` hook, no second `.wasm` request, no native build step.\n\nThe default path is realtime voice: 48 kHz, stereo, 20 ms frames, raw Opus\npackets, no Ogg/WebM container layer.\n\n- **Browser and Node** from one import. Bundles cleanly with Vite, webpack, esbuild.\n- **Int16 and Float32 PCM** — use whatever your pipeline already speaks.\n- **Loss-resilient** — in-band FEC and packet-loss concealment.\n- **Tunable** — bitrate, VBR/CBR, complexity, signal, bandwidth, DTX, plus a curated CTL passthrough.\n- **Drop-in `@discordjs/opus` adapter** — same method shape, no node-gyp.\n\n📖 **Full documentation: [libopus-wasm.dev](https://libopus-wasm.dev)**\n\n## Install\n\n```bash\nnpm install libopus-wasm\n```\n\nESM-only; Node 20+ or any current browser. No `@types` install needed.\n\n## Quick start\n\n```ts\nimport { createDecoder, createEncoder, getPacketInfo } from \"libopus-wasm\";\n\nconst encoder = await createEncoder(); // 48 kHz, stereo, 20 ms, audio\nconst decoder = await createDecoder();\n\nconst pcm = new Int16Array(encoder.frameSize * encoder.channels); // 960 * 2\nconst packet = encoder.encode(pcm);    // Uint8Array — one raw Opus packet\nconst info = await getPacketInfo(packet); // duration, frames, bandwidth\nconst frame = decoder.decode(packet);  // Int16Array — interleaved PCM\n\nencoder.free();\ndecoder.free();\n```\n\nBoth factories share one lazily-loaded WASM module; the first call pays the load\ncost and the rest are cheap.\n\n## Examples\n\n### Float32 PCM\n\nEncode and decode floats directly — ideal for Web Audio:\n\n```ts\nconst frame = new Float32Array(encoder.frameSize * encoder.channels); // [-1, 1]\nconst packet = encoder.encodeFloat(frame);\nconst decoded = decoder.decodeFloat(packet); // Float32Array\n```\n\n### Batches\n\n```ts\nconst packets = encoder.encodeFrames([frameA, frameB, frameC]); // Uint8Array[]\nconst frames = decoder.decodeFrames(packets);                   // Int16Array[]\n```\n\n### Packet loss: FEC + concealment\n\n```ts\n// Encoder: enable in-band FEC and declare the expected loss rate.\nconst encoder = await createEncoder({ fec: true, packetLossPercent: 15 });\n\n// Decoder: a packet is lost. If the next packet is in hand, recover from its\n// FEC data; otherwise synthesize a concealment frame.\nconst recovered = decoder.decode(nextPacket, { decodeFec: true, frameSize: 960 });\nconst concealed = decoder.decodePacketLoss(960); // == decode(null, { frameSize: 960 })\n```\n\nSee [Packet loss](https://libopus-wasm.dev/packet-loss.html) for the full receive loop.\n\n### Tuning the encoder\n\n```ts\nconst encoder = await createEncoder({\n  application: Application.Audio,\n  bitrate: 96000,        // or \"auto\" / \"max\"\n  complexity: 10,        // 0..10\n  signal: Signal.Music,\n  vbr: true,\n});\n\nencoder.setBitrate(128000);\nencoder.setMaxBandwidth(Bandwidth.Wideband);\nencoder.getBitrate();    // 128000\n```\n\n### Deterministic cleanup with `using`\n\n```ts\n{\n  using encoder = await createEncoder();\n  using decoder = await createDecoder();\n  decoder.decode(encoder.encode(new Int16Array(960 * 2)));\n} // both freed automatically at scope exit\n```\n\n## discord.js compatibility\n\n`libopus-wasm/discordjs` matches the [`@discordjs/opus`](https://github.com/discordjs/opus)\nmethod shape, minus the native toolchain. It is Node-only (uses `Buffer`) and\nloads asynchronously:\n\n```ts\nimport { OpusEncoder } from \"libopus-wasm/discordjs\";\n\nconst opus = await OpusEncoder.create(48000, 2);\nconst packet = opus.encode(pcmBuffer);\nconst decoded = opus.decode(packet);\nopus.setBitrate(64000);\nopus.setFEC(true);\nopus.free();\n```\n\nOr construct directly and await `ready` to keep existing call sites:\n\n```ts\nconst opus = new OpusEncoder(48000, 2);\nawait opus.ready;\n```\n\nMore in [discord.js compatibility](https://libopus-wasm.dev/discordjs.html).\n\n## Browser\n\nThe main entry inlines the WASM, so it bundles with no plugins and needs no\ncross-origin isolation. Web Audio delivers Float32 samples that go straight into\n`encodeFloat` — see [Browser usage](https://libopus-wasm.dev/browser.html) for a\nmicrophone-capture walkthrough.\n\n## API overview\n\nFull reference with every option and constant lives at\n[libopus-wasm.dev/api-reference](https://libopus-wasm.dev/api-reference.html).\n\n### Top-level\n\n| Function | Returns | Description |\n| --- | --- | --- |\n| `loadLibopus()` | `Promise\u003c{ version }\u003e` | Loads the module; returns the bundled libopus version. |\n| `createEncoder(options?)` | `Promise\u003cOpusEncoderHandle\u003e` | Create a raw-packet encoder. |\n| `createDecoder(options?)` | `Promise\u003cOpusDecoderHandle\u003e` | Create a raw-packet decoder. |\n| `getPacketInfo(packet, options?)` | `Promise\u003cOpusPacketInfo\u003e` | Validate a raw packet and return duration, frame count, channels, and bandwidth. |\n\n### Encoder\n\n| Member | Description |\n| --- | --- |\n| `encode(pcm, options?)` | Encode one Int16 frame (`Int16Array \\| Uint8Array`) → `Uint8Array`. |\n| `encodeFloat(pcm, options?)` | Encode one `Float32Array` frame → `Uint8Array`. |\n| `encodeFrames` / `encodeFloatFrames` | Batch variants → `Uint8Array[]`. |\n| `setBitrate` / `getBitrate` | Bitrate (`number \\| \"auto\" \\| \"max\"`). |\n| `setComplexity` `setSignal` `setMaxBandwidth` | Quality and bandwidth controls. |\n| `setVbr` `setVbrConstraint` `setDtx` | Rate-mode controls. |\n| `setFec` `setPacketLossPercent` | Loss-resilience controls. |\n| `getLookahead` `getInDtx` | Encoder state. |\n| `encoderCtl(request, value)` | Curated integer-setter [CTL passthrough](https://libopus-wasm.dev/ctl.html). |\n| `free()` / `[Symbol.dispose]()` | Release WASM memory. |\n\nRead-only: `application`, `channels`, `frameSize`, `sampleRate`.\n\n### Decoder\n\n| Member | Description |\n| --- | --- |\n| `decode(packet, options?)` | Decode a packet (or `null` for PLC) → `Int16Array`. |\n| `decodeFloat(packet, options?)` | Decode → `Float32Array`. |\n| `decodeFrames` / `decodeFloatFrames` | Batch variants; `null` entries are concealed. |\n| `decodePacketLoss(frameSize?)` | Synthesize one concealment frame. |\n| `decodePacketLossFloat(frameSize?)` | Float32 variant. |\n| `decoderCtl(request, value)` | Integer-setter CTL passthrough. |\n| `free()` / `[Symbol.dispose]()` | Release WASM memory. |\n\nRead-only: `channels`, `maxFrameSize`, `sampleRate`.\n\n### Constants\n\n`Application` (`Voip`, `Audio`, `RestrictedLowDelay`) ·\n`Signal` (`Auto`, `Voice`, `Music`) ·\n`Bitrate` (`Auto`, `Max`) ·\n`Bandwidth` (`Narrowband`…`Fullband`) ·\n`EncoderCtl` / `DecoderCtl` request codes ·\n`OpusError` (`code`, `operation`).\n\n### Supported formats\n\n| Constraint | Allowed values |\n| --- | --- |\n| Sample rate | `8000`, `12000`, `16000`, `24000`, `48000` Hz |\n| Channels | `1` (mono), `2` (stereo) |\n| Encode frame duration | `2.5`, `5`, `10`, `20`, `40`, `60` ms |\n| Decode output capacity | up to `120` ms |\n| PLC / FEC frame size | multiples of `2.5` ms, up to `120` ms |\n\nValidation errors (wrong frame size, out-of-range option, empty packet,\nnon-allow-listed CTL) throw a `RangeError` before reaching WASM; libopus errors\nsurface as `OpusError`.\n\n## Build from source\n\nThe npm package ships compiled output, so using it needs no toolchain. Building\nfrom source requires Emscripten (`emcc`) on `PATH`:\n\n```bash\npnpm install\npnpm build\npnpm test\n```\n\n`pnpm build` downloads `libopus 1.6.1` from Xiph.Org, verifies the pinned\nSHA-256, compiles it with Emscripten, and emits a single-file ES module under\n`dist/generated/`. See [Building from source](https://libopus-wasm.dev/building.html).\n\n## Benchmark\n\nNative comparison requires `@discordjs/opus` to build on the host:\n\n```bash\npnpm benchmark\n```\n\nApple Silicon, Node 26, 20k iterations, 48 kHz stereo, 20 ms frames:\n\n```text\nwasm encode:   15,304 ops/sec\nnative encode: 15,741 ops/sec\nwasm decode:   38,416 ops/sec\nnative decode: 41,280 ops/sec\n```\n\nA regression check, not a portable score. CI also exposes a manual `Benchmark`\nworkflow. More in [Benchmark](https://libopus-wasm.dev/benchmark.html).\n\n## Documentation \u0026 license\n\nFull docs: **[libopus-wasm.dev](https://libopus-wasm.dev)**. Released under the\n[MIT license](LICENSE); libopus carries its own BSD license, reproduced in\n[THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES.md). Not affiliated with Xiph.Org.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Flibopus-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenclaw%2Flibopus-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenclaw%2Flibopus-wasm/lists"}