File types · Explainer

The WebAssembly file (.wasm)

A WASM file is a compiled WebAssembly module: compact binary code that browsers, Node.js and other runtimes check and run inside a sandbox, usually alongside JavaScript. This guide explains what's inside one byte by byte, how pages load it, the versions and the errors worth knowing, and has free sample WebAssembly files to download for testing.

At a glance

Full name
WebAssembly binary module
Extensions
.wasm; the text format uses .wat
MIME type
application/wasm, registered with IANA in 2021
Kind
Binary: compiled code for a virtual stack machine
Standard
W3C WebAssembly Working Group. Wasm 1.0 is a W3C Recommendation; Wasm 3.0 (2025) is the current standard
First released
2017, when the first version shipped in the major browsers; W3C Recommendation on 5 December 2019
Runs in
Every major browser, Node.js, Deno, Wasmtime and edge platforms such as Cloudflare Workers
Memory
Linear memory in 64 KiB pages; 32-bit addresses unless the module uses Wasm 3.0's 64-bit memory
Opens with
Runtimes run it; wasm2wat (WABT) or wasm-tools print turn it into readable text
Magic number
00 61 73 6D (\0asm), then the version 01 00 00 00

The basics

What is a WASM file?

A .wasm file holds a WebAssembly module: functions compiled into a compact binary instruction format, plus a description of what the module needs from outside (imports) and what it offers (exports). It is the output of a compiler. People write C, C++, Rust, Go, C#, Zig or AssemblyScript, and the toolchain produces the .wasm file.

Browsers run it next to JavaScript, which is why photo and video editors, games, CAD tools, PDF viewers and databases can run inside a web page at speeds close to native code. The W3C called it the fourth language of the web, after HTML, CSS and JavaScript, when it became a Recommendation in 2019. The same files also run on servers, at the network edge and inside other applications as plug-ins.

Three things a .wasm file is not:

  • Not a replacement for JavaScript. In a browser, a module can't touch the page, the network or storage by itself. JavaScript loads it, passes it the functions it imports and calls what it exports.
  • Not machine code. The bytes target a virtual stack machine, not a real processor. The engine validates the module and compiles it to the processor it is running on, so one file runs on x86 and ARM alike.
  • Not readable text. Open it in a text editor and you see a few recognisable words (export names, embedded strings) among symbols, because most bytes are opcodes and numbers. The readable form is a separate text format, WAT.

Anatomy

What's inside a .wasm file, byte by byte

A module is a short preamble followed by sections. Each section starts with a one-byte id and its length, so an engine can find or skip any of them. Here is our 262-byte sample-module.wasm, compiled from the sample-module.wat source below, split at each section boundary. Numbers are stored in LEB128, a variable-length encoding where small values take one byte.

sample-module.wasm (hex, by section)
offset  bytes
; preamble: magic number, then version
000000  00 61 73 6D
000004  01 00 00 00
; section 1 (type), 20 bytes
000008  01 14 04 60 02 7F 7F 00 60 02 7F 7F 01 7F 60 01
000018  7E 01 7E 60 00 00
; section 2 (import), 11 bytes
00001E  02 0B 01 03 65 6E 76 03 6C 6F 67 00 00
; section 3 (function), 4 bytes
00002B  03 04 03 01 02 03
; section 5 (memory), 3 bytes
000031  05 03 01 00 01
; section 6 (global), 6 bytes
000036  06 06 01 7F 00 41 28 0B
; section 7 (export), 36 bytes
00003E  07 24 04 06 6D 65 6D 6F 72 79 02 00 03 61 64 64
00004E  00 01 09 66 61 63 74 6F 72 69 61 6C 00 02 05 67
00005E  72 65 65 74 00 03
; section 10 (code), 58 bytes
000064  0A 3A 03 07 00 20 00 20 01 6A 0B 27 01 01 7E 42
000074  01 21 01 02 40 03 40 20 00 42 01 57 0D 01 20 01
000084  20 00 7E 21 01 20 00 42 01 7D 21 00 0C 00 0B 0B
000094  20 01 0B 08 00 41 00 23 00 10 00 0B
; section 11 (data), 46 bytes
0000A0  0B 2E 01 00 41 00 0B 28 48 65 6C 6C 6F 20 66 72
0000B0  6F 6D 20 39 39 68 65 6C 70 65 72 73 2E 63 6F 6D
0000C0  2F 66 69 6C 65 2D 74 79 70 65 73 2F 77 61 73 6D
; section 0 (custom), 52 bytes
0000D0  00 34 05 61 62 6F 75 74 46 72 65 65 20 73 61 6D
0000E0  70 6C 65 20 66 72 6F 6D 20 39 39 68 65 6C 70 65
0000F0  72 73 2E 63 6F 6D 2F 66 69 6C 65 2D 74 79 70 65
000100  73 2F 77 61 73 6D
  1. 100 61 73 6D

    The magic number

    A zero byte, then the ASCII letters asm. Every core WebAssembly module starts with these four bytes; an engine that doesn't find them stops at once.

  2. 201 00 00 00

    The version

    A 32-bit little-endian 1. It stayed 1 through Wasm 2.0 and 3.0, because the newer versions only add features. WebAssembly components (see below) use a different value here.

  3. 301 14

    A section header

    Id 01 (the type section) and a length of 0x14, 20 bytes. Known sections appear in a fixed order and at most once each; custom sections, id 00, can go anywhere.

  4. 460 02 7F 7F 00

    Type section: function signatures

    60 introduces a function type: 2 parameters, both 7F (i32), and 0 results. That's the signature of env.log. The other three types use 7E (i64) for factorial and none at all for greet.

  5. 503 65 6E 76 03 6C 6F 67 00 00

    Import section

    The module needs a function the host must provide: module name env, field log, kind 00 (function) with type 0. Names are length-prefixed UTF-8. Instantiation fails if the host doesn't pass it.

  6. 603 04 03 01 02 03

    Function section

    Declares 3 functions with type indices 1, 2 and 3. Imports are numbered first, so log is function 0 and add is function 1. The bodies come later, in the code section, so engines can start compiling them in parallel.

  7. 705 03 01 00 01

    Memory section

    One memory, flag 00 (no maximum), starting at 1 page of 64 KiB. This is the module's linear memory: one flat, resizable array of bytes that only the module and its host can see.

  8. 806 06 01 7F 00 41 28 0B

    Global section

    One immutable i32 global. Its initial value is a tiny expression: 41 28 is i32.const 40, and 0B ends it.

  9. 906 6D 65 6D 6F 72 79 02 00

    Export section

    What the outside world can use: memory (kind 02), then add, factorial and greet (kind 00, functions 1 to 3). JavaScript sees these as instance.exports.

  10. 1007 00 20 00 20 01 6A 0B

    Code section: the body of add

    7 bytes, no extra locals, then the instructions: local.get 0, local.get 1, i32.add, end. Common instructions are a single opcode byte, which is what keeps modules small.

  11. 1100 41 00 0B 28 48 65 6C 6C 6F …

    Data section

    An active segment for memory 0, placed at offset i32.const 0: 40 (0x28) bytes of text, Hello from 99helpers.com/file-types/wasm, copied into memory when the module is instantiated.

  12. 1200 34 05 61 62 6F 75 74 …

    A custom section

    Id 0, a name (about) and any bytes. Engines ignore custom sections. Toolchains use them for function names (name), compiler details (producers) and DWARF debug information.

Under the hood

How an engine runs a .wasm file

  1. 1Decode. The engine checks the magic number and version, then reads the sections in order. Anything malformed is a CompileError before a single instruction runs.
  2. 2Validate. Every function is type-checked: each instruction must find values of the right type on the stack, and every branch must target an enclosing block. Code that fails can't be run at all.
  3. 3Compile. The engine turns the instructions into native code for the current processor. Browsers can do this while the file is still downloading, which is what instantiateStreaming is for.
  4. 4Instantiate. The host supplies the imports; the engine creates the memory, tables and globals, copies data segments into memory and runs the start function, if there is one.
  5. 5Call. The host calls exported functions. Values cross the boundary as numbers (i32, i64, f32, f64) or references; strings and arrays travel through linear memory as bytes, which glue code generated by the toolchain usually handles.

Why validation matters

Because every module is checked before it runs, a WebAssembly function can't jump into the middle of other code or call a function with the wrong signature. Out-of-bounds memory access stops execution with a trap instead of reading someone else's data.

Text format

WAT: the text form of a .wasm file

Every module has an equivalent text representation, the WebAssembly Text Format (.wat), written as S-expressions in brackets. It's what you read when you disassemble a .wasm file and what you write when you hand-code a small module. This is the add function from the anatomy above:

add.wat
(module
  (func (export "add") (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add))
  • WAT to WASM: wat2wasm add.wat from WABT, or wasm-tools parse add.wat -o add.wasm.
  • WASM to WAT: wasm2wat file.wasm or wasm-tools print file.wasm. Comments and local variable names are lost unless the file has a name custom section; you get $var0 and func 3 instead.
  • .wast is a superset of WAT used by the specification's test suite, with assertions about what each module should return or reject.
  • wasm-objdump -x file.wasm lists every section, import and export, which is the quickest way to see what a module needs before you try to run it.

Loading it

Loading a .wasm file in a web page

A page fetches the module and instantiates it through the JavaScript API, passing an object with everything the module imports:

app.js
const imports = { env: { log: (ptr, len) => { /* … */ } } };

const { instance } = await WebAssembly.instantiateStreaming(
  fetch("sample-module.wasm"), // must arrive as application/wasm
  imports,
);

instance.exports.add(2, 3); // 5
  • The MIME type is checked. WebAssembly.instantiateStreaming and compileStreaming reject a response unless its Content-Type is exactly application/wasm, with no parameters. The fallback is slower: fetch, then arrayBuffer(), then WebAssembly.instantiate.
  • Cross-origin rules apply. The response must be same-origin or allowed by CORS, and pages opened from file:// can't fetch modules at all. Test through a local web server or put the files online.
  • Content Security Policy. A page with a CSP must include 'wasm-unsafe-eval' in script-src before it can compile WebAssembly, as MDN's CSP reference explains.
  • Glue code. Toolchains such as Emscripten (C/C++) and wasm-bindgen (Rust) generate a .js file that does all of this for you. If a project ships app.js and app_bg.wasm, load the JavaScript file, not the module.

History

WebAssembly versions: 1.0, 2.0 and 3.0

VersionStatusWhat it added
MVP / 1.0Design agreed by the Chrome, Edge, Firefox and WebKit teams in February 2017; W3C Recommendation on 5 December 2019Integers and floats, functions, one linear memory, one function table, imports and exports
2.0Essentially finished around 2022; at the W3C still a Candidate Recommendation Draft128-bit vector (SIMD) instructions, bulk memory operations, multiple return values, simple reference types
3.0Completed as the new live standard on 17 September 202564-bit memories, multiple memories, garbage collection, typed references, tail calls, exception handling, relaxed vector instructions, a deterministic profile, custom annotations in WAT, JavaScript string built-ins

A .wasm file doesn't declare which version it targets; the header says version 1 either way. What matters is which instructions and section types it uses. An engine that meets a feature it doesn't support rejects the module with a CompileError. According to the 3.0 announcement, most major browsers already ship Wasm 3.0; the feature status page tracks each engine.

Garbage collection is what lets languages such as Kotlin and Dart compile to WebAssembly without shipping their own memory manager inside every module.

Beyond the web

Outside the browser: runtimes, WASI and components

Core WebAssembly has no way to open a file or print a line. A host has to provide that through imports. Outside the browser, the common set of imports is WASI, the WebAssembly System Interface, designed by a subgroup of the WebAssembly Community Group:

What it is
WASI 0.1 (preview 1)Functions imported from wasi_snapshot_preview1, such as fd_write. Still the most widely supported. sample-wasi-hello.wasm uses it.
WASI 0.2Launched on 25 January 2024 and rebuilt on the component model, with interfaces such as wasi:http and wasi:filesystem.
WASI 0.3Released on 11 June 2026. Adds native async (async func, stream<T>, future<T>) to the component model. Wasmtime supports it; support in language toolchains is still arriving.

A component wraps one or more core modules with typed interfaces, written in an interface language called WIT, so modules built from different languages can pass strings, lists and records to each other. Components use the same .wasm extension but a different header: an empty component built with jco 1.39 starts 00 61 73 6D 0D 00 01 00. Browsers and Node.js can't instantiate one directly (V8 reports expected version 01 00 00 00, found 0d 00 01 00); the jco tool transpiles components into JavaScript plus core modules.

Where .wasm files run
RuntimeNotes
BrowsersChrome, Edge, Firefox and Safari, through the JavaScript API.
Node.js, Deno, BunThe same JavaScript API; Node.js also has an experimental node:wasi module.
Wasmtime, Wasmer, WAMRStandalone runtimes: wasmtime file.wasm runs a WASI command from the terminal. Wasmtime is also embedded in other programs.
Cloudflare Workers and similar edge platformsRun modules compiled from Rust, Go or C close to users. Cloudflare's documentation describes its WASI support as experimental.

Troubleshooting

Why won't my .wasm file load?

These are the messages Chromium and Node.js give for the usual problems; Firefox and Safari word them differently but mean the same:

Error messageUsual causeFix
Incorrect response MIME type. Expected 'application/wasm'The server sends .wasm as application/octet-stream or text/plainMap .wasm to application/wasm on the server, or fall back to arrayBuffer() and WebAssembly.instantiate
expected magic word 00 61 73 6d, found 3c 21 44 4fThose bytes are <!DO: the server returned an HTML page, usually a 404 or a single-page app's fallback pageFix the path; check the response in the Network tab
HTTP status code is not okinstantiateStreaming received a 404 or 500 responseFix the path or the server
Import #0 "env": module is not an object or functionThe module imports functions and none were passedPass an imports object with every name wasm-objdump -x lists, or load it through its glue code
expected version 01 00 00 00, found 0d 00 01 00The file is a component, not a core moduleRun it in Wasmtime, or transpile it with jco
Cross origin requests are only supported for protocol schemes …The page was opened from file://Serve the folder with a local web server, or put it online

How to

How to open, inspect and convert a .wasm file

  • See what's inside: wasm-objdump -x file.wasm (WABT) lists sections, imports and exports; wasm2wat file.wasm prints the code as WAT. Browser developer tools show a disassembly of loaded modules in the Sources panel.
  • Run it: in a page, through the JavaScript API; on the command line, wasmtime file.wasm for a WASI command, or a few lines of Node.js for a library module.
  • Decompile it: wasm-decompile (WABT) produces C-like pseudo-code. The original source can't be recovered: names, types and structure are gone unless the file carries debug information.
  • Make it smaller: wasm-opt -Oz from Binaryen optimises for size, and wasm-strip removes custom sections such as names and debug data.
  • Compile to it: clang --target=wasm32, rustc --target wasm32-unknown-unknown, Emscripten for C and C++ projects, GOOS=wasip1 GOARCH=wasm go build for Go.

Security

Is a .wasm file safe?

WebAssembly was designed to run untrusted code. A module runs in a sandbox: it can only reach the outside world through the functions its host chooses to import, and in a browser it is bound by the same rules as the page's JavaScript, including the same-origin policy. It has no direct access to the DOM, your files, the camera or the network.

  • Bugs inside the module still matter. The sandbox protects the host, not the module from itself. A C buffer overflow compiled to WebAssembly can still overwrite other data in the module's own linear memory, as the WebAssembly security notes point out.
  • Side channels. Timing attacks against code running in a module are possible, as they are for JavaScript.
  • It's still code. A malicious page can use WebAssembly for anything it could do with JavaScript, only faster. Judge a site, not its file formats.
  • Opening a .wasm file on your computer runs nothing. No operating system executes it on double-click; it needs a runtime and a host that chooses to load it.

Free downloads

Sample WebAssembly files to download

Free sample WebAssembly files for testing servers, MIME types, runtimes, parsers and size limits. Each .wasm file passes wasm-validate and was instantiated in Node.js 22 with its results checked.

sample-demo.html · 7.0 KBDownloadOpen

What's inside sample-demo.html

The preview shows a page that loads sample-module.wasm and calls its exports. Open sample-demo.html in a text editor and each numbered comment marks one of these parts.

  1. Comment 1: the status line

    Reports how the module was loaded: "loaded" when instantiateStreaming worked, a warning naming the wrong Content-Type when only the slower fallback did, and "running from the embedded copy" when the file couldn't be fetched at all.

  2. Comment 2: the results table

    Filled in by the script with each call and its result, so you can see the module working.

  3. Comment 3: an embedded copy

    The same 262 bytes as base64. A sandboxed preview, like the one on this page, or a page opened from file:// can't fetch the .wasm file, so the page uses this copy instead.

  4. Comment 4: the import object

    The module imports env.log(pointer, length). JavaScript reads those bytes from the module's memory and decodes them as UTF-8: the usual way strings leave a module.

  5. Comment 5: loading

    instantiateStreaming first, then fetch with WebAssembly.instantiate, then the embedded copy.

  6. Comment 6: calling the exports

    add returns an i32, so add(2147483647, 1) wraps around to −2147483648. factorial takes and returns an i64, which JavaScript passes as a BigInt (20n). greet calls back into JavaScript through the import.

All sample WebAssembly files

  • Demo page that loads the module

    sample-demo.html · 7.0 KB (7,196 bytes)

    A self-contained page that loads sample-module.wasm from the same folder with instantiateStreaming, falls back step by step, and shows each export's result. It makes no other requests.

    • Checking a server sends `.wasm` as `application/wasm`
    • Seeing imports, exports and memory in action
  • Sample module

    sample-module.wasm · 262 bytes

    The module dissected in the anatomy above: one import (env.log), an exported memory with a data segment, a global, add, factorial and greet, and a custom section. Instantiate it with { env: { log } }.

    • Testing loaders, parsers and disassemblers
    • Teaching the binary format
  • Its WAT source

    sample-module.wat · 2.1 KB (2,139 bytes)

    The text-format source of sample-module.wasm, with numbered comments. Build it with wat2wasm --enable-annotations (the custom section uses Wasm 3.0's annotation syntax) and you get the same 262 bytes.

    • Learning WAT
    • Testing wat2wasm and editors
  • WASI command

    sample-wasi-hello.wasm · 195 bytes

    A hand-written WASI 0.1 program that prints Hello from WASI! and a link back here. Run it with wasmtime sample-wasi-hello.wasm or Node.js's node:wasi module. Browsers can't run it without a WASI shim.

    • Testing WASI runtimes
    • Checking standard output handling
  • Smallest valid module

    sample-minimal.wasm · 8 bytes

    Only the magic number and the version: no sections, no imports, no exports. It validates and instantiates to an empty set of exports.

    • Edge cases in parsers and upload checks
    • File-type detection by magic number
  • 100 KB module compiled from C

    sample-100kb.wasm · 100 KB (102,400 bytes)

    Two C functions (count_primes and fnv1a) compiled with clang 18, with the name, producers and target_features sections the toolchain adds, padded with a custom section to exactly 102,400 bytes. count_primes(100) returns 25.

    • Upload and size-limit tests
    • Seeing what a compiler's output looks like

Ways to test with them

  • MIME type: put sample-demo.html and sample-module.wasm in the same folder online and open the page. A green "loaded" means the server sends application/wasm; a warning names the type it sent instead.
  • Runtimes: sample-wasi-hello.wasm should print one line in Wasmtime or Node.js; sample-module.wasm should fail with a missing-import error until you pass env.log.
  • Parsers and file detection: sample-minimal.wasm is the 8-byte edge case: valid, but with no sections at all.
  • Upload limits: sample-100kb.wasm is exactly 102,400 bytes and still a valid module.

Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.

Put it online

Have a .wasm file? Give it a link.

Drop a .wasm file on 99helpers and it gets its own address, served as application/wasm. Opening the link downloads the file; a page on the same 99helpers site can load it with WebAssembly.instantiateStreaming.

Questions

WebAssembly files, frequently asked

What is a .wasm file?

A compiled WebAssembly module: binary code that browsers, Node.js and other runtimes can run safely inside a sandbox. It is usually produced by compiling C, C++, Rust, Go or another language.

How do I open a WASM file?

You don't open it like a document. To see what's inside, run wasm2wat file.wasm or wasm-objdump -x file.wasm from WABT. To run it, load it from a web page with JavaScript, or use a runtime such as Wasmtime for WASI programs.

Why does a .wasm file look like gibberish in a text editor?

Because it's binary: most bytes are opcodes and numbers, not text. Only names and embedded strings are readable. Convert it to the text format with wasm2wat to read it.

What MIME type should a server use for .wasm?

application/wasm, exactly. WebAssembly.instantiateStreaming refuses any other type, including application/octet-stream.

Can I convert a WASM file back to C or Rust?

Not to the original source. wasm2wat gives the exact instructions as text and wasm-decompile gives C-like pseudo-code, but variable names, comments and the original structure are lost unless the file has debug information.

Is WebAssembly faster than JavaScript?

Often, for heavy computation such as image processing, compression or physics, because it is compiled ahead of time with fixed types. For ordinary page code the difference is small, and every call between JavaScript and WebAssembly has a cost.

Can WebAssembly access the DOM?

Not directly. A module can only call functions it imports, so JavaScript has to do the DOM work on its behalf. Toolchains generate that glue code automatically.

What is the difference between WASM and WAT?

Same module, two forms. .wasm is the compact binary that engines run; .wat is the human-readable text format. Tools convert between them without loss, apart from comments.

Is it safe to open a .wasm file?

Opening one on your computer runs nothing. In a browser, WebAssembly runs inside the same sandbox as JavaScript and can only do what the page's own scripts let it do.

Related formats

Other file types explained

Keep going

Related

Sources

Last reviewed 27 October 2026.