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

r3f-monitor

v2.2.1

Published

A performance monitor for React Three Fiber. Track FPS, draw calls, memory, and GPU usage.

Downloads

1,935

Readme

npm

R3F-Monitor

Changelog

An advanced, easy-to-use performance monitoring tool for @react-three/fiber applications.

Add the <PerfMonitor /> component anywhere in your R3F Canvas — or go headless and bring your own UI.

Display Modes

🗂 Tab Display (Default)

A modern, structured interface with separated tabs for better focus and deep inspection.


📊 Classic Display

A compact, always-visible performance panel inspired by traditional FPS monitors.

🧪 Example

Live example:
👉 https://codesandbox.io/p/sandbox/3sqpy4

🚀 Key Features

  • Comprehensive Metrics: Monitor FPS, CPU/GPU render times, and JS Heap Memory.
  • Accurate Measurement Engine (v2): FPS via a real 1-second sliding window, CPU via performance.now() accumulation, and GPU via a WebGL2 timer-query queue (EXT_disjoint_timer_query_webgl2) reporting true milliseconds.
  • Headless Mode (v2.1): Run the measurement engine without the built-in UI. Read live metrics with usePerfData().
  • Adaptive Quality (v2.2): <PerfAdaptive /> automatically raises/lowers a quality factor (0–1) based on sustained FPS — drei <PerformanceMonitor>-compatible API, plus real GPU/CPU times to tell GPU-bound from CPU-bound. Pair with useGpuTier() for a per-device starting quality.
  • VRAM Estimation: Get an estimated breakdown of your GPU memory usage (Textures and Geometries).
  • Deep Analysis: Inspect individual WebGL programs, toggle visibility, and track matrix updates.
  • Flexible UI: Choose between graphical visualizations, detailed lists, or a minimal condensed view.

Note: Overclock mode was removed in v2. It still exists in 1.2.0 and earlier — pin to 1.2.0 if you rely on it.


📦 Installation

# npm
npm install r3f-monitor

# yarn
yarn add r3f-monitor

# pnpm
pnpm add r3f-monitor

# bun
bun i r3f-monitor

⚙️ Options

logsPerSecond?: number          // Log refresh rate (default: 10)

antialias?: boolean             // Enable text antialiasing

deepAnalyze?: boolean           // Enable detailed WebGL program inspection

showGraph?: boolean             // Toggle performance graphs (default: true)

graphType?: "line" | "bar"      // Graph style (default: "bar")

minimal?: boolean               // Compact condensed view

matrixUpdate?: boolean          // Count matrixWorld updates per frame

chart?: {
  hz?: number                   // Graph refresh frequency (default: 60)
  length?: number               // Number of data points displayed (default: 120)
}

className?: string              // Custom CSS class

style?: React.CSSProperties     // Inline style override

position?:
  | "top-right"
  | "top-left"
  | "bottom-right"
  | "bottom-left"               // Default: "top-right"

displayType?: "tab" | "classic" // Default: "tab"

📐 Measurement Engine

Starting with v2, the FPS / CPU / GPU values come from a reworked measurement core:

  • FPS — counted over a real 1-second sliding window and reported as a continuous value (smoothed with a light EMA), so the readout stays stable instead of flickering ±1.
  • CPU — wall-clock time of the render phase, accumulated per frame via performance.now().
  • GPU — measured with a WebGL2 timer-query queue (EXT_disjoint_timer_query_webgl2). Results are reported in true milliseconds (no scaling fudge). WebGL2 only.

The bar graph (graphType: "bar") scrolls left as new samples arrive and uses a fixed (high-water) vertical scale, with a gradient fill per metric.


🎛 Bring your own UI (Headless)

Added in v2.1

The default <PerfMonitor /> ships a ready-made UI. If you want to design your own interface — or adjust quality based on FPS — use headless mode: <PerfHeadless /> handles the measurement, you do the rest.

There are exactly two things you need:

  1. Read the values to displayusePerfData()
  2. Change quality based on FPS<PerfAdaptive /> (see Adaptive Quality)

The one rule: place <PerfHeadless /> somewhere inside <Canvas>.

1) Read the values — usePerfData()

A single hook, updating at logsPerSecond (default ~10×/sec, not every frame). The component reading the data can live outside <Canvas> — it only reads the store, no useThree required.

Call with no args → returns all the metrics:

const {
  fps,
  cpu,
  gpu,
  mem,
  vram, // the "hot" numbers
  gl, // { calls, triangles, points, lines, geometries, textures, programs }
  infos, // { version, renderer, vendor }
} = usePerfData();

| Field | Unit | Meaning | | ------- | -------- | ------------------------------------------ | | fps | frames/s | frames per second | | cpu | ms | CPU time per frame | | gpu | ms | GPU time per frame (WebGL2) | | mem | MB | JS heap in use | | vram | MB | estimated VRAM from the scene | | gl | — | render stats for the latest frame | | infos | — | renderer/vendor (constant for the session) |

Or pass a selector to read only the field you need — the component only re-renders when that field changes (avoids wasted re-renders when displaying a single number):

const fps = usePerfData((d) => d.fps);
import { Canvas } from "@react-three/fiber";
import { PerfHeadless, usePerfData } from "r3f-monitor";

// Custom UI — place OUTSIDE <Canvas>
function MyHud() {
  const { fps, cpu, gpu, mem, vram, gl } = usePerfData();
  return (
    <div
      style={{ position: "fixed", top: 12, left: 12, font: "12px monospace" }}
    >
      <div>{fps.toFixed(0)} FPS</div>
      <div>
        CPU {cpu.toFixed(1)} ms · GPU {gpu.toFixed(1)} ms
      </div>
      <div>
        RAM {mem.toFixed(0)} MB · VRAM {vram.toFixed(1)} MB
      </div>
      <div>
        {gl.calls} calls · {gl.triangles} tris
      </div>
    </div>
  );
}

export default function App() {
  return (
    <>
      <Canvas>
        <PerfHeadless logsPerSecond={10} />
        {/* <YourScene /> */}
      </Canvas>
      <MyHud />
    </>
  );
}

🎚 Adaptive Quality

Added in v2.2

<PerfAdaptive /> watches the sustained FPS coming from the measurement engine and maintains a quality factor between 0 and 1. When the device comfortably exceeds the upper FPS bound the factor steps up (incline); when it stays under the lower bound it steps down (decline). You map the factor to whatever costs performance: dpr, shadow map size, post-processing, draw distance…

The API mirrors drei's <PerformanceMonitor>, so existing code ports 1:1.

The one rule: mount a measuring component — <PerfHeadless /> or <PerfMonitor /> — inside <Canvas>. <PerfAdaptive /> itself renders nothing and only listens (1 FPS sample per engine log tick, ~10/s by default).

import { useState } from "react";
import { Canvas } from "@react-three/fiber";
import { PerfHeadless, PerfAdaptive } from "r3f-monitor";

export default function App() {
  const [dpr, setDpr] = useState(1);
  return (
    <Canvas dpr={dpr}>
      <PerfHeadless />
      <PerfAdaptive onChange={(e) => setDpr(0.5 + e.factor * 1.5)} />
      {/* <YourScene /> */}
    </Canvas>
  );
}

Options

| Prop | Default | Meaning | | ------------ | ------------------------------------------- | ------------------------------------------------------------ | | iterations | 10 | Samples collected before each decision | | threshold | 0.75 | Fraction of samples that must cross a bound to trigger | | step | 0.1 | Amount added/subtracted from factor | | factor | 0.5 | Starting factor | | flipflops | Infinity | Max incline/decline flips before onFallback fires | | bounds | (hz) => (hz > 100 ? [60, 100] : [40, 60]) | [lower, upper] FPS bounds, given the observed refresh rate |

The refresh rate is auto-detected from the rAF cadence at mount and refined by the highest FPS seen during the session — no configuration needed.

Callbacks

Every callback receives the engine: { fps, factor, refreshrate, gpu, cpu, fallback, … }.

  • onIncline(e) — sustained FPS above the upper bound: the device has headroom.
  • onDecline(e) — sustained FPS below the lower bound: reduce quality.
  • onChange(e) — the factor changed, in either direction.
  • onFallback(e) — flipped more than flipflops times: performance is unstable, set a fixed low baseline.

GPU-bound or CPU-bound? Each sample carries the real render times (ms), so you can pull the right lever:

<PerfAdaptive
  onDecline={(e) => {
    if (e.gpu > e.cpu)
      lowerResolution(); // GPU-bound → dpr/effects help
    else reduceDrawCalls(); // CPU-bound → dpr won't help
  }}
/>

Hook

Children of <PerfAdaptive> can subscribe with usePerfAdaptive — the equivalent of drei's usePerformanceMonitor:

usePerfAdaptive({ onChange: (e) => setQuality(e.factor) });

The decision core is also exported as a plain class, AdaptiveEngine (addSample(fps, gpu?, cpu?)), if you want to feed it samples yourself.

Starting quality — useGpuTier()

Runtime adaptation only reacts after the first slow frames. To start at a sensible quality per device, use useGpuTier() — a suspending wrapper around @pmndrs/detect-gpu:

import { Suspense } from "react";
import { useGpuTier } from "r3f-monitor";

function Effects() {
  const { tier } = useGpuTier(); // 0 (weakest) … 3 (strongest)
  return tier >= 2 ? <FancyPostProcessing /> : null;
}

// <Suspense fallback={null}><Effects /></Suspense>

useDetectGPU is exported as a drei-compatible alias, and <GpuTier> offers a render-prop version. The component calling the hook must be under <Suspense>.

One engine, no conflicts

Since v2.2 the measurement core is a ref-counted singleton: mounting <PerfHeadless /> and <PerfMonitor /> at the same time — or toggling the UI on and off — always runs exactly one engine. The first mount's options win; a console warning is logged if a later mount passes different options.


Usage

View Example

import { Canvas } from "@react-three/fiber";
import { PerfMonitor } from "r3f-monitor";

function App() {
  return (
    <Canvas>
      <PerfMonitor />
    </Canvas>
  );
}

Maintainers :

Thanks

Special thanks to twitter @utsuboco.

r3f-monitor was originally based on the excellent r3f-perf project and has since evolved with additional metrics, components, UI improvements, and various enhancements.