logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·31.10.26
← Back to news

The browser's main thread is expensive

Original article
kciter.so|317 points|102 comments|by kciter|Sep 1, 2026

The High Cost of the Browser's Main Thread

When developers discuss "frontend optimization," the conversation usually centers around reducing network requests, shrinking bundle sizes, or optimizing cache strategies. Some might mention minimizing re-renders or fine-tuning resource loading.

Rarely does the main thread enter the conversation. For most basic websites, it simply isn't a bottleneck. However, the situation shifts dramatically for highly interactive applications—sites where live data streams in constantly and scrolling, animations, and user inputs all collide.

Regardless of how small your bundle is, the user experience will shatter the moment the main thread is blocked. You've likely experienced this as "jank":

  • Scrolling that stutters intermittently.
  • Buttons that feel "mushy" or respond with a delay.
  • Text appearing in a search box a fraction of a second after you type it.

It isn't always a catastrophic failure, but it creates a subtle, irritating friction.

The Culprit: Thread Congestion

When we encounter this lag, our instinct is to ask, "Is my algorithm inefficient?" We start hunting for wasted CPU cycles. But usually, the issue isn't the speed of the code—it's the fact that the code is occupying the main thread.

The browser utilizes multiple threads, but the vast majority of our programmatic control is limited to the main thread. This single lane of traffic handles:

  1. General computation.
  2. Rendering processes.
  3. Event handling.
  4. Processing network responses.
  5. The internal logic of your chosen framework.

The Rendering Pipeline

When the DOM or CSS changes, the browser must execute a specific sequence to update the screen. Most of this happens on the main thread:

StepProcessDescription
1requestAnimationFrameExecutes JS registered to run immediately before the draw.
2StyleCalculates the final CSS values for every element.
3LayoutDetermines the exact position and size of elements (Reflow).
4PaintGenerates the commands to color the pixels.

Only the final step—Compositing (assembling the layers on screen)—is handed off to the Compositor Thread.

The Math of Smoothness

To achieve a fluid visual experience, the browser must hit the display's refresh rate. For a standard 60Hz monitor, the math looks like this:

Frame Time=160Hz≈16.6ms\text{Frame Time} = \frac{1}{60\text{Hz}} \approx 16.6\text{ms}Frame Time=60Hz1​≈16.6ms

Once you account for the browser's own overhead, the actual budget for our code is roughly 10ms. On a 120Hz display, that budget is sliced in half (≈8.3ms\approx 8.3\text{ms}≈8.3ms total).

Because JavaScript follows a single-threaded event loop, it can only do one thing at a time. If a function runs for 200ms, the browser is effectively paralyzed for that duration; it cannot repaint the screen or acknowledge a user's click.

Definition: Long Task Any task that occupies the main thread for more than 50ms is generally classified as a "long task" and is considered a performance failure.

Visualizing the Blockage

Imagine a page with two animations: one driven by JavaScript and one driven by CSS transform.

Main Thread Demo Placeholder

  • JS Animation: Runs on the main thread.
  • CSS Animation: Handled by the compositor.

If you trigger a blocking function (e.g., a heavy loop), the JS animation and all user input will freeze, but the CSS animation will keep spinning because it lives on a different thread.

// Example of a "Long Task" that freezes the UI
function blockMainThread(durationMs) {
    const start = performance.now();
    while (performance.now() - start < durationMs) {
        // Do nothing, just block the thread
    }
}
blockMainThread(500); // The screen freezes for 0.5 seconds

Measuring and Managing the Thread

Industry metrics like INP (Interaction to Next Paint) and TBT (Total Blocking Time) are essentially gauges for how often and for how long the main thread is held hostage.

To optimize, we have two primary strategies:

1. Spending the Resource Wisely

This involves staying on the main thread but managing time more intelligently. This is a matter of shaping and timing:

  • Splitting: Breaking one giant task into smaller chunks.
  • Batching: Grouping similar small tasks to reduce overhead.
  • Prioritizing: Ensuring critical updates happen first.
  • Deferring: Pushing non-essential work to a later time.

Example: The Chat Stream Problem In a high-traffic chat room, messages don't arrive in a steady trickle; they arrive in bursts. If you attempt to render 100 messages the instant they arrive, you trigger a massive wave of DOM creation, style calculations, and layout shifts all at once, blocking the thread.

2. Offloading the Work

The second strategy is to move the work away from the main thread entirely:

  • Moving to the Compositor: Using CSS properties (like transform or opacity) that bypass the main thread.
  • Web Workers: Sending heavy computation to a background worker thread.
  • Elimination: Simply removing the unnecessary work altogether.