npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fieldui/core

v0.2.0

Published

Headless React hooks for audio interfaces — transport, audio graph, peaks, regions, recording, analysis, export. The engine behind @fieldui/react.

Downloads

676

Readme

@fieldui/core

Headless React hooks for Field, a headless-first React component library for audio interfaces (waveforms, transports, meters, spectrograms, scrubbers, regions, knobs, faders).

This package is the engine. It builds on the Web Audio API and provides hooks for audio context lifecycle, transport state, peaks, regions, recording, analysis, and export. Everything the visual layer of Field is made of, with zero rendering opinions. If you want a custom UI, or no UI at all and just the audio plumbing, depend on this package alone.

┌──────────────────────────────────────────────────────────────┐
│  @fieldui/react        components (canvas + radix + tailwind) │
│  @fieldui/core   ←     headless hooks (audio graph, RAF, etc.)│
│  @fieldui/tokens       CSS variables + TS token map           │
└──────────────────────────────────────────────────────────────┘

Highlights

  • Two providers, one shared loop. AudioGraphProvider owns a single AudioContext plus an analyser → gain → destination master chain. RenderLoopProvider runs one shared requestAnimationFrame that all 60 fps consumers join, so a 10-component dashboard only ticks once per frame.
  • Refs-not-state for live data. Hooks that update at audio rate (peaks, level meter, playback time) return RefObjects the consumer reads inside its own RAF callback. Zero React re-renders during playback.
  • Type-safe source adapter. useAudioSource accepts exactly one of { buffer | element | stream } and bridges all three to the audio graph. Stream and element sources auto-connect to the master chain. Pass connect: false (or a specific AudioNode) to override. Per-context caches handle React 18 strict-mode remounts and the createMediaElementSource "called twice" footgun.
  • Bring your own AudioContext. AudioGraphProvider accepts an existing AudioContext (which it will not close on unmount) or AudioContextOptions for the default-create path. Drops into apps that already own their context (Tone.js, custom shells) cleanly.
  • Auto-resume on user gesture. Browsers require activation before audio can play. The provider attaches one-shot listeners for pointerdown, click, touchstart, and keydown, then resumes the context on the first one. No "click to start" overlay required.
  • Worker-offloaded peaks. usePeaks decimates buffers for waveform rendering. Large buffers (≳10 s mono at 48 kHz) run on a Blob-URL Worker; smaller ones inline so the postMessage round-trip doesn't dominate.

Install

npm install @fieldui/core react react-dom
# or pnpm / yarn / bun

react and react-dom are peer deps. Works on React 18 or 19.


Quickstart

A minimal player that decodes a clip, draws a waveform you supply, and exposes transport controls:

import {
  AudioGraphProvider,
  RenderLoopProvider,
  useAudioBuffer,
  useAudioGraph,
  useAudioSource,
  usePlaybackTime,
  useTransport,
} from "@fieldui/core";

const App = () => (
  <RenderLoopProvider>
    <AudioGraphProvider>
      <Player src="/clip.wav" />
    </AudioGraphProvider>
  </RenderLoopProvider>
);

const Player = ({ src }: { src: string }) => {
  const { masterAnalyser } = useAudioGraph();
  const { buffer } = useAudioBuffer(src);
  const audioSource = useAudioSource(buffer ? { buffer } : null);

  const transport = useTransport({
    buffer,
    createBufferSource: audioSource.createBufferSource,
    connectSource: (node) => masterAnalyser && node.connect(masterAnalyser),
  });
  const time = usePlaybackTime(transport); // time.timeRef.current is RAF-precise

  return (
    <div>
      <button onClick={transport.play} disabled={transport.state === "playing"}>
        Play
      </button>
      <button onClick={transport.pause}>Pause</button>
      <button onClick={transport.stop}>Stop</button>
      <span>{transport.currentTime.toFixed(2)}s / {transport.duration.toFixed(2)}s</span>
    </div>
  );
};

The two providers belong as high in the tree as you can put them, typically right under your app shell. useTransport doesn't know about your effect chain. It asks for a fresh source via createBufferSource() and lets you connect it via connectSource, so the same hook fits chainless rigs, multi-bus rigs, and send/return rigs.

For stream and element sources, useAudioSource auto-connects to masterAnalyser by default. No boilerplate:

useAudioSource({ stream: micStream });               // wired to masterAnalyser
useAudioSource({ stream, connect: "destination" }); // bypass master chain
useAudioSource({ stream, connect: myCustomNode });   // wire to your node
useAudioSource({ stream, connect: false });          // wire it yourself

Bring your own AudioContext

If your app already owns an AudioContext, hand it to the provider:

const ctx = new AudioContext({ latencyHint: "interactive" });

<AudioGraphProvider context={ctx}>{children}</AudioGraphProvider>

The provider builds its master chain on your context and does not call close() on unmount. You keep ownership of the lifecycle.

To customize the default-create path instead:

<AudioGraphProvider contextOptions={{ latencyHint: "playback", sampleRate: 48000 }}>
  {children}
</AudioGraphProvider>

Memoize or hoist contextOptions. The master chain rebuilds when latencyHint or sampleRate change.


Hook surface

Grouped by what you have and what you want:

| You have… | You want… | Use | |-----------------------------|----------------------------------|------------------------------------| | string URL or File | decoded AudioBuffer | useAudioBuffer | | string URL | streaming <audio> element | useMediaElement | | nothing | mic MediaStream | useMediaStream | | any source | adapt into the audio graph | useAudioSource | | any source | play / pause / seek state | useTransport | | transport | precise per-frame time ref | usePlaybackTime | | AudioBuffer | peak data for a waveform | usePeaks | | URL of pre-computed peaks | peak data | useRemotePeaks | | AnalyserNode | freq + time-domain refs | useAnalyser | | AnalyserNode | peak / RMS / hold refs | useLevelMeter | | AnalyserNode (live) | peaks built progressively | useWaveformBuilder | | MediaStream | recorded Blob | useRecorder | | AudioBuffer | rendered through a chain | useOfflineRender | | AudioBuffer | WAV / WebM blob | useExport | | nothing | gain / pan / filter nodes | useGainNode · usePanNode · useFilterNode | | an array of nodes | connected in series | useNodeChain | | transport | ARIA + live-region announcements | useAccessibleAudio | | any | document-level keyboard shortcuts | useAudioKeyboardShortcuts | | element ref | drag handler with axis lock | usePointerDrag | | element ref | observed size | useResizeObserver | | nothing | current device pixel ratio | useDevicePixelRatio | | input ref + value/min/max | arrow / page / home / end keys | useKeyboardStep | | region + duration | drag / resize handlers | useRegionDrag | | nothing | region list state | useRegions |

Plus utilities: formatTime / parseTime (mm:ss, mm:ss.ms, hh:mm:ss, SMPTE, bars:beats, samples), encodeWav / encodeWavFromChannels / sliceAudioBuffer, encodePeaks / decodePeaks / resamplePeaks, downloadBlob.


Architecture in one diagram

useAudioBuffer ──► AudioBuffer
                      │
                      ▼
useAudioSource ──► createBufferSource()  (factory)
                      │ each play()
                      ▼
useTransport ──► AudioBufferSourceNode ──► your chain (gain→pan→filter…)
                                                         │
                                                         ▼
                                                    masterAnalyser ──► masterGain ──► destination

The master analyser sits before the gain so muting the master output (e.g. for mic-monitor feedback prevention) doesn't silence level meters or spectrograms. Standard pre-fader-listen topology.

For the full architectural narrative (provider lifecycles, hook tiers, composition walkthroughs for the file player, streaming player, live mic, and record-then-playback patterns) see docs/architecture.md.


Sibling packages

| Package | Role | |---------|------| | @fieldui/react | Visual components (waveform, transport, meter, spectrogram, scrubber, knob, fader, regions, …) built on this package's hooks. | | @fieldui/tokens | Design tokens. CSS variables and TypeScript token map. Independent of this package. |


Compatibility

  • React 18 or 19 (peer dep on both).
  • Modern evergreen browsers. Uses AudioContext, AnalyserNode, MediaRecorder, OfflineAudioContext, ResizeObserver, matchMedia, Pointer Events, and Web Workers. Worker-offloaded peaks fall back to the main thread when Worker is unavailable.
  • ES2022 / Node ≥ 20 for development.
  • SSR-safe. Every hook guards its window / document / navigator access; nothing executes at module scope. The hooks no-op until they reach a real DOM environment.

License

MIT. © 2026 fieldui.