cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Meet the engines

Explore V8, SpiderMonkey, JavaScriptCore, smaller engines, shared pipelines, hosts, embedders, and tools for engine work.

By the end, you can
  • 01
    Name the engine familiesPlace V8, SpiderMonkey, JavaScriptCore, Hermes, QuickJS, and embedded engines in the ecosystem.
  • 02
    Trace the common pipelineFollow source text through tokens, an AST, bytecode, interpretation, feedback, optimization, deoptimization, and GC.
  • 03
    Separate engine from environmentDecide whether a feature belongs to ECMA-262, the host, or the embedder that wires an engine into an app.

Set the engine map for the stage

You already learned the beginner split in Engines, runtimes & hosts, then met Node, Deno, Bun, and edge runtimes in Runtimes. This expert stage looks inside the engine itself. It starts with the big picture so later lessons can zoom into scanners, parsers, bytecode, inline caches, garbage collection, and tiered compilation without redefining every moving part.

Plain definition

A JavaScript engine is the implementation that follows ECMA-262: it scans source text, parses syntax, creates internal code, runs that code, optimizes hot paths when it can, deoptimizes when assumptions fail, and manages JavaScript memory with a garbage collector. The host around it decides what outside world your program can touch.

Real-life analogyEngine and vehicle

Engine knowledge helps you read performance profiles and mysterious runtime messages, but the vehicle still matters. A DOM error is often a browser-host issue; a deopt trace is engine internals; a build failure may be neither.

In real life: The engine turns fuel into motion
In JavaScript: The JavaScript engine turns source text into running behavior
In real life: The car body has seats, lights, dashboard, and doors
In JavaScript: The host has DOM, timers, files, modules, permissions, and UI
In real life: A mechanic can inspect engine timing without redesigning the road
In JavaScript: You can study parsing, bytecode, JIT, and GC without pretending they define every browser API
In real life: The same engine family can power different models
In JavaScript: V8 appears in Chrome, Edge, Node.js, and Deno with different host surfaces

Where the analogy stops: Car engines are physical and stable for years. JavaScript engine internals change quickly, and hosts add policy, security, rendering, networking, and scheduling around the engine.

The previous lesson was Finding memory leaks. That lesson used tooling from the outside. This one names the inside. The next lesson, Scanning source text, follows the very first arrow: characters becoming tokens.

V8, SpiderMonkey & JavaScriptCore

BIG THREE

Most web developers meet three production engine families. V8 is built by the V8/Chromium project and runs in Chrome, Edge, Node.js, and Deno. SpiderMonkey is Mozilla's JavaScript engine in Firefox. JavaScriptCore is WebKit's engine in Safari and WebKit, and Bun uses JavaScriptCore for its runtime.

Tier names below are a snapshot verified against primary sources in September 2026. Treat them as a map, not a contract: engine teams rename tiers, replace backends, and move work between interpreter, baseline, and optimizing tiers.

Engine families, common homes, and tier names at a glance.
EngineWho builds itWhere you meet itTier names to recognizeCareful reading
V8Google; open source through the V8 projectChrome, Edge and other Chromium browsers, Node.js, DenoIgnition, Sparkplug, Maglev, TurboFanFast-moving tier names; inspect the V8 docs for current details.
SpiderMonkeyMozillaFirefoxBaseline Interpreter, Baseline JIT/Compiler, Warp/IonMozilla docs describe WarpMonkey as the top optimizing tier and Ion as the MIR/LIR backend lineage.
JavaScriptCoreApple/WebKitSafari/WebKit, WebKit-based views, BunLLInt, Baseline JIT, DFG JIT, FTL JITWebKit's posts use LLInt, Baseline, DFG, and FTL as the tier names.
HermesMeta/React NativeReact Native appsAOT bytecode for app bundles, interpreter-focused mobile startupReact Native builds JavaScript to Hermes bytecode during release builds.
QuickJS / QuickJS-ngFabrice Bellard and Charlie Gordon; community fork QuickJS-ngSmall embedders, CLIs, tests, constrained productsSmall interpreter with bytecode and reference-counting GCGreat for embedding, not a browser replacement.

Do not memorize every tier yet. The point for this first lesson is orientation: engines usually start with a lower-latency interpreter or baseline tier, collect feedback, then spend more compile time on hotter code. Later lessons unpack why each tier exists and why optimized code must still be able to bail out.

Hermes, QuickJS & embedded engines

The big three are not the whole ecosystem. Smaller engines make different trade-offs: startup over peak throughput, tiny memory over browser compatibility, Java embedding over DOM work, or research and conformance over market share. These engines are especially useful when JavaScript is embedded inside an app, a device, a test harness, or another language.

Smaller engines and why they exist.
EngineTypical homeWhat to remember
HermesReact Native default engineOptimizes startup and memory; release builds compile JavaScript to Hermes bytecode ahead of time.
QuickJS / QuickJS-ngTiny embeddable C engineA small interpreter with fast startup, broad ECMAScript support, and reference counting with cycle removal.
BoaRust implementationExperimental lexer, parser, interpreter, runtime crates, and Test262-driven conformance work.
LibJSSerenityOS / Ladybird ecosystemA browser-oriented JavaScript engine built as part of the SerenityOS project.
JerryScriptInternet of ThingsTargets very constrained devices such as microcontrollers with tens of kilobytes of RAM.
Moddable XSMicrocontroller appsImplements modern JavaScript for the Moddable SDK and focuses on small memory and flash budgets.
GraalJSGraalVM / Java embeddingECMAScript-compliant runtime implemented on GraalVM with Java interoperability and standalone launches.
Embedded does not mean toy

Embedded engines often skip a browser DOM and may avoid a heavyweight optimizing JIT, but they still care about conformance, startup, predictable memory, and safe host bindings. A microcontroller engine and a browser engine solve different production problems.

The shared pipeline

STEP THROUGH

Engines differ internally, but they share a recognizable path: source text becomes scanner tokens, tokens become a parse tree or AST, functions may be lazily parsed, executable bytecode is produced, an interpreter starts running it, inline caches and type feedback record what actually happened, optimizing JITs compile hot paths, guards deoptimize back to lower tiers when assumptions fail, and the garbage collector runs alongside everything to reclaim unreachable objects.

Pipeline walkthrough: source to toy bytecode
Step 0 of 6Ready
Your turn: follow the blue line

Step through the shared engine pipeline in miniature: source text, tokens, AST, bytecode, interpreter result.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const tokens = ["num:2","+/-:+","num:3","*:*","num:4"];const astRoot = "BinaryExpression";const bytecode = ["PUSH 2","PUSH 3","PUSH 4","MUL","ADD"];const result = 14;console.log(result);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Line 2 is the scanner story. Line 3 is the parser story. Line 4 is a teaching bytecode, not V8's Ignition bytecode, SpiderMonkey bytecode, or JavaScriptCore bytecode. It is deliberately tiny so you can see the same shape before later lessons introduce real engine-specific details.

Inspect real tokens, a real ESTree AST, and toy bytecode

LIVE

This playground runs in your browser. Acorn produces the token stream and an ESTree-compatible AST excerpt. A small compiler in this lesson accepts only arithmetic expressions and emits stack bytecode. The bytecode is a model you can step through; it is not copied from any production engine.

Live pipeline: acorn tokens, ESTree AST, toy bytecode
Pipeline driver used by the walkthroughJavaScript
const sourceText = "2 + 3 * 4";const tokens = ["num:2","+/-:+","num:3","*:*","num:4"];const astRoot = "BinaryExpression";const bytecode = ["PUSH 2","PUSH 3","PUSH 4","MUL","ADD"];const result = 14;console.log(result);
Observed artifactsresult 14

Tokens

  1. num 2 0-1
  2. +/- + 2-3
  3. num 3 4-5
  4. * * 6-7
  5. num 4 8-9

ESTree AST excerpt

{
  "type": "BinaryExpression",
  "start": 0,
  "end": 9,
  "operator": "+",
  "left": {
    "type": "Literal",
    "start": 0,
    "end": 1,
    "value": 2,
    "raw": "2"
  },
  "right": {
    "type": "BinaryExpression",
    "start": 4,
    "end": 9,
    "operator": "*",
    "left": {
      "type": "Literal",
      "start": 4,
      "end": 5,
      "value": 3,
      "raw": "3"
    },
    "right": {
      "type": "Literal",
      "start": 8,
      "end": 9,
      "value": 4,
      "raw": "4"
    }
  }
}

Toy bytecode and stack

  1. PUSH 2 stack [2]
  2. PUSH 3 stack [2, 3]
  3. PUSH 4 stack [2, 3, 4]
  4. MUL stack [2, 12]
  5. ADD stack [14]
Try it yourself

Acorn produced 5 tokens, the AST root is BinaryExpression, and the teaching bytecode returns 14.

Tokens and the ESTree excerpt come from acorn in the browser. The bytecode and interpreter are a tiny teaching model, not V8, SpiderMonkey, or JavaScriptCore bytecode.

Try (7 - 1) / 3 and 5 * (2 + 6). Notice that parentheses and precedence change the AST shape before bytecode is emitted. That is why the scanner and parser lessons come before bytecode and JIT lessons in this stage.

Interpreters, JIT compilers, feedback, deopt, and GC

OPTIMIZE

Interpreters are good at getting code running quickly. JIT compilers spend CPU time to generate machine code for hot code. Optimizing JITs rely on speculation: if calls have seen numbers, object shapes, and stable property accesses, optimized code can guard those assumptions and run faster. If a later call breaks an assumption, the engine deoptimizes to a lower tier that can handle the general case correctly.

Feedback and deoptimization teaching model
Step 0 of 9Ready
Your turn: follow the blue line

Step through how feedback can let an engine optimize speculatively, and why a later type change may deoptimize.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
  return a + b;} const feedback = [];feedback.push(typeof 1 + "+" + typeof 2);const first = add(1, 2);const optimizedFor = feedback[0];const later = add("3", 4);const outcome = typeof later === "number"  ? "stay in optimized code"  : "deopt to a lower tier";console.log(first);console.log(outcome);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Later call shape

Changing this choice starts a fresh replay. Real engines collect far richer feedback than this teaching model.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Garbage collection is not a final stage after execution. It is a partner beside the pipeline. Values, objects, functions, closures, bytecode, feedback vectors, optimized code, and host references all affect memory pressure. That is why memory lessons and engine lessons meet again when you read heap snapshots or investigate leaks.

Performance rule

Learn the pipeline to debug and measure better, not to micro-optimize blindly. Engine internals change, and a tiny rewrite can help one tier, hurt another, or be irrelevant beside network, rendering, or algorithmic cost.

Engine vs host vs embedder

BOUNDARIES

ECMA-262 defines the language: syntax, values, objects, execution semantics, and built-ins such as Promise and Array.prototype.map. Hosts define how JavaScript meets the outside world: HTML defines the browser event loop, timers, the DOM integration, and module loading behavior; Node defines process, file modules, package resolution, and its runtime policies. Embedders call engine APIs to create isolates, contexts, native bindings, snapshots, and globals.

Where common names come from.
ExamplesOwnerHow to think about it
let, functions, objects, Array.prototype.map, Promise, JSON, globalThisECMA-262 languageEngines implement these semantics.
DOM, window, document, rendering, user events, many browser module-loading detailsBrowser host standards such as HTML and DOMThe browser embeds an engine and provides page APIs.
Timers, event loop integration, fetch, streams, modules, workersHost specifications and runtime choicesBrowsers and server runtimes often share shapes but differ in policy and limits.
process, node:fs, node:path, package resolutionNode.js hostThese are not JavaScript language features.
Isolates, contexts, native bindings, snapshots, command-line flagsEmbedder-engine APIThe application embedding the engine decides how to create worlds and expose host objects.
Engine feature or host feature?
  • Promise
  • Array.prototype.map
  • document.querySelector
  • requestAnimationFrame
  • node:fs/promises
  • process.versions.v8
  • create a V8 Isolate
  • expose a native object to JS
Try it yourself
0 of 8 correct

Sort each card by who provides it. The explanations are the important part; several cards are intentionally close.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

The short probe below uses only standardized globals, so it runs in the browser editor and in Node. It is intentionally not a DOM or Node-only example.

Portable global probePop out in the code editor (opens in a new tab)JavaScript
console.log(typeof Array.prototype.map);console.log(typeof globalThis);console.log(typeof Promise);

An embedder sketch looks different because it is not ordinary page JavaScript. It is the surrounding application creating an engine instance, a context, and host objects before evaluating user code.

Embedder sketch, not browser JavaScriptJavaScript
// Pseudocode: embedders call engine APIs from C++/Rust/Java.const isolate = createIsolate();const context = isolate.createContext({ console });context.evaluate("console.log(1 + 2)");

Exploring engines with d8, jsvu & eshost

TOOLS

Engine tools are powerful and version-specific. d8 is V8's developer shell. jsvu installs recent engine binaries without compiling them yourself. eshost-cli runs the same snippet across registered engines. node --v8-options lists V8 flags exposed by your Node build. Test262 is the conformance suite engine implementers use to check ECMAScript behavior.

Node 22.23.1 (V8 12.4) real output from this environmentBash
$ node --versionv22.23.1$ node -p "process.versions.v8"12.4.254.21-node.56$ node --v8-options | grep -- --print-bytecode  --print-bytecode (print bytecode generated by ignition interpreter)

To find Chrome's V8 version, open chrome://version in that browser and read the JavaScript/V8 entry. Do not assume Chrome, Node, Deno, and an installed d8 shell all ship the same V8 revision.

Engine exploration commands, shown as reference shapesBash
# V8's developer shell after building V8 or installing a binary with jsvuv8 --print-bytecode demo.js # Install current engine shells without compiling them yourselfnpx jsvu --engines=v8,spidermonkey,javascriptcore,quickjs,hermes,xs # Register shells and compare behavior across enginesnpx eshost-cli --configure-jsvueshost -e 'print(1 + 2)' # Run the official ECMAScript conformance suite when building an enginenpm test -- path/to/test262

The commands are non-runnable in the lesson page because the browser editor is not your shell and most engine binaries are not installed here. The tests do run real Node commands and prove that this Node build reports V8 12.4 and exposes --print-bytecode.

Where engine knowledge helps

You rarely need to write code for a specific JIT tier, but this model helps in production work. It tells you where to look when startup is slow, why syntax errors appear before code runs, why a memory leak can keep closures and DOM nodes alive, why one odd call shape can spoil a hot path, and why a browser-only API fails in a server runtime.

  • When a page starts slowly, ask how much source must be downloaded, scanned, parsed, compiled, and executed before interaction.
  • When a profile shows a hot function, measure before changing code and look for algorithmic or allocation wins first.
  • When a heap snapshot shows retained objects, connect those references to closures, host objects, and GC roots.
  • When code crosses browser, Node, Deno, Bun, or embedded contexts, separate language semantics from host APIs.

Keep Runtime overview, Manuals & specs, and The event loop nearby. They explain the host side that engine internals deliberately do not own.

Common misconceptions

  • “V8 is the JavaScript standard.” V8 is one implementation. ECMA-262 is the language specification.
  • “A JIT always makes code faster.” JITs trade compile time and memory for speed on hot paths. Cold code may stay interpreted or baseline-compiled.
  • “Bytecode means the same thing in every engine.” Engines use different bytecode formats and tiers. This lesson's bytecode is only a teaching model.
  • “The engine owns timers and the DOM.” Hosts wire scheduling, rendering, files, networking, and global objects around the engine.
  • “Garbage collection fixes leaks automatically.” GC reclaims unreachable objects. If your code or host objects still reference data, it remains reachable.
  • “Engine flags are portable production APIs.” Flags are implementation tools and can change, disappear, or behave differently across versions.
Similar words that point at different layers.
WordMeansDoes not mean
EngineECMAScript implementation plus memory and execution machineryA full browser or server runtime
HostThe environment that embeds the engine and defines outside-world APIsThe parser or JIT by itself
RuntimeA complete program such as Node, Deno, Bun, a browser tab, or an edge workerOnly the language spec
EmbedderThe application code that creates engine isolates or contexts and exposes native objectsOrdinary JavaScript application code

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upName the first token

Use the live pipeline. What is the first token kind for 2 + 3 * 4?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const tokenValues = ["num:2", "+", "num:3", "*", "num:4"];
console.log(tokenValues.join(" | "));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFind the final bytecode instruction

    For the toy compiler's 2 + 3 * 4 output, type the final instruction.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const bytecode = ["PUSH 2", "PUSH 3", "PUSH 4", "MUL", "ADD"];
    console.log(bytecode.at(-1));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeClassify a DOM API

      Who provides document.querySelector: the engine, the browser host, Node, or Test262?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRead Node's V8 version

        Which Node property did this lesson use to show 12.4.254.21-node.56?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain a deopt trigger

          Why might optimized code built for number addition bail out after a later call with "1"?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          function add(a, b) {
            return a + b;
          }
          console.log(add(1, 2));
          console.log(add("1", 2));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: engines and their boundaries

            8 QUESTIONS

            Answer with the pipeline and layer boundaries in mind. Code answers are real JavaScript outputs, not guesses.

            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8Which statement best describes a JavaScript engine?

              Choose an answer to see the explanation.

            2. Question 2 of 8Which engine and tier list matches Chrome and Node as of this lesson's sources?

              Choose an answer to see the explanation.

            3. Question 3 of 8What does the acorn-backed toy pipeline compute for 2 + 3 * 4?

              Choose an answer to see the explanation.

            4. Question 4 of 8What does this code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function add(a, b) {
                return a + b;
              }
              console.log(add(1, 2));
              console.log(add("1", 2));

              Choose an answer to see the explanation.

            5. Question 5 of 8Which API belongs to the browser host, not ECMA-262?

              Choose an answer to see the explanation.

            6. Question 6 of 8What does this browser-safe probe print in modern browsers and Node?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(typeof Array.prototype.map);
              console.log(typeof globalThis);
              console.log(typeof Promise);

              Choose an answer to see the explanation.

            7. Question 7 of 8What is d8 useful for?

              Choose an answer to see the explanation.

            8. Question 8 of 8Why should engine knowledge not turn into premature micro-optimization?

              Choose an answer to see the explanation.

            Key takeaways and what comes next

            • V8, SpiderMonkey, and JavaScriptCore are the big browser engine families; Hermes, QuickJS, Boa, LibJS, JerryScript, XS, and GraalJS serve other embedding needs.
            • The shared path is source text to tokens, parser/AST, bytecode, interpreter, feedback, JIT tiers, possible deoptimization, and GC alongside the whole process.
            • ECMA-262 defines language semantics; hosts define DOM, timers, files, modules, event loops, and globals; embedders create engine contexts and native bindings.
            • Use d8, jsvu, eshost-cli, Node's V8 flags, and Test262 as investigation tools, not as portable production dependencies.
            • Engine knowledge is practical when debugging startup, memory, and performance, but measurements matter more than folklore.
            Preview map for the Inside the engine stage.
            Coming lessonWhat it will unpack
            Scanning source textHow bytes and UTF-16 code units become tokens, including ambiguous / and escaped identifiers.
            Parsing and ASTsGrammar, early errors, ESTree, cover grammars, and why parsers sometimes reparse.
            Lazy parsing and startupWhy engines delay work for functions that might never run.
            Scope analysis and bytecodeHow declarations, closures, and bytecode records prepare code for execution.
            Tiered compilation, feedback, inline caches, GCHow engines speed up hot code, bail out safely, and reclaim memory.

            Further reading from primary sources

            Up next: Scanning source text, where the first character stream becomes tokens.

            CompleteFrontend Clear concepts. Working examples.