From dejitter
Inject the Dejitter animation recorder into a browser page to detect jitter, flicker, shiver, jumps, stuck, and layout anomalies. Use when debugging visual glitches or animation issues.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dejitter:dejitterThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The recorder script is bundled with this plugin. First find where it's cached, serve it, then inject:
The recorder script is bundled with this plugin. First find where it's cached, serve it, then inject:
# Find the plugin cache path and serve it (once per session)
find ~/.claude/plugins -name 'recorder.js' -path '*/dejitter/*' -print -quit
# Then cd to the directory containing recorder.js and serve:
# cd <that directory> && python3 -m http.server 8787 --bind 127.0.0.1 &
Then inject via evaluate_script or equivalent:
const s = document.createElement('script');
s.src = 'http://localhost:8787/recorder.js';
document.head.appendChild(s);
The script does not survive page refreshes. Re-inject after each navigation.
dejitter.configure({
selector: '.chat-container, .message', // CSS selector (default: '*')
props: ['transform', 'opacity', 'boundingRect'], // properties to sample
sampleRate: 15, // target output samples/sec (default: 15)
maxDuration: 10000, // auto-stop after ms, 0 = manual (default: 10000)
minTextLength: 0, // ignore elements with shorter text (default: 0)
mutations: false, // observe DOM mutations (default: false)
idleTimeout: 2000, // auto-stop after ms of no changes, 0 = off (default: 2000)
thresholds: { // anomaly detection sensitivity (override individual values)
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 },
stuck: { minStillFrames: 3, maxDelta: 0.5, minSurroundingVelocity: 1, highDuration: 500, medDuration: 200 },
outlier: { ratioThreshold: 3 },
},
});
| Prop | What it tracks |
|---|---|
'boundingRect' | getBoundingClientRect() → rect.x, rect.y, rect.w, rect.h |
'scroll' | scrollTop, scrollHeight on matched elements |
'textContent' | innerText length changes (textLen) |
'--custom-var' | Any CSS custom property |
| Any CSS prop | Computed style value (e.g. 'transform', 'opacity') |
dejitter.start();
// ... interact with the page (scroll, click, wait for animations) ...
dejitter.stop();
Or use the floating UI button (injected automatically in the top-right corner).
dejitter.findings() // → YAML string (default)
dejitter.findings(true) // → raw array of finding objects
Finding types:
Severities: high, medium, low, info
dejitter.summary() // → YAML string (pass true for raw object)
dejitter.getData() // → full object with samples, elements, propStats, mutations
dejitter.toJSON() // → JSON string of getData()
dejitter.getRaw() // → { rawFrames, mutations } (unprocessed)
// 1. Configure for the page under test
dejitter.configure({
selector: '[class*="message"], [class*="chat"], main',
props: ['transform', 'opacity', 'boundingRect'],
mutations: true,
maxDuration: 8000,
});
// 2. Record
dejitter.start();
// ... scroll, trigger animations, wait ...
// Recording auto-stops after idle or maxDuration
// 3. Check findings
dejitter.findings()
// Look for high-severity jitters, shivers, or jumps
// 4. Get details if needed
dejitter.summary()
dejitter.getData()
selector: '*' sparingly — it tracks every DOM element and generates noiseboundingRect is the most useful prop for detecting layout jankmutations: true helps correlate DOM changes with visual glitchesonStop(callback) lets you register hooks that fire after recording endsrawFrameCount is < 10 after a multi-second interaction, the recorder is not working. Do NOT conclude "no anomalies" — diagnose why frames aren't being captured.'main' for a scrollable chat).[class*="flex"] — matching hundreds of elements causes layout thrashing that creates artificial anomalies (observer effect).idleTimeout — if no DOM changes happen within that window after start(), it auto-stops.dejitter.start() and the user interaction that triggers animation. Do both in the same evaluate_script call if possible, or send the message immediately after starting.idleTimeout high (5000-10000ms) when tool call roundtrips add latency before the interaction begins.dejitter.getRaw() mid-recording to verify frames are accumulating.dejitter.summary() for elementsTracked — if 0, your selector matched nothing.npx claudepluginhub wende/dejitter --plugin dejitterAudits and fixes animation performance issues: layout thrashing, compositor properties, scroll-linked motion, blur effects. Use when animations stutter or transitions jank.
Diagnose, fix, or validate SwiftUI animation and scroll bugs by instrumenting views with MotionEyes, capturing console traces via XcodeBuildMCP or CLI, and comparing motion data to expectations.
Diagnoses and fixes Core Web Vitals (LCP, INP, CLS) issues using CoreDash real user monitoring data and Chrome browser tools. For web performance, page speed, or slow pages.