Frontend Development 27 min read

Understanding WebAssembly: Modules, Binary and Text Formats, and a Hands‑On Example

This article provides a comprehensive overview of WebAssembly, covering its core concepts such as modules, types, variables, functions, instructions, traps, tables, and linear memory, explains the binary module structure and sections, details the human‑readable text format with S‑expressions and lexical rules, and walks through a step‑by‑step example of writing, compiling, and invoking a simple WebAssembly module.

ByteFE
ByteFE
ByteFE
Understanding WebAssembly: Modules, Binary and Text Formats, and a Hands‑On Example

1. Introduction

After covering WebAssembly's key features, history, and primary use cases, this section introduces the basic concepts that will be explored in depth, including the binary format, text format, and a demo WebAssembly text module.

2. Basic Concepts

2.1 Modules

A WebAssembly module is the fundamental unit containing functions, linear memory, globals, and tables. It is instantiated after loading, decoding, and validation, producing a runtime instance that interacts with the host environment.

2.2 Types

WebAssembly defines four primitive value types— i32 , i64 , f32 , f64 —and later extensions such as v128 (SIMD) and reference types ( funcref , externref ).

2.3 Variables

Variables are either local (function‑scoped) or global, with locals accessed via local.get and globals via global.get / global.set . Globals may be mutable or immutable and can be imported or exported.

2.4 Functions

Functions are the containers for instructions; they accept typed parameters and may return multiple values. Functions cannot be nested.

2.5 Instructions

WebAssembly uses a stack‑based execution model; instructions push and pop operands on an implicit operand stack (e.g., local.get idx loads a local variable onto the stack).

2.6 Traps

Executing an unreachable instruction or other runtime error triggers a trap, which aborts execution and propagates to the host (e.g., a JavaScript RuntimeError ).

2.7 Tables

Tables store a vector of reference elements ( funcref or externref ) and enable indirect calls via call_indirect , effectively providing function‑pointer semantics.

2.8 Linear Memory

Linear memory is a contiguous byte array (default page size 64 KB). A module may define at most one memory, which can be grown with grow_memory and accessed via load / store instructions using i32 offsets.

3. Module Structure

The binary format consists of a magic number, version, and up to twelve optional sections (custom, type, function, table, memory, global, import, export, start, element, code, data, data count). Sections must appear in a defined order, and each section contains vectors of the corresponding definitions.

4. Text Format

4.1 S‑Expressions

The human‑readable format uses S‑expressions, a parenthesized prefix notation similar to Lisp, to represent both code and data.

4.2 Lexical Definitions

WebAssembly text consists of Unicode characters and supports line and block comments. Five lexical value categories exist: integers, floating‑point numbers, strings, names, and identifiers (e.g., $add ).

4.2.1 Values

Integers may be signed/unsigned and decimal/hexadecimal; floating‑point numbers include exponent notation; strings support escape sequences and Unicode code points.

4.2.2 Types

Value types include numeric ( i32 , i64 , f32 , f64 ), vector ( v128 ), and reference types ( funcref , externref ). These are used to declare function signatures, globals, tables, etc.

4.2.3 Instructions

The full instruction set is defined in the specification; this article references a few common instructions such as local.get , call , and call_indirect .

4.3 Writing a Simple WebAssembly Module

The following step‑by‑step example demonstrates how to create a minimal .wat file, define types, imports, functions, and exports, compile it with wat2wasm , and invoke it from JavaScript.

(module)
(type (func (param i32)))
(type (func))
(import "imports" "imported_func" (func (type 0)))
(func (type 1)
  i32.const 88
  call 0)
(export "exported_func" (func 1))

After saving the file as simple.wat , compile it:

# install wat2wasm by `npm install -g wat2wasm`
wat2wasm simple.wat -o simple.wasm

JavaScript code to load and run the module:

const fs = require('fs');
const wasm_buffer = fs.readFileSync("simple.wasm");
const js_func = (i) => console.log("From WebAssembly: " + i);
const import_obj = { imports: { imported_func: js_func } };
WebAssembly.instantiate(wasm_buffer, import_obj)
  .then(result => result.instance.exports.exported_func());

5. Conclusion

WebAssembly's binary and text formats are closely related; the text format provides a readable representation for debugging and manual authoring, while the binary format is used for efficient distribution and execution in browsers and other runtimes.

By studying this material, readers should be able to understand the fundamental concepts of WebAssembly, write simple text modules, and grasp the overall structure of the binary format.

WASMWebAssemblyModulesBinary FormatText Format
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.