Node.js with WebAssembly

WebAssembly is a high-performance assembly-like language that can be compiled from various languages, including C/C++, Rust, and AssemblyScript. Currently, it is supported by Chrome, Firefox, Safari, Edge, and Node.js!

The WebAssembly specification details two file formats, a binary format called a WebAssembly Module with a .wasm extension and corresponding text representation called WebAssembly Text format with a .wat extension.

Key Concepts

  • Module - A compiled WebAssembly binary, i.e. a .wasm file.
  • Memory - A resizable ArrayBuffer.
  • Table - A resizable typed array of references not stored in Memory.
  • Instance - An instantiation of a Module with its Memory, Table, and variables.

In order to use WebAssembly, you need a .wasm binary file and a set of APIs to communicate with WebAssembly. Node.js provides the necessary APIs via the global WebAssembly object.

var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(WebAssembly);
/* Object [WebAssembly] { compile: [Function: compile], validate: [Function: validate], instantiate: [Function: instantiate] } */

Generating WebAssembly Modules

There are multiple methods available to generate WebAssembly binary files including:

  • Writing WebAssembly (.wat) by hand and converting to binary format using tools such as wabt
  • Using emscripten with a C/C++ application
  • Using wasm-pack with a Rust application
  • Using AssemblyScript if you prefer a TypeScript-like experience

Some of these tools generate not only the binary file, but the JavaScript "glue" code and corresponding HTML files to run in the browser.

How to use it

Once you have a WebAssembly module, you can use the Node.js WebAssembly object to instantiate it.

// Assume add.wasm file exists that contains a single function adding 2 provided arguments
const module "node:fs"fs = 
var require: NodeJS.Require
(id: string) => any
Used to import modules, `JSON`, and local files.
@sincev0.1.13
require
('node:fs');
// Use the readFileSync function to read the contents of the "add.wasm" file const const wasmBuffer: NonSharedBufferwasmBuffer = module "node:fs"fs.
function readFileSync(path: fs.PathOrFileDescriptor, options?: {
    encoding?: null | undefined;
    flag?: string | undefined;
} | null): NonSharedBuffer (+2 overloads)
Returns the contents of the `path`. For detailed information, see the documentation of the asynchronous version of this API: {@link readFile } . If the `encoding` option is specified then this function returns a string. Otherwise it returns a buffer. Similar to {@link readFile } , when the path is a directory, the behavior of `fs.readFileSync()` is platform-specific. ```js import { readFileSync } from 'node:fs'; // macOS, Linux, and Windows readFileSync('<directory>'); // => [Error: EISDIR: illegal operation on a directory, read <directory>] // FreeBSD readFileSync('<directory>'); // => <data> ```
@sincev0.1.8@parampath filename or file descriptor
readFileSync
('/path/to/add.wasm');
// Use the WebAssembly.instantiate method to instantiate the WebAssembly module WebAssembly.function WebAssembly.instantiate(bytes: BufferSource, importObject?: WebAssembly.Imports): Promise<WebAssembly.WebAssemblyInstantiatedSource> (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/WebAssembly/JavaScript_interface/instantiate_static)
instantiate
(const wasmBuffer: NonSharedBufferwasmBuffer).Promise<WebAssembly.WebAssemblyInstantiatedSource>.then<void, never>(onfulfilled?: ((value: WebAssembly.WebAssemblyInstantiatedSource) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<...>) | null | undefined): Promise<...>
Attaches callbacks for the resolution and/or rejection of the Promise.
@paramonfulfilled The callback to execute when the Promise is resolved.@paramonrejected The callback to execute when the Promise is rejected.@returnsA Promise for the completion of which ever callback is executed.
then
(wasmModule: WebAssembly.WebAssemblyInstantiatedSourcewasmModule => {
// Exported function lives under instance.exports object const { const add: WebAssembly.ExportValueadd } = wasmModule: WebAssembly.WebAssemblyInstantiatedSourcewasmModule.WebAssembly.WebAssemblyInstantiatedSource.instance: WebAssembly.Instanceinstance.WebAssembly.Instance.exports: WebAssembly.Exports
[MDN Reference](https://developer.mozilla.org/docs/WebAssembly/JavaScript_interface/Instance/exports)
exports
;
const const sum: anysum = const add: WebAssembly.ExportValueadd(5, 6); var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const sum: anysum); // Outputs: 11
});

Interacting with the OS

WebAssembly modules cannot directly access OS functionality on its own. A third-party tool Wasmtime can be used to access this functionality. Wasmtime utilizes the WASI API to access the OS functionality.

Resources