vite-plugin-fps-meter
Version:
vite-plugin-fps-meter is a tiny Vite plugin that overlays a live FPS / ms badge (with p95/p99 and Long Task %) on your app. It can auto-inject in dev or build, toggles via URL/localStorage, adapts to Vite's error overlay, and exposes a simple runtime API.
141 lines (93 loc) • 6.62 kB
Markdown
<div align="center">
<br>
<h1>vite-plugin-fps-meter</h1>
<p><sup><strong>vite-plugin-fps-meter</strong> is a tiny Vite plugin that overlays a live <strong>FPS / ms</strong> badge (with <strong>p95/p99</strong> and <strong>Long Task %</strong>) on your app. It can auto-inject in dev or build, toggles via URL/localStorage, adapts to Vite's error overlay, and exposes a simple runtime API.</sup></p>
[](https://www.npmjs.com/package/vite-plugin-fps-meter)
[](https://github.com/ux-ui-pro/vite-plugin-fps-meter)
[](https://www.npmjs.org/package/vite-plugin-fps-meter)
</div>
<br>
➠ **Install**
```bash
# yarn
yarn add -D vite-plugin-fps-meter
# pnpm
pnpm add -D vite-plugin-fps-meter
# npm
npm i -D vite-plugin-fps-meter
```
<br>
➠ **Quick Start**
```ts
// vite.config.ts
import { defineConfig } from 'vite';
import fpsMeter from 'vite-plugin-fps-meter';
export default defineConfig({
plugins: [
fpsMeter({
// Show badge only in dev by default ("dev" | "build" | boolean)
enabled: 'dev',
// Optional fine-tuning (defaults shown below in Options)
position: 'tl',
maxDisplayFps: 240,
}),
],
});
```
<br>
➠ **How it looks / Works**
- Click the badge to cycle: **FPS → ms → p95 → p99**.
- Long Task % shows how much time (in the last update window) was spent in [Long Tasks](https://developer.chrome.com/docs/devtools/evaluate-performance/reference#long-tasks) (if supported by the browser).
- The badge will automatically move to the bottom if Vite's error overlay appears while your preferred position is top.
- A tiny in-memory window is used to compute percentiles.
<br>
➠ **Options**
| Field | Type | Default | Description |
|:----------------------:|:------------------------------------------------:|:------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------|
| `enabled` | `boolean` | `'dev'` | `'build'` | `'dev'` | Controls when the runtime is injected and started. `'dev'` = Vite serve, `'build'` = during build preview/runtime, `true`/`false` to force on/off. |
| `updateIntervalMs` | `number` | `250` | UI update interval (ms). |
| `smoothingTauMs` | `number` | `300` | EMA smoothing time constant (ms). |
| `skipFrameThresholdMs` | `number` | `400` | Skip frames with `dt` above this threshold (e.g., tab switches) to avoid skewing EMA. |
| `pauseOnHidden` | `boolean` | `true` | Pause meter when `document.hidden` is true. |
| `maxDisplayFps` | `number` | `240` | Cap displayed FPS. |
| `position` | `'tl'` | `'tr'` | `'bl'` | `'br'` | `'tl'` | Badge position: top/bottom & left/right. |
| `zIndex` | `number` | `2147483647` | CSS z-index of the badge. |
| `storageKey` | `string` | `'vite:fps-meter'` | LocalStorage key for persisted toggle. |
| `showInIframe` | `boolean` | `true` | If `false`, the meter won't render inside iframes. |
| `percentileWindowSize` | `number` | `300` | Number of recent frame durations used to compute `p95`/`p99`. |
<br>
➠ **Runtime controls**
- URL param: `?fps=1` to force-enable, `?fps=0` to force-disable on first load.
- Persistence: state is saved in `localStorage['vite:fps-meter']` (customizable via `storageKey`).
- Global API (injected):
```ts
// Toggle at runtime
window.__fpsMeter?.setEnabled(false);
window.__fpsMeter?.setEnabled(true);
// Read current state
const on = window.__fpsMeter?.isEnabled();
```
<br>
➠ **Programmatic init (advanced)**
By default, the plugin auto-injects and auto-starts. If you need a custom instance, you can import the virtual client and mount manually:
```ts
// Somewhere in your app (advanced usage)
import initFpsMeter from 'virtual:fps-meter-client';
const dispose = initFpsMeter({ position: 'br' });
// ...later
dispose();
```
Note: manual init will create another badge in addition to the auto-injected one. Prefer the global API above unless you have a specific reason to manage instances yourself.
<br>
➠ **Notes on metrics**
- FPS and ms are derived from a smoothed EMA of frame durations.
- `p95`/`p99` are computed over a small sliding window of recent frames to avoid heavy allocations.
- Long Task % uses `PerformanceObserver` with `{ type: 'longtask', buffered: true }`. If unsupported, it's silently disabled.
<br>
➠ **Compatibility**
- Vite: peer dependency `^5` | `^6` | `^7`.
- Node: `>=18`.
- Works in iframes unless `showInIframe: false` is set.
<br>
➠ **License**
MIT