Skip to content
Performance Lab · LAB-05Web Performance

The Hidden Cost of JavaScript

Transfer size is the cheapest part of shipping JavaScript. This dossier separates the four costs a script imposes, network, parse, compile, execute, and shows why the last three dominate on the devices most people actually own.

Level
RESEARCHER
Status
ACTIVE
Experiments
14
Observations
27

Question

Two pages transfer 300KB. One is interactive in 900ms, the other in 5.4s. The network conditions are identical. Where did the difference come from?

Background

A byte of JavaScript is not a byte of an image. An image is decoded once, often on another thread, and never again. A script must be downloaded, parsed into an AST or lazily pre-parsed, compiled to bytecode, executed, and then, if it is hot, re-optimised by the JIT. Every one of those steps happens on the main thread, and every one of them scales with the device's single-core performance.

StageHigh-end laptopMid-range phone
Transfer (4G)~250ms~250ms
Parse + compile~90ms~450ms
Execute~120ms~700ms
Hydration of the same UI~80ms~600ms
Rough cost profile for the same 300KB of gzipped script

Method

  1. 01Record a trace on a throttled CPU profile (4× or 6×) rather than an unthrottled one.
  2. 02Separate 'Evaluate Script' from 'Compile Script' in the performance panel, they have different fixes.
  3. 03Attribute long animation frames with the LoAF API to find which script and which call site.
  4. 04Compare total blocking time before and after removing a single dependency, not after a batch of changes.

Findings

Finding 1. Parse cost is proportional to code shipped, not code run. V8 pre-parses everything it downloads to find function boundaries, then fully parses functions when first called. Shipping a large library and using one function still pays the pre-parse.

Finding 2. Hydration pays for the UI twice. The server rendered the markup; the client then walks the same tree, creates the same component instances and attaches listeners. Hydration cost scales with component count, not with how much of the page the user can see.

Finding 3. Deferring is not the same as removing. A deferred script still costs its full CPU budget; it just costs it later, often exactly when the user first tries to interact. Moving cost past the LCP measurement improves the score without improving the experience.

Finding 4. The cheapest script is the one never requested. Server-rendered HTML with no client component, or a progressively enhanced form, has a parse cost of zero and an execution cost of zero, at every device tier.

Interpretation

The practical hierarchy is: delete it, then do it on the server, then load it later, then make it smaller. Most optimisation effort goes into the last step, which is the weakest lever in the list.

The Why MachineDEPTH 1 / 4

Why is parse cost so much higher on phones?

  1. Because parsing is single-threaded work bounded by single-core performance.

Limitations

Engine behaviour differs. V8, SpiderMonkey and JavaScriptCore make different lazy-parsing and tiering decisions, and all three change between releases. Treat the mechanism as stable and the numbers as perishable.

Further research

  1. 01Measure hydration cost per route on a 4× throttled CPU.
  2. 02Quantify the parse cost of your three largest dependencies individually.
  3. 03Test whether a route needs client-side JavaScript at all.

References

  1. 01Addy OsmaniThe cost of JavaScript
  2. 02V8 blogBlazingly fast parsing
  3. 03web.devLong Animation Frames API