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:
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:
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:
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.