---
title: "Audio engines"
description: "Standalone FFT pipelines — local capture, client-side FFT of a PCM stream, PCM decoding, and the raw WASM processor."
canonical_url: "https://www.fftvisualizer.com/core/audio-engines"
---
# Audio engines

> Standalone FFT pipelines — local capture, client-side FFT of a PCM stream, PCM decoding, and the raw WASM processor.

The core ships the audio→FFT pipelines as standalone factories, so you can produce FFT
magnitudes without the renderer — drive your own visuals, or feed the result into
`FFTVisualizer` with `mode: 'external'`. These are exactly what the
[Vue composables](https://www.fftvisualizer.com/vue/composables) wrap.

All engines emit `Uint8Array` magnitudes (0–255 per bin) and share four options: `fftSize`
(default `2048`), `bins` (default `80`), `startFreq` (default `100` Hz), and `endFreq`
(default `18000` Hz).

## `createLocalAudio(options?)`

Captures mic or system/tab audio and computes the FFT in-browser via the Rust WASM processor.

```ts
import { createLocalAudio } from '@fft-visualizer/core'

const audio = createLocalAudio({
  bins: 80,
  onData: (data) => {
    // data: Uint8Array — fresh magnitudes each animation frame
  },
  onStateChange: () => {
    // isActive / devices / activeDeviceId / sourceType changed
  }
})

await audio.getDevices()   // enumerate inputs (prompts for mic permission)
await audio.start()        // microphone (optional deviceId argument)
await audio.startDisplay() // tab/system audio via screen sharing
audio.stop()
```

The returned `LocalAudioEngine` exposes read-only `fftData`, `isActive`, `sourceType`,
`devices`, and `activeDeviceId`, plus the `getDevices` / `start` / `startDisplay` / `stop`
methods above.

## `createWebSocketFft(options?)`

Connects to a WebSocket that streams **raw PCM** and computes the FFT client-side via WASM —
distinct from `FFTVisualizer`'s `websocket` mode, which expects **pre-computed** FFT (see
[WebSocket protocol](https://www.fftvisualizer.com/guide/websocket-protocol)). It produces mono, left, and right spectra.

```ts
import { createWebSocketFft } from '@fft-visualizer/core'

const engine = createWebSocketFft({
  bins: 80,
  overlap: 0.5,        // 0–0.75; more overlap = lower latency, ~2× FFT compute
  autoReconnect: true,
  onData: (mono, left, right) => { /* Uint8Array each */ }
})

engine.connect('wss://your-server/pcm')
engine.processSamples(float32Pcm) // or feed PCM manually
engine.disconnect()
engine.free() // tear down and free WASM processors
```

Read-only getters: `fftData`, `fftDataLeft`, `fftDataRight`, `isConnected`.

## `pcmToChannels(buffer, bitDepth, channels)`

Decodes an interleaved integer-PCM `ArrayBuffer` (16/24/32-bit) into normalized Float32
channels — `{ mono, left, right }`, each in `[-1, 1)`. Mono input is duplicated to both
channels; only the first two channels of multichannel input are used. This is the decoder
`createWebSocketFft` uses internally.

## Raw WASM processor

For full control, the Rust `FftProcessor` is exported from a dedicated subpath. It's kept out
of the main entry so importing the visualizer never eagerly loads ~289 kB of WASM.

```ts
import { FftProcessor } from '@fft-visualizer/core/wasm'

const processor = new FftProcessor(fftSize, bins, startFreq, endFreq, sampleRate)
const magnitudes = processor.process(float32TimeDomainSamples) // Uint8Array
processor.free()
```


## Sitemap

See the full [sitemap](https://www.fftvisualizer.com/sitemap.md) for all pages.
