Skip to content

Enable wasm-bindgen feature with target wasm32-unknown-emscripten - #1349

Open
DouglasDwyer wants to merge 1 commit into
RustAudio:masterfrom
DouglasDwyer:webaudio-reach-any-wasm-target
Open

Enable wasm-bindgen feature with target wasm32-unknown-emscripten#1349
DouglasDwyer wants to merge 1 commit into
RustAudio:masterfrom
DouglasDwyer:webaudio-reach-any-wasm-target

Conversation

@DouglasDwyer

@DouglasDwyer DouglasDwyer commented Sep 1, 2026

Copy link
Copy Markdown

Summary

The wasm-bindgen tool is finally getting support for integration with Emscripten. The feature is still quite new, but this means that most of Rust's web ecosystem can now work with the Emscripten target. I'd like to use cpal in a WASM/Emscripten project, but right now the wasm32-unknown-emscripten target is hard-coded to use the null backend. This PR eliminates the feature gate to make the wasm-bindgen feature work with wasm32-unknown-emscripten too.

This PR exposes the webaudio backend but not the audioworklet backend. That backend relies on re-instantiating the WASM module, but the way Emscripten modules get instantiated is different, so it wouldn't work without more changes.

Changes

  • Replace #[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))] with #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] for the webaudio backend
  • Use Closure::wrap_aborting instead of Closure::wrap so that the code properly compiles on WASM targets with panic=unwind
  • Bump wasm-bindgen dependency to 0.2.110 in order to use Closure::wrap_aborting
  • Add an additional CI job for the Emscripten target

Testing

In my own project, I have gotten cpal audio working with a Rust/Emscripten WASM module in Chrome. This PR also adds CI checks to ensure that compilation is successful.

Related issues

#92 #413 #810

The WebAudio host and its `wasm-bindgen`/`js-sys`/`web-sys` dependencies
were gated on
`all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen")`.
Nothing in the host needs `target_os = "unknown"` -- only `wasm32` and the
browser Web APIs `feature = "wasm-bindgen"` pulls in -- so on
`wasm32-unknown-emscripten` cpal silently fell back to the `null` host.

Drop the `target_os = "unknown"` clause from the WebAudio gates
(host/mod.rs, platform/mod.rs, lib.rs, sample_format.rs, Cargo.toml).
`wasm32-unknown-unknown` still matches unchanged.

The AudioWorklet host stays `wasm32-unknown-unknown`-only: its `worklet.js`
bootstrap re-instantiates the module through wasm-bindgen's ESM
`initSync`, which only exists in the `bundler`/`web` output that
`target_os = "unknown"` implies. Its module gate, its `platform_impl`
entry, and `frames_to_duration` (its sole wasm consumer) keep the
constraint.

Switch the WebAudio host's three JS event closures to
`Closure::wrap_aborting`. `Closure::wrap` requires the closure to be
`UnwindSafe` under `panic = "unwind"`, which is the default only on
`wasm32-unknown-emscripten`; `wrap_aborting` selects the non-catching
invoke shim on every target, identical to what `wrap` already did on the
`panic = "abort"` wasm targets. Raises the minimum `wasm-bindgen` to
0.2.110, where `wrap_aborting` was added.

Add a `wasm-emscripten` job to the platform matrix and a `WASM-emscripten`
clippy entry, mirroring the existing `wasm-bindgen` coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@roderickvd roderickvd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great! Not so long ago we removed the old Emscripten host that had become defunct. This seems like a light-weight manner to get Emscripten support back.

Beyond the changes requested in the review points, please also consider updating README.md with Emscripten support.

Comment thread src/host/webaudio/mod.rs
})
as Box<dyn FnMut(AudioProcessingEvent)>);
as Box<dyn FnMut(AudioProcessingEvent)>;
let on_audio_process = Closure::wrap_aborting(on_audio_process_fn);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap_abort instead of wrap would abort the whole WASM instance for regular WebAudio users, where currently they just get a JS exception.

Comment thread Cargo.toml

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
wasm-bindgen = { version = "0.2", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not necessary for wasm32-wasip1/wasip2.

Comment thread CHANGELOG.md
- `InputCallbackInfo`/`OutputCallbackInfo` merged into `CallbackInfo`.
- `InputStreamTimestamp`/`OutputStreamTimestamp` merged into `StreamTimestamp`; `capture`/`playback` renamed `device`.
- Renamed the `wasm-beep` and `audioworklet-beep` examples to `webaudio` and `audioworklet`.
- **WebAudio**: the host and its `wasm-bindgen`/`js-sys`/`web-sys` dependencies are now gated on `target_arch = "wasm32"` rather than `all(target_arch = "wasm32", target_os = "unknown")`, so the WebAudio host works under `wasm32-unknown-emscripten` (previously it fell back to the `null` host). The AudioWorklet host stays `wasm32-unknown-unknown`-only. Raises the minimum `wasm-bindgen` to 0.2.110.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this consistent with the other lines: short and observable behavior, not implementation details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants