Skip to main content

initCppJs(opts) — Runtime API

The single entry point for calling into your Wasm module from JavaScript. Produced by the cpp.js build pipeline; consumed by your application code.

The same function is exported by browser, Node, and Edge runtime entries. The opts shape is identical; only the available defaults differ per runtime.

Signature

initCppJs(opts?: InitOptions): Promise<Module>

initCppJs.terminate(): void // browser-only when useWorker:true

Module is the Emscripten runtime module enriched with cpp.js helpers (see § Module helpers below).

InitOptions

{
// ─────────────────────────────────────────────────────────────
// Worker bridging (browser-only)
// ─────────────────────────────────────────────────────────────
useWorker: false,
// When true, the Wasm module is instantiated inside a Web Worker
// and the main-thread receives a Comlink-bridged proxy.
//
// REQUIRED for OPFS persistent storage (the OPFS API is only
// exposed in Worker scope; mounting /opfs/... from the main
// thread throws).
//
// Independent from threading: you can use `useWorker: true`
// with `runtime: 'st'` and still get OPFS.
//
// NOT supported on edge runtimes (Cloudflare Workers, Deno
// Deploy, Vercel Edge) — they don't expose the Worker API.

workerUrl: undefined,
// Override the worker script URL. Defaults to whatever the
// bundler plugin set in config.paths.worker.

// ─────────────────────────────────────────────────────────────
// Filesystem
// ─────────────────────────────────────────────────────────────
fs: {
opfs: true,
// Browser default true. When false, only memfs is mounted.
// Has no effect outside the browser.
//
// Even when true, OPFS only activates if:
// 1. useWorker: true (Worker scope required), AND
// 2. The browser supports OPFS (Chrome 86+, Firefox 111+,
// Safari 15.2+).
//
// If you mount /opfs/... but conditions aren't met, cpp.js
// logs an error and silently redirects the path to /memfs.
},

// ─────────────────────────────────────────────────────────────
// Environment variables (passed to the Wasm process)
// ─────────────────────────────────────────────────────────────
env: {
SOME_VAR: 'static-string',
DYNAMIC_VAR: (state, target) => `${state.config.paths.build}/data`,
// Values can be strings OR functions of (state, target).
// Functions resolve lazily at the point of use (build-time
// env wiring) and produce a string for the runtime.
// See ADR-0003.
//
// Token replacement: the literal string `_CPPJS_DATA_PATH_`
// inside any value is replaced with the runtime data path
// (e.g. /opfs/<app> in browser, host fs path in Node).
},

// ─────────────────────────────────────────────────────────────
// Logging hooks
// ─────────────────────────────────────────────────────────────
logHandler: undefined,
// (text: string, channel: 'stdout') => void
// Replaces the default `console.debug('wasm stdout: ...')`.

errorHandler: undefined,
// (text: string, channel: 'stderr') => void
// Replaces the default `console.error('wasm stderr: ...')`.

// ─────────────────────────────────────────────────────────────
// Lifecycle
// ─────────────────────────────────────────────────────────────
onRuntimeInitialized: undefined,
// (m: Module) => void
// Called after the Wasm runtime finishes initializing.
// Use this for post-init bootstrapping that needs `m.FS`, etc.

// ─────────────────────────────────────────────────────────────
// WebAssembly override
// ─────────────────────────────────────────────────────────────
getWasmFunction: undefined,
// () => WebAssembly.Module
// Bypass the default fetch-and-compile flow with a precompiled
// module. Useful when you've embedded the .wasm via your bundler
// and want zero network round-trips.

// ─────────────────────────────────────────────────────────────
// Path overrides (advanced; bundler plugin sets these)
// ─────────────────────────────────────────────────────────────
paths: {
wasm: undefined, // override .wasm URL
data: undefined, // override packaged .data file URL
js: undefined, // override main script URL
worker: undefined, // override worker script URL
},
path: '', // global URL prefix prepended to every asset
}

Return value: Module

The Emscripten runtime module, plus cpp.js extensions. Embind exports from your C++ code are attached as named members.

Module helpers

m.FS // Emscripten virtual FS (mkdir, writeFile, …)

m.toArray(vector) // embind vector → JS Array
m.toVector(ClsOrName, []) // JS Array → embind vector

m.getFileBytes(path) // Uint8Array of file contents
m.getFileList(startPath?) // Recursive file listing → [{ path, size }]

// Browser-only mount helpers
m.getDefaultPath() // '/opfs' or '/memfs'
m.getFinalPath(path) // validate + maybe redirect (OPFS→memfs fallback)
m.getRandomPath(startPath?) // /<base>/<app>/automounted/<rand>
m.autoMountFiles(files, parentPath?) // mount File[] (e.g. from <input type=file>)

m.unmount() // placeholder, no-op

Worker mode (useWorker: true)

When useWorker: true, Module is a Comlink-wrapped proxy. Behavior is identical from your code's perspective with one caveat: every call crosses a worker boundary, so:

  • All embind objects are auto-proxied (via cpp.js's custom Comlink transfer handlers).
  • Calls are async by nature even when the underlying C++ is synchronous.
  • Returned vectors arrive as proxies; treat them the same — m.toArray(vec) still works.

Call initCppJs.terminate() to kill the worker and release resources.

Examples

Minimal browser

import { initCppJs } from './native/native.h'

const m = await initCppJs()
console.log(m.add(2, 3)) // an embind-exported function

Browser + persistent storage

const m = await initCppJs({
useWorker: true, // mandatory for OPFS
fs: { opfs: true }, // default, shown for clarity
})

// Files written under /opfs/<appName>/ survive page reloads.
m.FS.writeFile('/opfs/myapp/data.bin', new Uint8Array([1, 2, 3]))

Browser + multithread

const m = await initCppJs({
// Nothing extra here — `runtime: 'mt'` was set in cppjs.config.js
// at build time, so this Wasm IS multithreaded.
// Just make sure your prod host sends COOP/COEP headers.
})

Node.js

import { initCppJs } from './native/native.js'

const m = await initCppJs({
env: { TMPDIR: '_CPPJS_DATA_PATH_/scratch' },
})

Cloudflare Worker

import { initCppJs } from './native/native.js'

const m = await initCppJs()
// useWorker, OPFS, multithread all unavailable on edge.
// Run in single-thread + memory-fs only.

Custom logging

const m = await initCppJs({
logHandler: (text) => myLogger.info(`[wasm] ${text}`),
errorHandler: (text) => myLogger.error(`[wasm] ${text}`),
})

See also

  • filesystem.md — full OPFS / memfs / node-fs decision tree.
  • threading.mdruntime: 'mt' requirements, COOP/COEP, edge limits.
  • cppjs-config.md — build-time config that produces what initCppJs consumes.