vibecheck0.3.0
Performancelong-task-attributionwarning

Long tasks blocking the main thread

The browser runs your JavaScript on the same single thread it uses to respond to clicks and paint frames, so any task that runs too long freezes everything — the page can’t respond to input while it works. This is what wrecks INP (Interaction to Next Paint) and makes a page feel janky right after load. AI code tends to do expensive work synchronously in render or on mount instead of chunking or deferring it.

Symptoms

  • The page is unresponsive for a beat after it loads or on first interaction
  • Clicks and typing feel delayed (poor INP)
  • The Performance panel shows long yellow "Task" blocks with a red corner
  • A single script dominates the main-thread flame chart

How VibeCheck catches it

In your widget · Problems

warninglong-task-attributionScript causing long frames: bundle.js

To your coding agent · MCP

agent › get_detected_issues
{ detector: "long-task-attribution", issue: "Script causing long frames: bundle.js", threshold: "A single script attributed to more than 3 long animation frames (LoAF)" }

The same string in your widget and in your agent’s context — no screenshot, no copy-paste.

Root causes

  • Expensive computation (parsing, sorting, formatting) run synchronously on the main thread
  • Large hydration or initialization work on mount
  • A heavy third-party script executing during load
  • Rendering or diffing a very large component tree in one pass

The fix

Keep individual tasks short. Break long work into chunks that yield to the browser between them, move pure computation into a Web Worker, defer non-critical work until the page is idle, and memoize expensive results so they don’t recompute on every render.

  1. Profile to find the script behind the long task (VibeCheck names it)
  2. Move heavy pure computation into a Web Worker, or chunk it with yielding
  3. Defer non-critical work with requestIdleCallback / scheduler.yield
Yield to the browser between chunks so input can be handledjs
async function processInChunks(items) {
  for (let i = 0; i < items.length; i++) {
    doWork(items[i])
    if (i % 50 === 0) {
      // let the browser paint and handle input
      await (window.scheduler?.yield?.() ?? new Promise((r) => setTimeout(r)))
    }
  }
}

FAQ

What counts as a long task?
The browser flags any task that occupies the main thread for 50ms or more. VibeCheck goes further and attributes long animation frames back to the specific script responsible, so you know what to fix.
When should I use a Web Worker vs chunking?
Use a Worker for pure, CPU-bound computation with no DOM access (parsing, crunching, transforming data). Use chunking/yielding when the work must touch the DOM or React state and can be spread across frames.
How does this relate to INP?
INP measures how long the page takes to visually respond to an interaction. Long tasks are the main cause of poor INP — while a long task runs, the browser can’t process the click or paint the response.