Interactive UI Stress Canvas
Visual proof of UI starvation
Frame Duration & Event Loop Profiler
MONITORING ACTIVE
Ready to Profile
Select thread model and click 'Run Workload' to compare UI responsiveness.
Why JavaScript Freezes: Browsers execute DOM manipulations, layout recalcs, CSS transitions, and user inputs on the exact same thread as regular JavaScript code. When you run large array sorting or heavy loops on the main thread, the call stack is blocked. Frames are dropped, spinners stall, and clicks are ignored.
The Worker Fix: A Web Worker spawns an isolated background thread with its own event loop and memory context. Data passes asynchronously via postMessage(), leaving the main thread 100% free to render buttery 60 FPS animations.
worker.js (Dedicated Thread Blob)
// 1. Worker definition (or external worker.js file)
const workerCode = `
self.onmessage = function(e) {
const { data, filterVal } = e.data;
// Heavy computation isolated from the main thread
const result = data.filter(item => item.latency > filterVal)
.sort((a, b) => b.val - a.val);
self.postMessage(result);
};
`;
// 2. Initialize worker via inline Blob URL
const blob = new Blob([workerCode], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
// 3. Dispatch task without halting UI
worker.postMessage({ data: largeArray, filterVal: 200 });
worker.onmessage = function(e) {
console.log('Workload finished in background:', e.data);
};
useWorker.ts (React Non-blocking Hook)
import { useState, useRef, useEffect, useCallback } from 'react';
export function useWorker<TInput, TOutput>(workerFn: (input: TInput) => TOutput) {
const [loading, setLoading] = useState(false);
const workerRef = useRef<Worker | null>(null);
useEffect(() => {
const code = `self.onmessage = (e) => {
const res = (${workerFn.toString()})(e.data);
self.postMessage(res);
};`;
const blob = new Blob([code], { type: 'application/javascript' });
workerRef.current = new Worker(URL.createObjectURL(blob));
return () => workerRef.current?.terminate();
}, [workerFn]);
const execute = useCallback((payload: TInput): Promise<TOutput> => {
return new Promise((resolve) => {
setLoading(true);
if (workerRef.current) {
workerRef.current.onmessage = (e) => {
setLoading(false);
resolve(e.data);
};
workerRef.current.postMessage(payload);
}
});
}, []);
return { execute, loading };
}