Requirements

Browser support

Wavelength runs the full wallet daemon in the browser as WebAssembly. Target browsers with mature WASM, Web Worker, and storage APIs:

Browser Minimum version
Chrome (and Chromium-based browsers) 100+
Firefox 110+
Safari (macOS and iOS) 16+

Node.js 20 or later is recommended for local development (Vite, Next.js, and similar bundlers). No special build plugins are required beyond serving the WASM runtime assets and, for worker mode, setting cross-origin isolation headers.

Passkey support (WebAuthn with PRF) is optional and depends on the browser and OS. Password-based wallets work everywhere in the matrix above.

Cross-origin isolation

The default transport runs the daemon in a dedicated Web Worker and persists wallet state with OPFS-backed SQLite. Both paths rely on SharedArrayBuffer, which browsers only expose on cross-origin isolated pages.

Your app must send Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy response headers on every route that loads the wallet. See Cross-origin isolation for the exact values and a quick verification command.

If you cannot isolate the page (for example, because third-party embeds block require-corp), you can run the runtime on the main thread with createWebClient({ runtimeThread: 'main' }), but OPFS persistence will not be available and the UI thread will block while the daemon is busy. See Data & persistence for how storage behavior differs between main-thread and worker mode.

Backends

Wavelength is self-custodial: the seed never leaves the browser. The embedded daemon still needs reachable Ark, Esplora, and (for Lightning swaps) swap server endpoints at runtime.

Signet (recommended for development) works out of the box with the public preset. Pass defaultConfig('signet') to start() and the client connects to Lightning Labs hosted gateways:

config.ts
import { createWebClient, defaultConfig } from '@lightninglabs/wavelength-web';
const client = createWebClient();
await client.start(defaultConfig('signet'));

Presets also exist for testnet. No local backend is required for any hosted network.

Regtest (local development) targets services you run yourself, so there is no defaultConfig preset for it; local ports vary per machine, and the URLs are the whole point of a preset. Start arktest, note the ports it prints, and build the config by hand, including the insecure-transport flags for plain-HTTP local gateways:

config.ts
import type { RuntimeConfig } from '@lightninglabs/wavelength-web';
const config: RuntimeConfig = {
network: 'regtest',
arkServerAddress: 'http://127.0.0.1:7071',
walletEsploraUrl: 'http://127.0.0.1:8501',
swapServerAddress: 'http://127.0.0.1:10032',
arkServerInsecure: true,
swapServerInsecure: true,
};
await client.start(config);

Mainnet has no public preset yet, so like regtest there is no defaultConfig for it. Build the config by hand with your own gateway URLs and set allowMainnet: true before going live:

config.ts
await client.start({
network: 'mainnet',
arkServerAddress: 'https://your-ark-server.example.com',
walletEsploraUrl: 'https://your-esplora.example.com/api',
swapServerAddress: 'https://your-swap-server.example.com',
allowMainnet: true,
});

On web, arkServerAddress and swapServerAddress must be REST URLs. walletEsploraUrl is an HTTP Esplora endpoint. The web transport always rejects arkServerTlsCertPath. It accepts swapServerTlsCertPath only when disableSwaps: true, which suppresses every swap field.

Mainnet access is gated to an approved allowlist. See Mainnet access for how to request it with your client’s identity_pubkey.