Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Browser & App Integration

This guide explains how to integrate Quiver into your browser application using the WASM bindings and npm packages.

Overview

Quiver provides three npm packages for browser integration:

PackagePurpose
@quiver/wasmCore WASM engine and AudioWorklet utilities
@quiver/reactReact hooks for UI integration
@quiver/typesTypeScript type definitions

Installation

npm install @quiver/wasm @quiver/react

Initializing the Engine

The WASM module must be initialized before use:

import { initWasm, createEngine } from '@quiver/wasm';

// Initialize once at app startup
await initWasm();

// Create an engine instance (44100 Hz sample rate)
const engine = await createEngine(44100);

Building a Patch

Add Modules

// Add modules by name and type
engine.add_module('vco', 'Vco');
engine.add_module('vca', 'Vca');
engine.add_module('output', 'StereoOutput');

Connect Modules

// Connect output port 0 of 'vco' to input port 0 of 'vca'
engine.connect('vco', 0, 'vca', 0);

// Connect VCA to stereo output (both channels)
engine.connect('vca', 0, 'output', 0);  // Left
engine.connect('vca', 0, 'output', 1);  // Right

Compile the Graph

After adding or connecting modules, compile the graph:

engine.compile();

Processing Audio

Sample-by-Sample

// Process one sample, returns [left, right]
const [left, right] = engine.tick();

More efficient for real-time audio:

// Process 128 samples at once
const samples = engine.process_block(128);
// Returns Float64Array: [L0, R0, L1, R1, ...]

AudioWorklet Setup

For real-time audio output in the browser:

import { createQuiverAudioNode } from '@quiver/wasm';

async function startAudio(engine) {
  const audioContext = new AudioContext();

  // Create worklet node connected to engine
  const quiverNode = await createQuiverAudioNode(audioContext, engine);

  // Connect to speakers
  quiverNode.connect(audioContext.destination);

  // Start (requires user gesture)
  await audioContext.resume();

  return { audioContext, quiverNode };
}

Architecture

Main Thread                      Audio Thread
┌─────────────┐                 ┌─────────────────┐
│  React UI   │ ──postMessage──▶│  AudioWorklet   │
│  (params)   │ ◀──────────────│  (process)      │──▶ Speakers
└─────────────┘                 └─────────────────┘

React Integration

useQuiverEngine

Initialize the engine in a component:

import { useQuiverEngine } from '@quiver/react';

function Synth() {
  const { engine, loading, error } = useQuiverEngine(44100);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <PatchEditor engine={engine} />;
}

useQuiverParam

Bind a parameter to UI:

import { useQuiverParam } from '@quiver/react';

function FrequencyKnob({ engine, nodeId }) {
  const [value, setValue, info] = useQuiverParam(engine, nodeId, 0);

  return (
    <Knob
      value={value}
      min={info.min}
      max={info.max}
      onChange={setValue}
    />
  );
}

useQuiverLevel

Display level meters:

import { useQuiverLevel } from '@quiver/react';

function Meter({ engine, nodeId, portId }) {
  const { rms_db, peak_db } = useQuiverLevel(engine, nodeId, portId);

  return <LevelMeter rms={rms_db} peak={peak_db} />;
}

Next Steps