An interactive JavaScript Event Loop visualizer โ paste real code, watch it actually run through the Call Stack, Web APIs, Microtask Queue, and Callback Queue.
๐ Live Demo โ (replace with your GitHub Pages URL after deploying, e.g.
https://yourname.github.io/loopscope/)
- What is this?
- Why it's different
- Screenshots / Preview
- Features
- How it works internally
- Project structure
- Deploying to GitHub Pages
- Usage guide
- Supported vs. unsupported JavaScript
- The Event Loop โ quick primer
- Reading the step explanations
- Tech stack
- Roadmap
- Contributing
- FAQ
- License
Most "event loop visualizers" animate a handful of hardcoded example snippets. Loopscope is different โ it's a real, hand-built JavaScript interpreter (parsing with Acorn) wired to its own virtual scheduler, so you can paste any JavaScript you want and watch the actual execution order unfold:
- Function calls landing on the Call Stack
- Async work (
setTimeout,fetch) handed off to Web APIs - Promise reactions and
queueMicrotaskpiling up in the Microtask Queue - Timer callbacks waiting their turn in the Callback Queue
The entire run is computed instantly against a virtual clock, then replayed step-by-step so you can scrub through it like a debugger โ play, pause, step forward, step backward, adjust speed โ with the current line highlighted in the editor as you go.
| Typical event loop visualizer | Loopscope |
|---|---|
| A few fixed, hardcoded demos | Paste any JavaScript |
| Animation is scripted by hand | Animation is a real execution trace |
| No feedback on your own code | Full interpreter: closures, classes, destructuring, async/await, promises, try/catch, loops... |
| Just shows queues moving | Every step has a plain-English explanation of why it happened |
| No teaching material | Ships with a full written guide (beginner โ advanced) baked into the page |
(Add a screenshot or GIF here once deployed โ this section is a great place for a
docs/preview.gifshowing the Run โ Trace โ Playback flow.)
docs/
preview.png โ recommended: full-app screenshot
demo.gif โ recommended: 5โ10s clip of stepping through a trace
- ๐งฉ Custom interpreter โ parses your code with Acorn and tree-walks it, delegating to real JS natives (
Math,JSON, array/string/object methods) so the data behaves exactly like real JavaScript. - โฑ Accurate async simulation โ
setTimeout/setInterval/clearTimeout/clearInterval, real Promise semantics (then/catch/finally/all/race/allSettled/any),async/awaitsuspension & resumption,queueMicrotask, and a mockedfetchwith simulated network delay. - ๐ Modern syntax support โ closures, all control flow (
if/for/while/do-while/for-of/for-in/switch/try-catch-finally), destructuring (objects & arrays, defaults, rest), spread/rest, template literals, classes (constructor, methods, getters/setters,static,extends/super), labeledbreak/continue. - ๐ฌ Full playback controls โ play/pause, step forward/back, adjustable speed, live line highlighting in the editor.
- ๐ฃ Beginner-friendly explanations โ every step in the trace comes with a plain-English "why," not just a mechanical log line.
- ๐จ Distinct, non-generic dark UI โ four color-coded panels (Call Stack, Web APIs, Microtask Queue, Callback Queue) that visually mirror the real event loop diagram.
- โ๏ธ Live "custom code" detection โ the sample dropdown automatically switches to "โ Custom code" the moment you edit or paste your own script.
- ๐ Full built-in guide โ a complete, SEO-friendly article covering the event loop from first principles to Node.js internals, with a table of contents, glossary, and FAQ.
- ๐ก Safety guards โ infinite-loop protection, step-count caps, and graceful syntax/runtime error reporting.
- ๐ Zero build step โ plain HTML/CSS/JS. Open the file or host it as a static site; no npm install, no bundler.
Loopscope doesn't just animate pre-scripted timelines โ it actually runs your code once, instantly, against a virtual event loop, recording every meaningful moment (a function call, a scheduled timer, a microtask draining, a console log) as a discrete step with a full snapshot of all four panels. Playback simply replays that recorded array of steps.
Your code
โ
โผ
Acorn parser โโโบ AST
โ
โผ
Tree-walking interpreter (generator-based, so `await` can suspend)
โ
โโโ Call Stack โ real push/pop tracking per function call
โโโ Web APIs โ virtual timers & mocked fetch, keyed by virtual clock time
โโโ Microtask Queue โ custom Promise implementation (VPromise) + queueMicrotask
โโโ Callback Queue โ macrotasks released once due, one per event-loop tick
โ
โผ
Full step trace (array of snapshots)
โ
โผ
UI playback (scrub / play / step)
Key design choices:
- Real values, custom control flow. Numbers, strings, arrays, and objects are genuine JS values โ only scheduling primitives (
setTimeout,Promise,queueMicrotask,fetch,console) are intercepted, so the full native standard library "just works." - Generators for
await. The interpreter's statement/expression evaluators are JS generator functions, so anawaitcanyieldand suspend exactly at that point โ mirroring real async function semantics (the caller gets control back immediately with a pending promise). - Deterministic virtual clock. Timer delays advance a virtual clock rather than real wall-clock time, so the whole trace computes synchronously and instantly, no matter the delays used in your code.
.
โโโ index.html # Markup: editor, four visualizer panels, transport controls, full guide article
โโโ styles.css # Dark theme, panel layout, responsive rules, docs/article styling
โโโ script.js # Interpreter core (Acorn-based), virtual scheduler, and UI wiring
โโโ README.md # You are here
Note: if you split the original single-file build into these three files, double-check that:
index.htmlloads Acorn beforescript.js(<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/8.11.3/acorn.min.js"></script>first, then<script src="script.js"></script>at the end of<body>).index.htmlhas<link rel="stylesheet" href="styles.css">in<head>.- All
ids referenced inscript.js(codeInput,sampleSelect,stackBody,webapiBody,microBody,macroBody,consoleBody,stepDesc,stepWhy,stepCount,playBtn,stepBackBtn,stepFwdBtn,speedRange,runBtn,clearBtn,customBadge,gutterHighlight,lineNumbers) still exist inindex.htmlwith matching names.
- Pick a sample from the dropdown, or paste your own JavaScript into the editor.
- Click โถ Run & Trace โ your code executes instantly against the virtual scheduler.
- Use the transport controls at the bottom:
โฎ/โญโ step backward / forward one action at a timeโถ / โธโ auto-play through the whole trace- speed slider โ control playback speed
- Watch the four panels:
- ๐ต Call Stack โ what's executing right now
- ๐ Web APIs โ async work happening in the background
- ๐ฃ Microtask Queue โ promise reactions &
queueMicrotask, always drained first - ๐ข Callback Queue โ
setTimeout/setIntervalcallbacks, one run per loop tick
- Read the description + "why" explanation under the controls for what just happened and why.
- Scroll down for the full written guide if you want the concepts explained from scratch.
Supported
- Closures, all control flow, labeled
break/continue - Destructuring (objects/arrays, defaults, rest) in declarations, params, and assignments
- Spread/rest, template literals, optional chaining (
?.), nullish coalescing (??) - Classes: constructors, methods, getters/setters,
staticmembers,extends/super()/super.method() - Promises (
then/catch/finally/all/race/allSettled/any),async/await setTimeout,setInterval,clearTimeout,clearInterval,queueMicrotask- Full native standard library โ
Math,JSON,Array/String/Object/Map/Setmethods, etc.
Not supported
- Generators (
function*) - True private class fields (
#xis tolerated but not genuinely private) - Decorators
- ES Modules (
import/export) - DOM APIs
fetchis mocked โ it simulates a network delay and returns fake data; there is no real networking
setInterval auto-stops after 8 firings so traces stay finite.
1. Run all synchronous code top-to-bottom.
2. Call Stack empty? โ drain the ENTIRE Microtask Queue
(including new microtasks scheduled while draining).
3. Take ONE task from the Callback Queue, run it.
4. Go back to step 2. Forever.
- Microtasks always win. Every pending
.then()/awaitcontinuation runs before the nextsetTimeoutcallback โ even asetTimeout(fn, 0)scheduled earlier. - Only one macrotask per tick. This is what gives the browser room to repaint between timer callbacks.
- Blocking the stack blocks everything. A long synchronous loop freezes timers, promises, clicks, and rendering alike โ there's only one thread.
The full in-app guide covers this in much more depth: Call Stack mechanics, Web APIs vs. Node APIs, async/await desugaring, rendering timing, Node.js's phase-based event loop (libuv), and common real-world pitfalls (microtask starvation, setTimeout(0) myths, memory leaks from lingering timers).
| Step | Meaning |
|---|---|
| Call Stack push | A function call just started and was placed on top of the stack. |
| Call Stack pop | The function finished and was removed from the stack. |
| Web APIs โ added | An async op (setTimeout, fetch) was handed off to run in the background. |
| Callback Queue โ added | The Web API finished; its callback is now waiting its turn. |
| Callback Queue โ running | Stack + microtasks empty โ the oldest queued callback now runs. |
| Microtask Queue โ added | A promise settled or queueMicrotask was called. |
| Microtask Queue โ running | The next microtask runs โ always before the next macrotask. |
- Vanilla HTML / CSS / JavaScript โ no framework, no build step
- Acorn (via CDN) โ JavaScript parser, used only to produce the AST the interpreter walks
- Generator function (
function*) support - Shareable trace links (encode code in URL)
- Dark/light theme toggle
-
requestAnimationFramelane in the visualizer - Export trace as GIF/video
Issues and PRs are welcome โ this is a static, dependency-light project, so contributing is just: fork, edit index.html / styles.css / script.js, test in a browser, and open a PR.
Do microtasks or macrotasks run first? Microtasks always run first, completely, before the next macrotask โ even ones scheduled while others are draining.
Is setTimeout(fn, 0) really immediate?
No โ it still waits for the current call stack and the full microtask queue to clear first.
Why does fetch show fake data?
There's no real networking in this sandboxed interpreter. fetch is mocked with a simulated delay so its position in the event loop stays accurate, even though the response is fabricated.
Can this run in production apps? No โ this is an educational tool. It's not a JS engine replacement and doesn't aim for 100% spec compliance (see Supported vs. unsupported).
MIT โ free to use, modify, and distribute. Add a LICENSE file with the standard MIT text if you haven't already.