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

dejitter

v1.0.4

Published

Browser-injectable animation recorder & jank detector. Records every rAF at full speed, downsamples intelligently, and auto-detects jitter, flicker, shiver, jumps, stutter, and outliers.

Readme

Dejitter

A browser-injectable animation frame recorder that captures every requestAnimationFrame at full speed, then downsamples intelligently and auto-detects visual anomalies.

What it does

  • Records computed style changes, bounding rects, scroll positions, and DOM mutations across all (or selected) elements at full rAF speed
  • Downsamples output to a configurable sample rate — rare discrete changes are kept as-is, continuous properties are evenly sampled
  • Analyzes recordings to detect jitter, flicker, shiver (oscillation), sudden jumps, stutter (mid-motion reversals), and statistical outliers
  • Reports findings with severity levels, element identification, and detailed metadata

Install as Claude Code plugin

claude plugin add dejitter from wende/dejitter

Then use /dejitter in any conversation to get injection and usage instructions.

Quick start (manual)

Inject into any page:

const s = document.createElement('script');
s.src = 'http://localhost:8787/recorder.js';
document.head.appendChild(s);

Serve the file locally:

python3 -m http.server 8787 --bind 127.0.0.1

Then use the floating UI button or the console API:

dejitter.configure({
  selector: '.chat-container',
  props: ['transform', 'opacity', 'boundingRect'],
});
dejitter.start();
// ... interact with the page ...
dejitter.stop();
dejitter.findings();  // YAML report of detected anomalies

API

| Method | Description | |--------|-------------| | configure(opts) | Set selector, props, sampleRate, maxDuration, etc. | | start() | Begin recording at full rAF speed | | stop() | Stop recording, fire onStop callbacks | | onStop(callback) | Register a callback to run after stop | | findings(raw?) | Auto-detected anomalies (YAML default, pass true for array) | | summary(raw?) | Recording stats (YAML default, pass true for object) | | getData() | Full export: samples, elements, propStats, mutations | | toJSON() | JSON string of getData() | | getRaw() | Raw unprocessed frames and mutations |

Detection types

| Type | What it catches | |------|----------------| | jitter | Property bounces from rest state and returns — layout thrashing | | flicker | Opacity-specific bounce — element appears/disappears | | shiver | High-frequency oscillation — two forces fighting (e.g. scroll vs overscroll) | | jump | Single-frame discontinuity far exceeding typical delta | | stutter | Brief mid-motion direction reversal (1–3 frames) during smooth movement | | outlier | Property changing at statistically unusual rate vs siblings |

Configuration

dejitter.configure({
  selector: '*',        // CSS selector for elements to track
  props: ['opacity', 'transform'],  // properties to sample
  sampleRate: 15,       // target output samples/sec
  maxDuration: 10000,   // auto-stop after ms (0 = manual)
  minTextLength: 0,     // ignore elements with short text
  mutations: false,     // observe DOM mutations
  idleTimeout: 2000,    // auto-stop after idle ms (0 = off)
  thresholds: {         // anomaly detection sensitivity (partial overrides OK)
    jitter:  { minDeviation: 1, maxDuration: 1000, highSeverity: 20, medSeverity: 5 },
    shiver:  { minReversals: 5, minDensity: 0.3, highDensity: 0.7, medDensity: 0.5, minDelta: 0.01 },
    jump:    { medianMultiplier: 10, minAbsolute: 50, highMultiplier: 50, medMultiplier: 20 },
    stutter: { velocityRatio: 0.3, maxFrames: 3, minVelocity: 0.5 },
    outlier: { ratioThreshold: 3 },
  },
});

Only specify the values you want to change — unset values keep their defaults.

| Detector | Key | Default | Meaning | |----------|-----|---------|---------| | jitter | minDeviation | 1 | Min peak deviation to report | | jitter | maxDuration | 1000 | Max bounce duration (ms) | | jitter | highSeverity / medSeverity | 20 / 5 | Severity breakpoints | | shiver | minReversals | 5 | Min direction reversals to flag | | shiver | minDensity | 0.3 | Min reversal density to flag | | shiver | highDensity / medDensity | 0.7 / 0.5 | Severity breakpoints | | shiver | minDelta | 0.01 | Ignore deltas smaller than this | | jump | medianMultiplier | 10 | Delta must exceed median × this | | jump | minAbsolute | 50 | Min absolute delta (px) | | jump | highMultiplier / medMultiplier | 50 / 20 | Severity breakpoints | | stutter | velocityRatio | 0.3 | Reversal magnitude / local velocity threshold | | stutter | maxFrames | 3 | Max consecutive reversal frames to qualify | | stutter | minVelocity | 0.5 | Ignore reversals when motion is slower (px/frame) | | outlier | ratioThreshold | 3 | Change-count ratio vs median to flag |

Special props

  • 'boundingRect' — tracks getBoundingClientRect() (x, y, width, height)
  • 'scroll' — tracks scrollTop and scrollHeight
  • 'textContent' — tracks text length changes
  • '--custom-var' — any CSS custom property
  • Any standard CSS property (e.g. 'transform', 'opacity', 'top')

How downsampling works

Recording always runs at full requestAnimationFrame speed. On data export:

  1. Properties with zero changes → dropped entirely
  2. Properties with fewer changes than target → kept as-is (anomalies / rare discrete events)
  3. Properties with more changes than target → evenly downsampled to target frame count

This preserves important moments while keeping output manageable.

License

MIT