Video Recorder Stress Test
The page evaluates the stability and reliability of the video recording client under heavy system load, specifically testing microphone input detection, silence detection, and performance resilience during high CPU usage. It aims to improve the platform’s robustness on slower devices by simulating bottlenecks and identifying edge-case bugs.
STRESS TEST // main thread
while loop doing heavy math (sin/cos/sqrt, string concatenation) for a set duration, then waits for an interval before firing again. Directly starves the event loop.offsetHeight (forced reflow), mutates their styles, then removes them. The read-after-write pattern prevents the browser from batching layout work.setTimeout callbacks simultaneously every tick, each doing light work (array sort, string ops). Stresses the timer queue and task scheduling overhead.Promise callbacks back-to-back, flooding the microtask queue. Unlike timers, microtasks run synchronously between tasks and cannot be interrupted — a deep chain fully blocks rendering.How this page works & what everything does
Background Loop
A requestAnimationFrame loop runs continuously to measure frame timing. Each frame, the loop records the elapsed time since the last frame, appends it to a rolling 120-sample buffer, and redraws the frame-time graph on a <canvas> element. This loop itself is intentionally lightweight — all frame budget consumption comes from the stress modules below.
Stats Bar (top-right)
| Stat | Description |
|---|---|
| FPS | Frames per second calculated from the last 30 frames. Drops below 60 indicate the main thread is busy. At 0 FPS the page is fully frozen. |
| Frame | The raw milliseconds elapsed since the previous frame. Smooth animation requires <16.67ms. Values above 50ms are red and represent severe jank. |
| Jank | The number of frames in the last 120 that exceeded 50ms (a "long task"). Higher = worse user experience. |
Frame Time Graph
The canvas graph plots the last 120 frame durations as a bar chart. Bars are color-coded: green = normal (<16ms), amber = degraded (16–50ms), red = severe jank (>50ms). A dashed line marks the 16.67ms target threshold.
Master Intensity (slider)
A global multiplier (1× – 20×) applied on top of each individual stressor's settings. At 20×, the burn duration, DOM element count, allocation size, timer count, and microtask depth are all scaled up by 20. Use this to quickly ramp all active stressors simultaneously without adjusting each card.
☢ NUKE Button
Temporarily overrides every stressor to its absolute maximum value and enables all of them for 5 seconds, then restores all previous states. Use this to instantly simulate a worst-case scenario and observe how the browser (and any monitoring tools you're testing) responds.
↺ RESET ALL Button
Disables every stressor, resets all sliders to their default values, clears the held memory buffer, and clears the frame-time graph. Returns the page to a clean baseline state.
Stress Modules
🔥 CPU Burn Loop
What it does in the background: a setInterval fires every Interval ms. When it fires, it runs a synchronous while loop for exactly Burn duration ms (measured via performance.now()), performing floating-point math (Math.sin, Math.cos, Math.sqrt) and string concatenation to prevent the JIT from optimizing it away. This is the most direct way to starve the event loop — nothing else can run during the burn.
| Control | Effect |
|---|---|
| Toggle | Enables / disables the burn interval. |
| Burn (ms/tick) | How many milliseconds the synchronous burn runs per invocation. At 16ms+ you will start dropping frames. At 100ms+ the page becomes noticeably unresponsive. |
| Interval (ms) | How often (in ms) a new burn tick fires. Lower = more frequent blocking. E.g. 50ms interval with 50ms burn = near 100% main thread saturation. |
🌪 DOM Thrash
What it does in the background: inside every requestAnimationFrame callback, it creates N <div> elements in an off-screen container, then reads their offsetHeight (forcing a synchronous layout reflow), then mutates their style.width (invalidating layout again). This read-write-read pattern prevents the browser from batching style recalculations and layout. After processing all elements, they are removed. High element counts can spike frame time into the hundreds of milliseconds.
| Control | Effect |
|---|---|
| Toggle | Enables / disables per-frame DOM thrashing. |
| Elements / frame | How many DOM nodes are created, force-reflowed, mutated, and removed each animation frame. Linear relationship with frame cost — 5000 elements typically causes severe jank. |
💾 Memory Pressure
What it does in the background: a setInterval allocates a Float64Array of the configured size. In hold mode the reference is pushed into a retained array, growing the JS heap until the tab is killed or the stressor is reset. In release mode the reference is immediately discarded, triggering frequent garbage collection cycles which pause JS execution (GC pauses can be 5–50ms depending on heap size).
| Control | Effect |
|---|---|
| Toggle | Enables / disables memory allocation. |
| Alloc (MB/tick) | Size of the typed array allocated per tick in megabytes. |
| Interval (ms) | How often a new allocation is made. |
| Hold references | When ON, allocated arrays are kept alive (heap grows). When OFF, they are immediately discarded, stressing the GC instead of raw heap size. |
⏱ Timer Flood
What it does in the background: every Interval ms, it schedules N setTimeout(fn, 0) calls simultaneously. Each callback sorts a small random array, does a string join, and runs a short math loop — lightweight per-task but collectively overwhelming. This exercises the browser's task scheduler and timer queue, which can cause starvation of other event listeners and I/O callbacks.
| Control | Effect |
|---|---|
| Toggle | Enables / disables timer spawning. |
| Timers / batch | How many setTimeout callbacks are scheduled per batch invocation. |
| Spawn every (ms) | Interval between batches. Lower interval + higher count = deeper timer queue saturation. |
⚡ Microtask Storm
What it does in the background: every Interval ms, it kicks off a chain of N resolved Promise.resolve().then() calls, each scheduling the next. Crucially, the microtask queue is drained completely before the browser returns to the task queue — so a chain of 100,000 microtasks blocks rendering for the entire duration of the drain, with no opportunity for the browser to paint frames or handle input. Unlike timers, you cannot "yield" out of a microtask chain once it starts.
| Control | Effect |
|---|---|
| Toggle | Enables / disables the microtask chain. |
| Chain depth | Number of chained .then() callbacks per storm. At 100,000 this will freeze rendering for multiple seconds. |
| Interval (ms) | How often a new chain is initiated. |