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

lite-fps-meter

v1.0.3

Published

Lightweight visual FPS monitor with auto-refresh-rate detection, compact mode, theming, and zero dependencies.

Readme

lite-fps-meter

npm version npm bundle size npm downloads npm total downloads TypeScript Zero Dependencies License: MIT

A lightweight, zero-dependency FPS monitor that renders a real-time graph overlay on a canvas element.

Drop it into any web project during development to spot jank, profile animations, or verify you're hitting your target frame rate. Auto-detects the display refresh rate (60Hz, 120Hz, 144Hz, etc.) and adapts thresholds automatically.

🎬 Live Demo (SmartObserver)

https://codepen.io/Zahari-Shinikchiev/debug/LERWgyQ

Features

  • Zero dependencies — single ES module, no build step required
  • Auto-detects refresh rate — adapts target from 30Hz to 240Hz displays
  • Canvas-rendered graph — scrolling history bar chart with color-coded thresholds
  • Compact mode — text-only readout when graph: false (15px tall)
  • EMA smoothing — configurable exponential moving average for stable readouts
  • Themeable — override any color (good/ok/bad/bg/mid/detecting)
  • Configurable — position, dimensions, smoothing factor, mount target
  • Clean teardowndestroy() cancels all rAF frames and removes DOM elements

Installation

npm install lite-fps-meter

Or drop the file directly into your project — it's a single ES module.

Quick Start

import { FPSMeter } from 'lite-fps-meter';

// Create and start (auto-attaches to document.body)
const meter = new FPSMeter();

// Later: clean up
meter.destroy();

A fixed-position overlay appears in the top-left corner showing current FPS, min/max range, and a scrolling bar graph.

Options

const meter = new FPSMeter({
    width: 120,              // Canvas width (px)
    height: 50,              // Canvas height (px) — auto-shrinks to 15 when graph: false
    graph: true,             // Show scrolling bar graph (false = compact text-only)
    graphHeight: 30,         // Graph area height (px)
    textUpdateInterval: 100, // Min ms between text refreshes
    smoothing: 0.1,          // EMA factor (0–1). Lower = smoother, higher = responsive
    position: 'top-left',    // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
    target: null,            // Mount target element (default: document.body)
    theme: {                 // Color overrides (merged with defaults)
        good: '#0f0',        // ≥ 80% of target FPS
        ok: '#ff0',          // 50–80% of target
        bad: '#f00',         // < 50% of target
        bg: '#111',          // Background
        mid: '#800',         // 50% target line
        detecting: '#aaa',   // Text during refresh rate detection
    },
});

Compact Mode

For a minimal footprint, disable the graph. The meter shrinks to a 15px-tall text-only readout:

const meter = new FPSMeter({ graph: false, position: 'bottom-right' });

API

| Method | Description | |--------|-------------| | new FPSMeter(options?) | Create meter and start measuring immediately | | .pause() | Pause the measurement loop | | .resume() | Resume after pause (resets timestamp to avoid delta spike) | | .reset() | Clear min/max counters, history graph, and refresh text | | .destroy() | Stop everything, remove DOM elements. Idempotent. |

Properties

| Property | Type | Description | |----------|------|-------------| | .fps | number | Current smoothed FPS (EMA) | | .min | number | Minimum FPS since last reset | | .max | number | Maximum FPS since last reset | | .targetFPS | number | Detected display refresh rate | | .showGraph | boolean | Whether the bar graph is rendered | | .smoothing | number | EMA smoothing factor | | .theme | object | Active color theme |

Color Thresholds

The graph and text change color based on current FPS relative to the detected target:

| Color | Condition | |-------|-----------| | 🟢 Green | ≥ 80% of target | | 🟡 Yellow | 50–80% of target | | 🔴 Red | < 50% of target |

How It Works

Refresh rate detection: On creation, the meter counts requestAnimationFrame callbacks over a 250ms window and derives the average frame rate. Background-throttled frames (>100ms) are discarded. The result is clamped to 30–240Hz. Detection survives pause — it resumes timing when the meter restarts.

FPS calculation: Each frame computes an instantaneous FPS from the delta, then applies an exponential moving average: fps = fps + (instant - fps) * smoothing. Zero and negative deltas (tab resume, first frame) are skipped to prevent Infinity from polluting the average.

Graph rendering: A Uint8Array ring buffer stores the last N frame heights. On each draw, the buffer is read from the current index (oldest) to produce a left-to-right scrolling chart. Two reference lines mark 100% and 50% of the target FPS. In compact mode, the buffer is never written and the bar loop is skipped entirely.

TypeScript

Full type definitions included:

import { FPSMeter, type FPSMeterOptions, type FPSMeterTheme } from 'lite-fps-meter';

const meter = new FPSMeter({
    position: 'bottom-right',
    theme: { good: '#00ff88' },
});

License

MIT