• Terms of Service

Learn Internet Grow

Tech Skills

  • Learn
  • Books
  • Search
  • Create
  • Measure
  • Secure
  • Share
  • Terms of Service

How V8 Runs JavaScript: Two Optimisers Died, for Two Completely Different Reasons

Filed Under: Create

Load v8.dev/docs/turbofan on 9 September 2026 and V8’s own documentation still introduces its top-tier compiler like this: TurboFan is one of V8’s optimizing compilers leveraging a concept called “Sea of Nodes”. Eighteen months earlier, on the same domain, the team had published a post explaining why it spent three years taking Sea of Nodes back out.

A machine hall where one shaft keeps turning while new plain gearing is fitted around it, with two retired mechanisms on the floor — one worn out, one merely too intricate

That gap is the honest place to start on how V8 runs JavaScript, because the engine is not a design you learn once. It is a building site — rebuilt underneath live traffic five times since 2008, with two optimisers retired outright along the way. And the two retirements have almost nothing in common. Crankshaft was killed by the language it existed to run. Sea of Nodes was killed by what it cost the people maintaining it. Neither was killed by hardware, which is what most engineers guess first.

The documentation is a decade behind the machine

The documentation index has an “Under the hood” section. Loaded on 9 September 2026, it lists exactly two compilation-pipeline pages: Ignition and TurboFan. The strings Sparkplug, Maglev and Turboshaft appear zero times on that page, and v8.dev/docs/sparkplug, v8.dev/docs/maglev and v8.dev/docs/turboshaft all return 404. Half of the shipping pipeline has no documentation page at all.

A beautifully drawn old harbour chart pinned beside a window, through which the real harbour is visible with twice as many piers and a breakwater the chart does not show

The same index describes the collector this way: V8’s stop-the-world, generational, accurate garbage collector is one of the keys to V8’s performance. V8 has been publishing about dismantling exactly that since 2016; by 2019 its own retrospective was describing the result as a mostly parallel and concurrent collector with incremental fallback.

Documentation lags everywhere; the lesson is about sourcing, not sloppiness. A single article, talk or vendor page about V8 is a snapshot of one year, and something in it has probably been replaced since. That includes this one, so every claim below carries a date.

The pipeline as it actually ships:

TierWhat it isLandedV8’s stated reason for adding it
IgnitionBytecode interpreterChrome 53 (Aug 2016), universal in v5.9 (May 2017)Cut the memory that baseline machine code was occupying on low-RAM Android
SparkplugBaseline JIT, no IR at allV8 v9.1 (May 2021)Short-lived sessions never reach the optimiser at all
MaglevMid-tier optimising JIT, SSA over a CFGChrome M117 (Dec 2023)Sparkplug’s ceiling was low; TurboFan’s compile cost was high
TurboFanTop-tier optimising JIT2015; exclusive from v5.9Crankshaft could not absorb new language features

Checked live against Chromium’s own milestone API on 9 September 2026: Chrome milestone 153 is the stable channel, mapped to V8 branch 15.3, with 154 already at stable_cut. Worth one line, because V8 stopped publishing a post per release in June 2022 and replaced the announcement with a rule — Take the Chrome version and divide by 10 to get the V8 version. Four years on, the arithmetic still holds exactly.

Crankshaft: the optimiser the language outgrew

Crankshaft arrived in 2010. V8’s ten-year retrospective records what it bought: machine code twice as fast and 30% smaller than the previous (unnamed) V8 compiler. For seven years it was the fast path for every piece of JavaScript in Chrome.

A fine chisel worn down to a stub beside the simple piece it was made for, with ever more intricate workpieces arriving behind it and nine hand-reground grinding wheels along the wall

Then on 15 May 2017, Launching Ignition and TurboFan retired it, and the sentence V8 chose is unusually specific about the cause:

Furthermore, starting with v5.9, Full-codegen and Crankshaft, the technologies that served V8 well since 2010, are no longer used in V8 for JavaScript execution, since they no longer are able to keep pace with new JavaScript language features and the optimizations those features require.

That names a specific failure mode. Crankshaft’s intermediate representation had to be extended by hand for every new operation, and the same post says what that cost: It is difficult to add support for new language features in Crankshaft, since these features almost always require writing architecture-specific code for nine supported platforms. The bill came to more than ten thousand lines of code per chip architecture.

The pressure, then, was not that programs got bigger or CPUs got wider. It was ES2015. A committee kept adding syntax, and each addition landed on nine hand-written assembly ports. V8 names the clearest symptom outright: Crankshaft was not designed to optimize JavaScript code using structured exception handling, i.e. code blocks demarcated by JavaScript’s try, catch, and finally keywords. A compiler that could not optimise try shipped in the browser for seven years — not neglect: retrofitting it meant nine more assembly ports, and V8’s later retrospective records that multiple engineers had spent months trying to support them, without success.

Deleting Crankshaft and Full-codegen removed 130,380 lines from the tree.

One honesty note: the two V8 posts do not tell an identical story. The March 2025 retrospective lists six reasons for replacing Crankshaft, and only some are about language features — it also names asm.js, cliffs where an edge case could cause performance to drop by a factor 100, and deoptimisation loops that reoptimised on an assumption that had just failed. The 2017 framing is contemporaneous, the 2025 list retrospective, and a fair reading takes both. What survives both is that the fatal constraint was the cost of adding anything new to the IR — and from 2010 to 2017, what kept being new was JavaScript.

Sea of Nodes: the optimiser the compiler engineers outgrew

TurboFan’s answer to that was an intermediate representation designed so that adding an operation would not mean writing assembly. The 2015 announcement names it: a more relaxed sea-of-nodes internal representation (IR) of the code which allows more effective reordering and optimization, with fewer than 3,000 lines of platform-specific code per architecture against Crankshaft’s 13,000–16,000.

The idea is genuinely elegant. In a conventional control-flow graph, instructions inside a block are implicitly ordered whether or not that order matters. Sea of Nodes encodes only real dependencies: a node is an instruction, an edge means one consumes another’s result, and two independent operations with no edge between them simply float, scheduling deferred until the compiler knows most about what will be fastest. Control and memory effects get explicit edges layered back on, because those genuinely cannot float. It was not a V8 invention — the same IR had been running in C2, the HotSpot JVM’s compiler, for over a decade.

A glowing suspended tangle of free-floating thread beside a plain stone staircase, both lit by one lantern that reveals nothing in the tangle and every step of the stairs

On 25 March 2025, V8 published Land ahoy: leaving the Sea of Nodes:

after ten years of dealing with Turbofan and battling Sea of Nodes, we’ve finally decided to get rid of it, and instead go back to a more traditional CFG IR.

And, two sentences later, the measured result:

compile time got divided by 2 compared to SoN, the code of the compiler is a lot simpler and shorter, investigating bugs is usually much easier

Not one of the reasons given is a JavaScript language feature. They are, in V8’s own order:

  • You cannot read it. The post takes a five-line string-concatenation loop, shows its Sea of Nodes graph mid-pipeline, and calls it a messy soup of nodes. Then the quiet part: as a compiler engineer, a big part of my job is looking at Turbofan graphs to either understand bugs, or to find optimization opportunities. Well, it’s not easy to do when the graph looks like this. The IR’s cost was falling on humans, daily.
  • The freedom rarely materialises for JavaScript. To profit from Sea of Nodes most nodes must float free, and that’s not really the case in the typical JavaScript graph, because almost all generic JS operations can have arbitrary side effects. Every memory operation needs an effect input and usually a control input; even division needs one, because of guarded special cases.
  • It is cache-hostile. Lowering introduces new nodes far from the originals, so locality worsens the deeper the pipeline runs: Sea of Nodes suffers on average from about 3 times more L1 dcache misses compared to our new CFG IR, and up to 7 times more in some phases, costing up to 5% of compile time by V8’s own — explicitly a bit handwavy — estimate.
  • Slow compilation is a correctness-adjacent problem in a JIT. This might not be a big deal for an ahead of time compiler, but in a JIT compiler, compiling slowly means that we keep executing slow unoptimized code until the optimized code is ready, while taking away resources from other tasks (eg, other compilation jobs, or the Garbage Collector). And the consequence V8 draws is the sharpest line in the post: the team was forced to think very carefully about the compile time – speedup tradeoff of new optimizations, often erring towards the side of optimizing less to keep optimizing fast. An expensive IR does not merely slow the compiler down; it shrinks the set of optimisations anyone is allowed to write.

That is an optimiser dying of maintenance cost, not obsolescence. Sea of Nodes still runs in C2. It was correct. It simply cost V8’s team more than it returned.

Two deaths, and hardware is not in either autopsy

CrankshaftSea of Nodes (in TurboFan)
Lifespan in V82010 → 15 May 2017 (v5.9)2013 → migration began ~2022, described 25 Mar 2025
Killed by⭐ The language moved⭐ The compiler cost too much to maintain
The stated symptomCould not keep pace with new JavaScript language featuresCompiling is slow; graphs hard to inspect
The measurableten thousand lines of code per chip architecture; 130,380 lines deletedcompile time got divided by 2; ~3× L1 dcache misses
Who bore the costWhoever implemented the next ES featureWhoever debugged a graph on a Tuesday
Replaced byTurboFan (Sea of Nodes)Turboshaft (CFG), plus Maglev on the JS front end

The instinct when a compiler is retired is to reach for hardware — new instruction sets, wider vectors, a memory-model change. Search both explanations and hardware never appears as a cause. The nearest thing to it, the nine architecture ports, is a labour cost rather than a capability gap: the problem was writing the assembly, not what the chips could do. The other near-miss, the L1 cache misses, is not a change either — Sea of Nodes was always cache-unfriendly, and V8 only got a number for it once a CFG rival existed to measure against.

An optimiser, then, can be obsoleted by the language above it or by the engineering budget beneath it, and those failure modes want opposite fixes. Language pressure is answered with a more general IR. Maintenance pressure is answered with a more boring one. V8 did both, in that order, and ended up back at a control-flow graph.

Ignition was a memory decision that turned into a speed one

Two identical crates, one completely filled by a single heavy casting, the other holding the same shape as a compact stack of thin plates with most of the crate left empty

When V8 introduced Ignition on 23 August 2016, the stated problem was RAM, not compile time. Full-codegen produced real machine code for every function on first execution, including functions that run once, and that code has to live somewhere. The 2017 post puts the number plainly: before Ignition, baseline-compiled code typically occupied almost one third of the overall JavaScript heap in Chrome. Ignition emits bytecode instead — between 50% to 25% the size of the equivalent baseline machine code — and interprets it. Shipped for Chrome 53 on Android devices with 512 MB of RAM or less, Ignition reduces the memory of each Chrome tab by around 5%.

Two design choices repay attention. Ignition’s bytecode handlers are not hand-written assembly; they are expressed in TurboFan’s architecture-independent macro-assembly and compiled by TurboFan, eliminating the burden of maintaining nine separate platform ports. The interpreter is built by the optimiser — trivia until you remember that hand-maintained per-architecture code is exactly what killed Crankshaft. And Ignition is a register machine whose special accumulator register is an implicit input and output register for many bytecodes, so left-to-right expression chains keep intermediate values in one place instead of emitting explicit loads and stores.

⚠️ Then the motivation changed, and one number is routinely attached to the wrong year. The often-quoted shrank by a factor of nine on ARM64-based mobile devices is not in the 2016 post and is not a claim about tab memory. It is in the 2017 post, and it describes the memory footprint required for baseline, non-optimized JavaScript code on Chrome M53’s limited-RAM ARM64 devices. Two posts, two numbers, two scopes.

By 2017, paired with TurboFan, Ignition was also delivering speed: TurboFan could consume Ignition’s bytecode directly rather than having to re-compile from source code as Crankshaft did, and V8 reports the switch improving its Speedometer score by 5%-10%, depending on platform and device, with AcmeAir — a Node.js benchmark — running more than 10% faster using V8 v5.9. The interpreter was conceived to save memory and ended up paying in speed once the compiler behind it was rebuilt to read its output.

Two more tiers, because compile time is a budget

Ignition and TurboFan alone leave an obvious hole, and V8 names it in the Sparkplug launch of 27 May 2021: Particularly for short-lived sessions, like loading websites or command line tools, there’s a lot of work that happens before the optimising compiler even has a chance to start optimising, let alone having time to generate the optimised code.

A workshop with a rough bench at one end and a great precision machine at the other, and two newly built benches installed in the long gap between them

Sparkplug (V8 v9.1) exists purely to compile fast. It cheats — its input is Ignition bytecode, so variable resolution, destructuring and the arrow-function-versus-parentheses problem are already solved — and it generates no intermediate representation at all. The price is accepted openly: no IR means limited optimisation, and the whole thing is hand-ported per architecture, tolerable only because it is small. Its neatest trick is organisational: Sparkplug maintains interpreter-compatible stack frames, so V8’s debugger, profiler and stack-trace machinery needed no parallel implementation for the new tier.

Maglev (Chrome M117, 5 December 2023) fills the other half of the gap — a fast optimizing compiler that generates good enough code, fast enough. It does use an IR, and chose one pointedly: a somewhat traditional static single-assignment (SSA) based approach, using a CFG (control flow graph) rather than TurboFan’s more flexible but cache unfriendly sea-of-nodes representation. That was written fifteen months before TurboFan’s own team announced the same conclusion.

⚠️ A citation trap sits in that post. Its benchmark section recites Sparkplug’s historical figures — +45% and +41% over Ignition, +22% over Ignition plus TurboFan — several paragraphs before it discusses Maglev’s own results, and they are widely re-quoted as Maglev’s. Maglev’s own text-stated figure is a compile-time ratio: roughly 10x slower than Sparkplug, and 10x faster than TurboFan.

Garbage collection is a latency project, not a throughput one

V8’s collector has its own decade-long rewrite, codenamed Orinoco, and its 2016 charter states the goal as reduce garbage collection jank and memory consumption while providing high throughput. The 2019 retrospective reports the shape of the result: the project has taken a sequential, stop-the-world garbage collector and transformed it into a mostly parallel and concurrent collector with incremental fallback.

A pendulum swinging without pause while small sweepers clear the floor beneath it, beside a shadowed alcove where an identical pendulum is clamped still above an unswept heap

Why that is a rendering problem rather than a memory one, V8 states in the units a front-end engineer already thinks in: in Chrome, at 60 frames per second, the browser has approximately 16.6 ms to render each frame of an animation. Against that budget, concurrent marking supplies the number that matters: marking done all at once can take several hundred milliseconds for large heaps. More than ten frames, gone in one uninterruptible stall — which is what a user calls a stutter.

The fixes are all the same move. Incremental marking (2011) chopped the work into chunks between which the application could run. Concurrent marking, on by default in Chrome 64 and Node.js v10, moved it off the main thread entirely, using tri-colour marking and a rewritten write barrier so the mutator cannot hide a live object behind the collector’s back; V8’s benchmarks show it reduces the time spent marking on the main thread by 60%–70%. Parallel compaction spread the copying across threads.

None of that reduces the amount of garbage-collection work. It reduces the share that has to happen inside a pause on the thread the user is waiting on. Throughput was never the objective; the worst pause was.

V8 is a library with two landlords

The embedding documentation is blunt about a relationship most people have backwards: Your application can use V8 just like any other C++ library. Chrome is a consumer of V8, not its container.

A single building cut open to show a bright shallow arrivals porch on one side and a deep hall with a fire banked for months on the other, both carried by one unbroken beam

The second consumer arrived almost immediately — 2009 also marked the first release of the Node.js project, which embeds V8 — and in 2017 became a hard constraint rather than a courtesy: We officially recognized Node.js as a first-class V8 embedder alongside Chromium. Since then, it’s impossible for a V8 patch to land if doing so breaks the Node.js test suite.

That commitment is unusually strong because the two tenants want different things. A browser tab is short-lived and startup-sensitive, which is why V8 treats initialisation as its own optimisation target rather than a special case of speed — startup snapshots deserialise a pre-built heap instead of constructing the built-ins from nothing, taking context creation from 40 ms down to less than 2 ms on 2015 desktop hardware, and the code cache does the same across visits. A Node process is long-running and cares about the far end of the tier ladder instead. The test-suite gate is what stops either tenant quietly steering the architecture.

Sixty per cent of what, exactly

One security fact follows directly from everything above, and it is quoted with its denominator sheared off more often than not. From The V8 Sandbox, 4 April 2024:

all Chrome exploits caught in the wild in the last three years (2021 – 2023) started out with a memory corruption vulnerability in a Chrome renderer process that was exploited for remote code execution (RCE). Of these, 60% were vulnerabilities in V8.

That is 60% of in-the-wild renderer memory-corruption exploit chains, not 60% of Chrome’s bugs. The load-bearing part is what kind of bug they are: V8’s vulnerabilities are rarely “classic” memory corruption bugs (use-after-frees, out-of-bounds accesses, etc.) but instead subtle logic issues which can in turn be exploited to corrupt memory. Which is why V8’s own team writes that neither switching to a memory safe language, such as Rust, nor using current or future hardware memory safety features, such as memory tagging, can help with the security challenges faced by V8 today. A JIT bug is a compiler being wrong about what JavaScript can do between two instructions — a correctness error with a memory-corruption consequence. The sandbox that answers it deserves its own article.

The bytecode is the only part that stayed

What persisted across three compiler generations is not a compiler.

Ignition’s bytecode is what TurboFan consumes directly rather than having to re-compile from source code. It is what Sparkplug compiles from, which is the entire basis of that trick. It is what Maglev walks in its prepass before building SSA. Every tier V8 has shipped since 2016 takes Ignition bytecode as input, and Ignition itself is built out of TurboFan’s macro-assembly, so the interpreter is compiled by a compiler whose IR has since been replaced under it.

The stable thing in a system rebuilt this often turned out to be a format, not a component. Crankshaft recompiled from source and died; every tier that reads bytecode instead has been cheap enough that V8 added two in three years. The observation transfers well past JavaScript: in a pipeline you expect to rewrite, the interface between stages is the part worth designing carefully, because it is the part still standing when the stages are not.

And the documentation will be last to hear about it.

❓ Frequently Asked Questions

Is TurboFan still V8’s optimising compiler, or has Turboshaft replaced it? Both, awkwardly. As of the 25 March 2025 post, “TurboFan” names the top tier, but its JavaScript backend runs on Turboshaft’s CFG IR, WebAssembly uses Turboshaft throughout, and the JavaScript front end is being handed to Maglev. Two pieces of Sea of Nodes were still in use at that date, and V8 has published nothing further as of 9 September 2026 — so treat it as in progress, not done.

Why would anyone put an interpreter inside a JIT engine? Because compiled machine code has to be stored, and most functions run rarely. V8’s stated 2016 motivation was memory on low-RAM Android phones, where baseline machine code took almost a third of the JavaScript heap. Bytecode is a quarter to a half the size. Speed came a year later, once TurboFan could read that bytecode directly instead of recompiling from source.

Does a garbage collection pause actually cost me a frame? It did, and that is why Orinoco exists. V8 puts the budget at roughly 16.6 ms per frame at 60 fps, and reports that marking a large heap all at once could take several hundred milliseconds. Concurrent marking cut main-thread marking time by 60%–70%; the total work did not shrink, only the part that blocks you.

Would rewriting V8 in Rust fix its security record? V8’s own security team says no, with a reason: its vulnerabilities are rarely classic memory-safety bugs but logic errors in speculative optimisation that corrupt memory downstream. A memory-safe language stops you dereferencing a dangling pointer; it does not stop a compiler assuming an array’s length cannot change mid-expression when JavaScript lets a callback change it.

Is V8 the same thing as Chrome’s JavaScript support? No, and it is not “the JavaScript engine” either. V8 is a C++ library with a public embedding API; Chrome and Node.js are two consumers with different priorities, and since 2017 a V8 patch cannot land if it breaks Node’s test suite. SpiderMonkey and JavaScriptCore are separate, independently engineered, currently shipping engines.

Why do so many V8 explainers disagree with each other? Because most of them are accurate about a different year. The pipeline gained Sparkplug in 2021 and Maglev in 2023, and swapped TurboFan’s IR from around 2022. A 2017 explainer describing “Ignition and TurboFan” was correct in 2017 and is missing two tiers and a backend swap now. Check the publication date before the content.


📚 Recommended Resources

Some links below are Amazon affiliate links. If you buy through them, this site earns a small commission at no extra cost to you. The free primary sources come first, because on this particular subject they are genuinely better than any book.

Read the engine’s own writing first

Nothing published about V8 by a third party is as good as what V8 publishes about itself, and almost all of it is free.

  • The V8 blog archive — the whole thing, in date order. Sorting by date is the point: an article’s publication year tells you which pipeline it describes. Note that V8 stopped posting per release in June 2022, so the archive thins out after that without the engine slowing down.
  • Land ahoy: leaving the Sea of Nodes (25 March 2025) — the single best compiler-engineering post V8 has published. A production team explaining, with graphs and cache-miss counts, why it abandoned its own signature architecture. Read it even if you never touch a compiler.
  • Launching Ignition and TurboFan (15 May 2017) and Firing up the Ignition interpreter (23 August 2016) — read them as a pair and in that order, because the motivation changes between them.
  • Trash talk: the Orinoco garbage collector (3 January 2019) — the clearest short account of generational collection anywhere, with V8’s frame-budget arithmetic attached.
  • Getting started with embedding V8 — the page that makes the library relationship concrete. If you have ever wondered what Node actually calls, it starts here.
  • Chromium’s milestone API — machine-readable, free, and the fastest way to check which V8 branch is currently shipping before you cite a version.

Books worth the money

Crafting Interpreters — Robert Nystrom. Two complete implementations of one language: a tree-walking interpreter, then a bytecode VM with its own compiler, garbage collector and hash-table object model. It is the shortest honest route to understanding why Ignition emits bytecode rather than machine code, because you build the thing. The entire book is also free to read online at craftinginterpreters.com — buy the print edition because it is worth owning, not because you need it to read the text.

The Garbage Collection Handbook: The Art of Automatic Memory Management — Richard Jones, Antony Hosking and Eliot Moss, second edition. The reference work for everything in the Orinoco section above: generational hypotheses, tri-colour marking, write barriers, concurrent and parallel collection, and the latency-versus-throughput trade that V8 spent a decade paying down. Dense, and the only book on this list you will still be consulting in ten years.

Engineering a Compiler — Keith Cooper and Linda Torczon, third edition. The textbook that will let you read the Sea of Nodes post properly — SSA form, control-flow graphs, instruction scheduling and register allocation are all treated at the level V8’s engineers assume. Pick this over a general compiler survey if what you want is the middle and back end rather than parsing.

ASINs verified 9 September 2026: each ISBN-10 check digit was recomputed from the ISBN-13, and each listing’s title was matched against the book named. ⚠️ Amazon served captcha pages for two of the three requests, so availability is unconfirmed even though the identity is — the titles are right, the stock is not guaranteed.

Newsletter

Tags

Agentic AI Agentic Development AI AI Agents AI Coding Agents Artificial intelligence Backlinks Blockchain Bounded Context Browser ChatGPT Plugins Claude Code CLPR Context Engineering Cross-Chain Cryptocurrency Developer Tools Distributed Systems DLT Domain-Driven Design Email Google Search HBAR Hedera Hedera Hashgraph Internet LLM Machine Learning MCP Microservices Model Context Protocol News NLP Orchestration Photoshop Productivity Saga Pattern SEO Smart Contracts Software Architecture software engineering Twitter Typescript Web3 Writing

Recent Articles

  • QUIC and HTTP/3: The Protocol That Won Is Being Routed Around October 11, 2026
  • The Mark I Perceptron: The Machine That Learned to See October 11, 2026
  • GraphRAG vs Vector RAG: Knowledge Graphs for AI, Explained October 11, 2026
  • Cardano Tokenomics: How ADA’s Supply Actually Works October 10, 2026
  • Transformer Encoder vs Decoder: How to Read the Diagram October 10, 2026
  • Nobody Checks the Verified Badge: The Real State of Git Commit Signing October 10, 2026

Copyright © 2026 · News Pro Theme On Genesis Framework · WordPress · Log in

Content on this site is produced with the assistance of Large Language Models and edited by a human. It is intended for educational purposes — facts may occasionally be incorrect. If you spot an error, please email Larionovdmitri@learninternetgrow.com.

Affiliate Disclosure: This site contains affiliate links to Amazon and other partners. If you purchase through these links, LearnInternetGrow.com may earn a commission at no additional cost to you. We only recommend products we genuinely believe in.

Privacy Policy · Terms of Service