wasm-feature-detect: one function per WebAssembly proposal
A small library to detect which features of WebAssembly are supported.
At a glance
- What is it?
- wasm-feature-detect is a small JavaScript library that tells you whether the browser or runtime in front of your user supports a given WebAssembly proposal. Every detector returns a Promise, the whole set adds up to about a kilobyte gzipped, and you pay only for the detectors you import.
- Who is it for?
- wasm-feature-detect fits any project that ships WebAssembly to a mixed audience, since it turns a support question into a single awaited call per proposal and keeps the bundle cost close to a kilobyte. It is the wrong dependency for a project with one fixed target environment, where a compile-time flag on the Wasm toolchain settles the question without a runtime check.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A support question that keeps changing
WebAssembly is not one thing. It is a core specification plus a long tail of proposals, each of which ships in browsers and runtimes on its own schedule. A module compiled with SIMD instructions, a tail call, reference types or multiple memories will fail to instantiate on an engine that does not implement that proposal, and it will fail at run time in the user's browser rather than on your build machine.
That is the problem wasm-feature-detect addresses. It is described in the README as a small library to detect which features of WebAssembly are supported, and the package description in package.json is more specific: which features are supported in your current browser. The homepage link points at the WebAssembly roadmap, which is the right place to look for where a given proposal currently stands in the specification process.
The audience is web developers and library authors who ship WebAssembly and need to choose between a fast path and a fallback without guessing. The design goal, stated in the README as a list, is a library that runs in browsers, Node and Deno, is tree-shakable so that only the detectors you use end up in the bundle, ships as an ES6, CommonJS and UMD module, stays compatible with a strict Content Security Policy, and where all detectors add up to only about 1020 bytes gzipped.
That last number is the one that makes the library practical to adopt. A support check that costs a kilobyte is a decision you can make at the top of a feature module without thinking about it.
One detector per proposal, all returning Promise<bool>
The detector surface is a flat table of named functions, one per proposal. The README lists them alongside the proposal each one covers: bigInt, bulkMemory, exceptions, exceptionsFinal, extendedConst, gc, jsStringBuiltins, jspi, memory64, multiMemory, multiValue, mutableGlobals, referenceTypes, relaxedSimd, saturatedFloatToInt, signExtensions, simd, streamingCompilation, tailCall, threads, typeReflection, typedFunctionReferences and wideArithmetic.
Two details in that table matter for how you plan an integration. First, the list is not limited to the long-established proposals. It includes several that are still moving through the specification process, such as exceptionsFinal for exception handling with exnref, typedFunctionReferences, typeReflection and wideArithmetic. Detecting support for a proposal that is not yet shipped is exactly the case where a runtime check earns its keep, since no amount of user agent parsing will tell you.
Second, the granularity is coarse in one specific place. The exceptions entry covers legacy exception handling and exceptionsFinal covers the newer exnref-based scheme, so a project that cares about the distinction has to check the one it actually needs rather than assuming one covers the other.
All detectors return a Promise<bool>. There is no synchronous variant and no callback form, which means every call site has to be in an async context.
Why the tests are asynchronous on purpose
The README has a section titled Why are all the tests async, and it gives two reasons that are worth reading separately because they argue different things.
The first is technical and forward-looking. Some tests might have to become asynchronous in future, and the example given is Firefox, which the README says is planning a change that would require a postMessage call to detect SABs, which in turn are required for the threads detector. A detector written synchronously today could not be retrofitted to that change without breaking its callers. Making everything async from the start keeps the API stable when the underlying detection mechanism changes.
The second reason is about usage discipline. The README argues that you should be using WebAssembly.compile, WebAssembly.instantiate, or their streaming versions WebAssembly.compileStreaming and WebAssembly.instantiateStreaming, all of which are asynchronous. Code that loads WebAssembly is already async code, so a detection API that forces an await fits the surrounding code instead of fighting it.
There is a practical consequence for callers: you cannot branch on a detector inside a synchronous function body, and in a hot path you pay for a promise per check. For a startup feature decision that is irrelevant. For a check in a tight loop it would be, which is not a use case this library is aimed at.
Module formats, tree-shaking and CSP
The packaging in package.json is designed for a library rather than for a command line tool, even though the README install line reads npm install -g wasm-feature-detect. The manifest declares the package as an ES module with an exports map that resolves to an ESM build for import, a CommonJS build for require, and types for both, plus a browser condition pointing at the ESM build. The main and module fields point at the same dist output, and only the dist directory is published to npm.
That exports map is what makes the tree-shaking claim work. Because the package is ESM and each detector is a separate named export, a bundler can drop the detectors you never import. The README makes the same point in plain language: only bundle the detectors you use. The cost of that design is that you get ES6, CommonJS and UMD builds of a package whose main entry point is still consumed by name.
The Content Security Policy line in the README is worth understanding too, because it constrains how the library can be delivered. The hotlinking example points at unpkg with the ?module query parameter, which serves an ES module rather than a classic script, and the UMD example loads a plain script tag from the same CDN path. Neither pattern requires inline script execution in the page, which is what keeps a strict CSP without unsafe-inline satisfied.
If you self-host, the constraint is simply that the shipped files must not need eval or injected script to work.
Installing the package and calling a detector
The README installation section is a single line, which installs the package globally on the command line:
npm install -g wasm-feature-detectFor use inside a project you would add it as a dependency instead, and import the detectors you need. The usage example in the README is an ES module script that imports simd and awaits the detector:
<script type="module">
import { simd } from "wasm-feature-detect";
if (await simd()) {
/* SIMD support */
} else {
/* No SIMD support */
}
</script>You should see the branch resolve to the SIMD path on an engine with fixed-width SIMD support, and to the fallback otherwise. The pattern generalises: call the detector for the proposal your module needs, and only then load the specialised build.
The hotlinking variant skips the install step entirely and pulls the module from a CDN:
<script type="module">
import { simd } from "https://unpkg.com/wasm-feature-detect?module";
// ...
</script>And if your page cannot use modules, the UMD build exposes the same functions on a global:
<script src="https://unpkg.com/wasm-feature-detect/dist/umd/index.js"></script>
<script>
if (await wasmFeatureDetect.simd()) {
// ...
}
</script>In that UMD example the global is wasmFeatureDetect, and the call is still awaited.
Adding a detector means adding a folder
The contributing section describes the extension mechanism, which is unusually mechanical. To add a feature test you create a new folder in src/detectors and it is picked up automatically. The folder may contain a module.wat file, which gets compiled using wabt.js, and it may contain an optional index.js file whose default export must be an async function that performs additional testing in JavaScript and returns a boolean. The folder must contain at least one of the two.
The threads detector is the named example of the JavaScript-only path, which fits, since detecting shared array buffers needs more than a module compile. The WebAssembly text template carries three header comments that the tooling reads: a name for the README, a link to the proposal explainer or repository, and a space-separated list of WasmFeatures from wabt.js.
;; Name: <Name of the feature for the README>
;; Proposal: <Link to the proposal’s explainer/repo>
;; Features: <Space-separated list of WasmFeatures from wabt.js>
(module
;; More WAT code here
)Those comments are not decoration. The README is generated from the detector folders, with render-readme.mjs producing the table and render-dts.mjs producing the type declarations, so a missing or malformed header shows up as a broken entry in the published documentation rather than as a runtime error.
The build itself runs through Rollup, with separate scripts for the library, the README and the type declarations, and the test command runs the formatter check, the build and then the test suite in test/index.cjs.
Version history, licence, and the alternative of not checking
The release record is sparse. Tagged versions include v1.7.0 on 2024-09-17, v1.8.0 on 2024-09-18 and v1.9.0 on 2026-09-14. The repository is not archived and the last push was on 2026-09-30, so work continued after the latest tag, though the gap between v1.8.0 and v1.9.0 shows that this is not a project that ships every week. The licence is Apache-2.0, stated in the README and in the package.json license field, with the LICENSE file at the repository root.
The alternative worth weighing is writing the check yourself. Each proposal needs a small module that either compiles or does not, plus a call to WebAssembly.validate or an instantiate attempt, and you would end up maintaining that code, its packaging and its documentation. The library's contribution is precisely the packaging and the per-proposal naming, at about 1020 bytes gzipped for everything it ships.
A second alternative is to skip runtime detection entirely. If you control the deployment, you can compile the Wasm with the flags for the features you want and ship one build to everyone, or you can decide that the minimum browser version of your product already implies the proposals you need. In that case a runtime check is dead weight, and a build-time flag is the more honest answer.
The rule of thumb is that detection earns its place when the same bundle reaches engines you do not control. Where you do control them, the detector table is still a useful checklist of what the specification currently offers, including the proposals such as typeReflection and wideArithmetic that are still moving.
Editorial conclusion
wasm-feature-detect fits any project that ships WebAssembly to a mixed audience, since it turns a support question into a single awaited call per proposal and keeps the bundle cost close to a kilobyte. It is the wrong dependency for a project with one fixed target environment, where a compile-time flag on the Wasm toolchain settles the question without a runtime check. Before adopting, confirm the proposals you depend on appear in the detector table, remember that a detection result reflects the runtime in front of you and not the browser a user might switch to, and note that the newest published version is v1.9.0 from 2026-09-14 with the last push on 2026-09-30.
Frequently asked questions
How do I check whether a browser supports WebAssembly SIMD?
Import the simd detector and await it. The README example imports { simd } from "wasm-feature-detect" and branches on if (await simd()). All detectors return a Promise that resolves to a boolean.
Which WebAssembly proposals does wasm-feature-detect cover?
The README lists detectors for proposals including bigInt, bulkMemory, exceptions, exceptionsFinal, extendedConst, gc, jsStringBuiltins, jspi, memory64, multiMemory, multiValue, mutableGlobals, referenceTypes, relaxedSimd, saturatedFloatToInt, signExtensions, simd, streamingCompilation, tailCall, threads, typeReflection, typedFunctionReferences and wideArithmetic.
Why are the wasm-feature-detect detectors asynchronous?
The README gives two reasons: some tests may have to become asynchronous later, for example a planned Firefox change that would need a postMessage call to detect SABs for the threads test, and because WebAssembly.compile, WebAssembly.instantiate and their streaming versions are asynchronous anyway.
How large is wasm-feature-detect and how do I install it?
The README states that all detectors add up to only about 1020 bytes gzipped and shows npm install -g wasm-feature-detect as the install command. It is tree-shakable, so bundling only the detectors you import costs less, and it ships as ES6, CommonJS and UMD modules.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/googlechromelabs-wasm-feature-detect)Was this useful?