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

react-sonic-perf

v0.1.0

Published

Audio-based real-time performance feedback for React apps — hear long tasks, layout shifts, slow renders and hydration delays as sound cues via the Web Audio API.

Downloads

146

Readme

react-sonic-perf

Hear your React app's performance problems in real time.

react-sonic-perf turns performance degradations — long tasks, layout shifts (CLS), slow component renders, and hydration delays — into live sound cues using the Web Audio API. Wrap your tree in <SonicProfiler> and jank becomes audible: the worse the incident, the more intense the sound.

Why audio? Your eyes are busy looking at the UI you're building. A Geiger-counter click when the main thread blocks, or a pitch that rises with render cost, gives you an ambient performance channel you can't tune out — no DevTools tab required.

  • Zero runtime dependencies
  • React 18 and 19 (peerDependencies)
  • SSR-safe and Next.js App Router compatible ("use client" built in)
  • Fully typed (TypeScript, strict)

Intended as a development tool. Gate it behind process.env.NODE_ENV !== "production" (Next.js) or import.meta.env.DEV (Vite).

Install

npm install react-sonic-perf
# or
pnpm add react-sonic-perf
# or
yarn add react-sonic-perf

Quickstart — Next.js (App Router)

<SonicProfiler> is a client component. Create a small client wrapper and render it from your root layout:

// app/sonic-perf-provider.tsx
"use client";

import { SonicProfiler } from "react-sonic-perf";

export function SonicPerfProvider({ children }: { children: React.ReactNode }) {
  return (
    <SonicProfiler
      mode="geiger"
      threshold={16}
      enabled={process.env.NODE_ENV !== "production"}
    >
      {children}
    </SonicProfiler>
  );
}
// app/layout.tsx
import { SonicPerfProvider } from "./sonic-perf-provider";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <SonicPerfProvider>{children}</SonicPerfProvider>
      </body>
    </html>
  );
}

See example/NextLayoutExample.tsx for a fuller version with a demo "block the main thread" button.

Quickstart — Vite

// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { SonicProfiler } from "react-sonic-perf";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <SonicProfiler mode="pulse" threshold={16} enabled={import.meta.env.DEV}>
      <App />
    </SonicProfiler>
  </StrictMode>
);

See example/ViteAppExample.tsx for a demo app with buttons that trigger artificial long tasks and slow renders.

Modes

| Mode | Sound | How severity is expressed | Best for | | --------- | --------------------------------------- | -------------------------------------------------------- | ------------------------------------- | | pulse | Short sine blip (default) | Pitch rises: 220 Hz base + severity × 220 Hz (max 880 Hz) | General use; distinct, non-intrusive | | geiger | Geiger-counter square-wave click bursts | Click count and rate scale with severity (1 → 7 clicks) | Hunting jank; visceral feedback | | ambient | Low 55 Hz drone | Loudness (gain) rises with severity | Background monitoring while you work |

Threshold guidance

threshold is the duration (ms) an incident must exceed before it makes sound. Severity is (duration − threshold) / threshold, clamped to 0..3.

  • 16 ms (default) — one 60 fps frame. Strict; good for animation-heavy UIs.
  • 50 ms — the "long task" definition from RAIL. Good general default for busier apps.
  • 100–200 ms — only hear the genuinely bad stuff.

Layout shifts are unitless (CLS), so they are scaled onto the same model: value × 250 pseudo-ms. At the default 16 ms threshold, shifts below ~0.064 stay silent and a "poor" 0.25 CLS lands near max severity.

Autoplay policy note

Browsers block audio until the user interacts with the page. The engine handles this for you: the AudioContext is created lazily and one-time pointerdown/keydown listeners resume it on the first gesture. Until then, cues are silently dropped (not queued) — so click or press a key once before expecting sound. Incidents are still reported to onIncident the whole time.

API reference

<SonicProfiler>

| Prop | Type | Default | Description | | ------------ | ---------------------------------- | ------------------- | ---------------------------------------------------------------------- | | mode | 'ambient' \| 'geiger' \| 'pulse' | 'pulse' | Auditory style (see Modes). | | threshold | number | 16 | Duration (ms) above which an incident fires. | | enabled | boolean | true | Master switch. When false, no observers run and no audio is created. | | volume | number | 0.2 | Master volume, 0..1. | | muted | boolean | false | Silence audio but keep reporting incidents via onIncident. | | onIncident | (incident: PerfIncident) => void | — | Called for every detected incident (also when muted). | | id | string | 'sonic-profiler' | Id passed to the underlying React <Profiler>. | | children | ReactNode | — (required) | The subtree to profile. |

What it detects:

  • slow-render — React <Profiler> commits whose actualDuration exceeds threshold.
  • long-taskPerformanceObserver longtask entries (main thread blocked > 50 ms).
  • layout-shiftlayout-shift entries without recent input, scaled from the CLS value.
  • hydration-delay — measured once per page load, from module load to the first effect.

All observers are feature-detected; unsupported environments simply skip that signal.

PerfIncident

interface PerfIncident {
  kind: "long-task" | "layout-shift" | "slow-render" | "hydration-delay";
  durationMs: number;   // observed (or synthesized) duration
  severity: number;     // 0..3, see threshold guidance
  timestamp: number;    // performance.now()-based
  detail?: string;      // human-readable context
}

computeSeverity(durationMs, thresholdMs): number

Pure helper: 0 at/under threshold, else (duration − threshold) / threshold clamped to 0..3.

mapSeverityToSound(severity, mode): SoundCue

Pure helper mapping a severity + mode to a playable cue ({ frequencyHz, gain, durationMs, kind: 'click' | 'blip' | 'drone', clickCount? }).

SonicAudioEngine

The Web Audio wrapper used internally — exported for advanced use and testing.

const engine = new SonicAudioEngine({
  volume: 0.2,
  muted: false,
  contextFactory: () => new AudioContext(), // optional; inject a fake in tests
});
engine.play(mapSeverityToSound(2, "pulse"));
engine.setVolume(0.5);
engine.setMuted(true);
engine.dispose();

All methods are SSR-safe and no-op gracefully when the Web Audio API is unavailable.

License

MIT © Dinesh Kumar