vite-plugin-fps-meter
v1.0.0
Published
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.
Maintainers
Readme
➠ Install
# yarn
yarn add -D vite-plugin-fps-meter
# pnpm
pnpm add -D vite-plugin-fps-meter
# npm
npm i -D vite-plugin-fps-meter➠ Quick Start
// 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,
}),
],
});➠ 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 (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.
➠ 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. |
➠ Runtime controls
- URL param:
?fps=1to force-enable,?fps=0to force-disable on first load. - Persistence: state is saved in
localStorage['vite:fps-meter'](customizable viastorageKey). - Global API (injected):
// Toggle at runtime
window.__fpsMeter?.setEnabled(false);
window.__fpsMeter?.setEnabled(true);
// Read current state
const on = window.__fpsMeter?.isEnabled();➠ 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:
// 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.
➠ Notes on metrics
- FPS and ms are derived from a smoothed EMA of frame durations.
p95/p99are computed over a small sliding window of recent frames to avoid heavy allocations.- Long Task % uses
PerformanceObserverwith{ type: 'longtask', buffered: true }. If unsupported, it's silently disabled.
➠ Compatibility
- Vite: peer dependency
^5|^6|^7. - Node:
>=18. - Works in iframes unless
showInIframe: falseis set.
➠ License
MIT
