EVENT LOOP VISUALIZER
Browser ↔ Node.js runtime trace
SpaceRT
Example
Console Output
Event Loop
Idle Waiting to start
Iteration 0
JavaScript Engine
Heap
Call Stack
Web APIs · provided by browser
setTimeout
fetch
DOM events
XHR
localStorage
indexedDB
Task Queue · macrotasks
Microtask Queue
Pick an example above, then press ▶ Play, or use ⏭ Forward / ⏮ Back in the dock to step through one transition at a time.

Browser model — what each part does

JavaScript EngineV8 (Chrome), SpiderMonkey (Firefox). Runs your code via call stack + heap. Single-threaded for JS itself.
Call StackTracks running functions. Only one frame at a time. Blocks the loop until empty.
Web APIsProvided by the browser, NOT by JS. Run in parallel: timers, fetch, DOM. When done, push callbacks to queues.
Task QueueSingle queue. Holds setTimeout, setInterval, I/O, UI events. Loop picks ONE per iteration.
Microtask QueueHigher priority. Promise.then, queueMicrotask, MutationObserver. Drains fully after every macrotask.
Event LoopScheduler. Rule: stack empty → drain ALL microtasks → take ONE macrotask → repeat. requestAnimationFrame runs in its own pre-render step.
Example
Console Output
Event Loop
Idle Waiting to start
Iteration 0
V8 Engine
Call Stack
Heap
libuv · C library
Native async work
Timer threads
FS thread pool
Network (epoll)
DNS resolver
Six phases — visited in order
01timers
02pending
03idle
04poll
05check
06close
Timers queue
Poll queue
Check queue
nextTick queue
Drains between every phase
Microtask queue
After nextTick, between phases
Loop priority order
  1. Sync code
  2. nextTick queue
  3. Microtask queue
  4. Run one callback from next phase
  5. Drain nextTick → microtasks → repeat
Node 11+: nextTick + microtasks drain after every individual callback, not just at phase boundaries. So microtasks can interleave with other timers within the timers phase.
Pick an example above, then press ▶ Play. Watch how the loop stops in the hallway between every phase to drain nextTick and microtasks.

Node model — what each part does

V8 EngineSame engine as Chrome. Executes JS via call stack + heap.
libuvC library. Owns the event loop, timer threads, FS thread pool (4 by default), and OS event polling (epoll/kqueue/IOCP). Internal — not exposed to your code.
worker_threads (different!)JS-level worker threads — require('worker_threads'). Separate V8 instances for CPU-bound JS. Not the libuv FS thread pool.
Phase 1 — TimersRuns expired setTimeout/setInterval. "0ms timer" runs at next loop tick.
Phase 2 — PendingDeferred I/O callbacks (TCP errors etc.). Rarely matters in app code.
Phase 3 — IdleInternal Node bookkeeping.
Phase 4 — PollWhere Node blocks waiting for new I/O. Most async work lands here.
Phase 5 — CheckRuns setImmediate callbacks. Use to defer until after I/O.
Phase 6 — CloseCleanup like socket.on('close', ...).
nextTick queueNode-specific. Drains between every callback (Node 11+), before microtasks.
Microtask queuePromise.then, queueMicrotask. Drains right after nextTick.
Example

Same code, two runtimes — side by side

Watch how the browser and Node.js handle identical JavaScript. Pay attention to the moments where their output order diverges.

Browser Iteration 0
Idle
Call Stack
Task Queue
Microtask Queue
Console
Node.js Iteration 0
Idle
Call Stack
Timers / Check / Poll
nextTick + Microtask
Console
Click ▶ Play in the dock to run both runtimes in parallel.

What to look for

Same code, different pathsBoth runtimes execute identical JavaScript, but the browser uses one task queue while Node uses 6 phases. The internal route differs even when output matches.
Divergence on setImmediateWeb has no setImmediate (non-standard). Try the comparison and you'll see it ignored on the web side.
Divergence on process.nextTickWeb has no process. Calls would throw. Only Node has this priority queue.
Same Promise behaviorMicrotask drain rules are now identical (since Node 11+). Promises behave the same in both runtimes.