WebAssembly Module Inspector
A WebAssembly module is eight bytes of preamble — the magic \0asm and a version — followed by a sequence of sections, each one an id byte, a LEB128 length and exactly that many bytes. This inspector walks that sequence locally and reports everything the module declares about itself: the version, every section in file order with its byte size, offset and item count, the imports and exports with their full types, the memory limits in pages and in bytes, the start function if there is one, and the custom sections at the end. The size breakdown is usually why a binary gets opened at all. In most builds the code section is the bulk of the file, but that is not a rule: a Go module carries a data section that can run past a megabyte, an emscripten build with embedded assets can be mostly data, and a debug build hides its weight in custom sections that never affect what the module does. Imports are the contract with the host — emscripten output asks for env and wasi_snapshot_preview1, wasm-bindgen output asks for a JavaScript glue file by path, a Go build asks for gojs — and a single unsatisfied entry is the LinkError you get at instantiation. Exports are the surface you can call, and each signature is read out of the type section rather than guessed from the name. In a stripped binary two custom sections carry more than everything else: name, which restores the module and function names a debug build kept, and producers, which records the compiler and the tools that touched the file. Both are decoded here when they are present. What this cannot tell you is what the module does. The code section is measured, never disassembled, and nothing is executed, so a proposal used only inside a function body — SIMD arithmetic, atomics, tail calls — leaves no trace unless it also appears in a signature, a limits flag or a section id. The features listed are only the ones the encoding actually proves.
How to use
- Drop a .wasm file onto the box, or click it to pick one. The file is read in the page and never leaves your browser.
- Start with the section table: it gives every section's byte size and share of the file, which is the answer to "why is this module three megabytes".
- Read the imports as the contract the host has to satisfy — module, name, kind and the exact signature each one expects.
- Read the exports as the API you can call from JavaScript, with parameter and result types taken from the module's own type section.
- Check the memory panel for the initial and maximum page counts (a page is 65,536 bytes), and the custom sections for a name or producers section that tells you which toolchain built the file.
Frequently asked questions
- Why is my .wasm file so big?
- Look at the section table first. In a typical Rust or C build the code section is 70-90% of the file and the honest fix is less code: fewer generic instantiations, no panic formatting, wasm-opt -Oz. But the breakdown is often a surprise. Go builds ship a large data section because the runtime's static data is compiled in; emscripten builds that embed files can be mostly data; and a build that kept DWARF debug information carries it in custom sections called .debug_info and friends, which are pure overhead at runtime and are what wasm-strip removes. The percentages here tell you which of those you are looking at before you start changing compiler flags.
- What does a memory of "17..∞ pages" mean?
- WebAssembly memory is counted in pages of 65,536 bytes, so an initial size of 17 pages is about 1.1 MB reserved the moment the module is instantiated. The second number is the declared maximum: when it is absent the module may grow its memory as far as the host allows, which for 32-bit WebAssembly is 65,536 pages, or 4 GiB. A declared maximum is not a promise of frugality — it is a ceiling the host may pre-reserve address space for. A shared memory, used with threads, is the one case where the maximum is mandatory.
- Why are all the imports and exports called a, b, c?
- That is emscripten with optimisation and Closure enabled: import and export names are strings in the binary, so shortening them shrinks the file, and the JavaScript glue that ships alongside is minified to match. Nothing is lost for the runtime, but the module is unreadable on its own. The name custom section, which maps indices back to source-level function names, is stripped in the same pass. When it survives, this tool decodes it; when it does not, the producers section is often the only thing left that says who built the file.
- The module imports env and wasi_snapshot_preview1 — what do I have to provide?
- Every import is an entry your host object must fill in, matched by module name and field name, with a type the engine checks at instantiation. env is emscripten's catch-all namespace for the C runtime callbacks it expects from JavaScript; wasi_snapshot_preview1 means the module was built against WASI and wants a system interface, so it needs a WASI shim in the browser; gojs is the Go runtime's bridge; a path like ./thing_bg.js is wasm-bindgen expecting its generated glue module. If one entry is missing or has the wrong arity, WebAssembly.instantiate throws a LinkError naming it, which is exactly the list shown here.
- Does this run the module, or disassemble it?
- Neither. It parses the module's declarations: sections, types, imports, exports, limits, the start function index and the custom sections. Function bodies are counted and their total size is reported, but their instructions are never decoded, and no engine is instantiated, so a malicious module cannot do anything here. The trade-off is that behaviour is invisible: this cannot tell you what a function computes, which imports are really called, how much memory the module ends up using, or whether SIMD and atomic instructions appear inside a body when they never show up in a type.
- How can it say a module uses reference types or multi-value if it does not read the code?
- Because some post-MVP proposals change the shape of the encoding itself, not just the instruction set. A function type with two or more results is multi-value; an externref in a signature or a second table is reference types; the third bit of a memory's limits flags is the shared bit that threads require; a data count section, or a data or element segment that is passive rather than active, only exists under bulk memory; a v128 in a signature is SIMD. Those are stated plainly because the bytes prove them. A feature that only ever appears as an opcode inside a function body is not claimed at all, which is why the list can be shorter than what the module really uses.
Related tools
GIF Structure Inspector
Open a .gif and read its blocks: frame delays and disposal, loop behaviour, palettes, transparency, and how many bytes each frame really costs.
EditorConfig Tester
Paste an .editorconfig and some file paths: see the property set each file resolves to, and which section on which line decided every value.
.gitignore Tester
Paste a .gitignore and a list of paths to see which are ignored and exactly which line decided — the answer git check-ignore -v gives.
ELF Binary Inspector
Open a Linux executable, .so or .o in your browser: architecture, shared libraries it needs, build ID, and whether it is PIE, NX and RELRO hardened.
Font File Inspector
Open a .ttf, .otf or .woff and read its real family name, glyph and character counts, version, licence text and embedding permissions.
JSON Schema Validator
Check a JSON document against a JSON Schema in your browser. Every error gets its JSON Pointer, the line it lands on, and the keyword that failed.