Chrome DevTools · Performance Panel

Reading a performance profile

A hands-on field guide to the two views that matter most: the network waterfall and the flame chart. Every figure below is interactive, click and toggle to build the mental models a senior engineer uses to read a trace in seconds.

Built around a real capture: sample: a CSR login SPA CSR · Angular LCP gated by JavaScript
01

The color language

Before anything else: in both the CPU overview and the flame chart, color encodes the kind of work, not the file. Learn these five and half the panel reads itself.

ScriptingJS: evaluate, compile, function calls, timers, event handlers
RenderingRecalculate Style, Layout (reflow), hit-testing
PaintingPaint, Composite layers, image decode
LoadingParse HTML, network, resource decode
System / IdleGC, profiler overhead, waiting, other

A wall of one color is a diagnosis in itself. A mostly-yellow profile is CPU-bound on JavaScript, exactly the shape of the sample trace used throughout this guide.

02

Anatomy of a network request

In the Network track, each request is one bar. Its shape tells you where the time went. Click a segment to inspect it, then switch the scenario to see how the same bar reshapes when the bottleneck changes.

start finish
Pale segment = waiting on the server (TTFB). Solid segment = downloading bytes. Whiskers = connection/queue and the finish tail.
Tip

Click a part of the bar

Each phase is selectable. In real DevTools, hover any bar for the exact breakdown (Queueing, DNS, Connect, SSL, Request sent, Waiting, Download) plus Priority and Initiator.

03

The waterfall tells a dependency story

The vertical shape is the real signal. Bars that start at the same X were discovered together; a staircase means each request had to wait for the previous one. Toggle the two patterns.

Six requests discovered by the HTML at once, they run in parallel and finish early.
How an expert reads it

A second wave of requests that starts only after a script executes was requested by that JS, not by the HTML. When something appears late, ask who initiated it: that is the lever.

The same, in real DevTools · Network

A real capture, annotated

Real Chrome DevTools network waterfall of a client-side-rendered login SPA

Hover or tap a row to spotlight it on the capture.

  1. A

    The ~10 app bundles share one start X, the HTML referenced them together, so they download in parallel.

  2. B

    The HTML document: the very first request. Everything else is discovered from it.

  3. C

    config.js → oauth.json → level.json: a serial chain, each waits for the one before it.

  4. D

    A second wave (en.json, es_ES.json, master.model.json, authenticate) fires after the app boots, requested by the JS, not the HTML.

  5. E

    The stylesheet arrives late and at low priority (purple), well after the scripts.

  6. F

    FCP and LCP (green) both land after the scripting wave, a JavaScript-gated paint.

04

The flame chart, decoded

Two axes trip everyone up at first. X is real time. Y is call-stack depth: each row is a function called by the row above it. Width is total time including children. Click any frame to read it.

A long task (red corner) running the Angular bootstrap. The LCP marker lands inside a scripting frame, visual proof the paint is JS-gated.
Selected frame

Click a frame

Total time is the bar’s width. Self time is the part not covered by any child below it: that’s where the CPU actually burns.

Self vs total is the concept that separates novice from expert: a fat bar with a fat child has almost no self time, the child is the culprit, not the parent. Toggle Show self time above to see each frame’s own work hatched.

The Timings track: line up your own milestones

Above the flame chart, the Timings track holds the browser's own markers, FCP, LCP, DCL, and onload, so you can read what executed before each one. Add your own with performance.mark() and performance.measure(): the labels land right there in the track, so instead of guessing which block is your route change or data fetch, you read it straight off the timeline.

Initiators: reading the arrows

Select an event and DevTools draws arrows to what scheduled it and what it schedules next. A setInterval re-arms itself, so its firings chain across the timeline. Click a firing.

A setInterval(fn, ~400 ms) firing repeatedly: each firing is its own Task; the arrows link each one to the next.

In real DevTools

Initiator arrows in the Chrome DevTools flame chart
The dark arrows are the real thing: with the recurring timer selected, DevTools dims unrelated frames and links each firing to the next.
setInterval (unknown) row in the Bottom-up table
In Bottom-up it surfaces as setInterval (unknown) under [unattributed]: 8.9 ms, all self time.

Not everything is on the main thread

The flame chart above reads the Main thread, where most work piles up. But a capture has parallel tracks, and a frame on another thread is work that is not blocking your interactions. Knowing which track owns a frame is half the fix.

TrackWhat runs there
MainYour JS, style, layout, and paint setup: almost everything in this guide. The thread every interaction competes for.
WorkerWeb and Service Worker JS: parsing, sync, caching, off-main-thread compute. A frame here is work you already moved off Main.
Raster · CompositorTurning paint commands into pixels and assembling layers. Compositor-only animations (transform, opacity) live here, not on Main.
GPUThe GPU process drawing the final frame to the screen.

That is the whole goal of the Animations & compositing section: move work off Main onto the compositor. When Main is jammed, always ask whether the work could live on one of these other threads instead.

The same, in real DevTools · Flame chart

A real capture, annotated

Real Chrome DevTools flame chart of a client-side-rendered login SPA

Hover or tap a row to spotlight it on the capture.

  1. A

    Task rows with a red hatched corner are long tasks (>50 ms), the thread is blocked for their whole width.

  2. B

    Evaluate script / Event: load (yellow) = the bundles executing at the top level.

  3. C

    invokeTask · runTask = Zone.js internals, a framework running change detection.

  4. D

    Layout Shifts: the diamonds are CLS events, firing as the page settles.

  5. E

    Frames: an 834 ms frame = a stalled thread. Long frames are the jank users feel.

  6. F

    The LCP marker cuts through a scripting task, the largest paint waits on JavaScript.

05

One dataset, three views

The Flame chart, Call tree and Bottom-up are the same samples, aggregated differently. This is the exact tree from the flame chart above; switch the lens and watch what each one surfaces.

The punchline: the flame chart's widest bars (Task, Evaluate Script) are mostly their children. Bottom-up, sorted by self time, exposes the real hogs, detectChanges and process, buried deep and invisible from the top. That gap is why experts open Bottom-up first.

The same three views, in real DevTools

Bottom-up & Call tree, for real

Real Chrome DevTools Bottom-up table
Bottom-up, sorted by self time (grouped by owner here). Browser [unattributed] work is 57%; first-party JS (aurora-demo.test) is 944 ms / 38% of the main thread, the real cost. Third parties are tiny. Expand any row to drill into functions.
Real Chrome DevTools Call tree
Call tree, top-down. First-party JS shows Total 601 ms / 24% flowing down the hierarchy, while [unattributed] holds 71% of total, the mirror of Bottom-up's self-time view.
06

The master diagnosis: CPU-bound or network-bound?

Line the network track up against the main thread. If the thread is idle waiting on a bar, you have a network problem. If it’s solid with work while nothing downloads, it’s a CPU problem. Flip between the two.

Verdict

CPU-bound

JS is fully downloaded by ~700 ms, but the main thread stays solid yellow until FCP at 1.85 s. The fix is on the CPU side: less JS to execute, code-splitting, deferring the bootstrap, not faster downloads.

07

LCP & the four phases

The trio's loading metric. LCP (Largest Contentful Paint) marks when the biggest element in the viewport, usually the hero image or headline, finishes painting. The panel splits it into four phases; read which one dominates and you know whether the fix is on the network or the main thread.

TTFB and load time are network (blue), load delay is idle (grey), render delay is rendering (purple). Click a phase for its cause and fix.

The whole diagnosis is one question: did the resource arrive late (load delay + load time) or did the render arrive late (render delay)? Thresholds: good ≤ 2.5 s, needs work ≤ 4 s, poor > 4 s. In the panel, the LCP marker sits in the Timings track; hover it to highlight the element. You don’t estimate these sub-parts by hand: open the Insights sidebar and the LCP by phase insight breaks the metric into exactly these four sub-parts, while LCP request discovery flags a resource the browser found too late.

08

Frames & layout shifts (CLS)

Two more overview tracks carry the visual-stability story. Frames shows every painted frame: uniform green is smooth, a fat block is a stalled thread. Layout Shifts marks content jumping, the raw material of CLS.

Watch a layout shift happen

9:41checkout
Confirm your order
Layout Shifts

CLS 0.00

Nothing has shifted yet. Load the late image and watch the “Pay now” button jump under your finger.

CLS = impact fraction × distance fraction, summed within a session window. The cure is boring: reserve the space (width/height, aspect-ratio) so late content lands without pushing anything.

09

INP & interactions

Loading metrics are only half the story. INP (Interaction to Next Paint) measures responsiveness, from a tap to the next painted frame. It splits into three phases; read each and you know exactly which one to fix.

Input delay (grey) · Processing (yellow) · Presentation (green). Click a phase for its cause and fix.

Feel it: click the button

Thresholds: good ≤ 200 ms, needs work ≤ 500 ms, poor > 500 ms. In the panel, open the Interactions track to see each interaction's three phases, then attribute the processing block to a handler in Bottom-up. Scroll jank is the same story: long tasks stealing the frame budget while you scroll. When there is no real interaction to measure, Total Blocking Time stands in for INP in the lab: the blocking part of every task past 50 ms, summed. Drive TBT down and INP usually follows.

10

Animations & compositing

The Animations track (purple) lists every property a CSS transition or @keyframes animates. One question decides whether it is cheap: is it composited? Composited animations run on the compositor, off the main thread, and stay smooth even under load. Everything else recomputes layout or paint every frame, and DevTools marks it Compositing failed.

Composited-safe: transform, opacity, and filter. Animating anything that changes box size or pixels (width, margin, top/left, box-shadow, color) forces the main thread to work each frame. Select an animation in the panel and read the Compositing failed reason in Summary; the fix is almost always to move it to transform/opacity.

11

Recalculate Style, Layout & the render pipeline

Whenever styles change, the browser runs a fixed pipeline before the screen updates: Recalculate Style → Layout → Paint → Composite. On Main each stage is its own event; reading them tells you what changed, how much it cost, and whether it stole the frame. Click a stage, and switch what triggered it.

Click a stage to see what it does and what to read in Summary. Switch the trigger to change the pipeline's cost.

The event's Initiated by tells you where the recalc came from, here a Parse stylesheet (guide.css) finishing and invalidating the page. Read Elements affected on Recalculate Style, and Nodes that need layout / Layout root on Layout: a root of #document with hundreds of nodes is a page-wide reflow. When Recalc + Layout run long they turn the Task into a long task that blocks the main thread, so a tap landing on it waits, and that delay lands in INP.

12

The expert reading workflow

Nobody eyeballs the flame chart cold. This is the order the pros actually work in.

Before you record: the pre-flight

A reading is only as trustworthy as its capture. Set these before you hit record, or every conclusion below inherits the error.

SettingWhy it matters
CPU throttle 4–6×Your dev machine is far faster than a real phone. Without it, long tasks shrink and you miss the very jank users feel.
Network throttleMatch a real connection (Slow / Fast 4G). It reshapes the waterfall and moves when LCP actually lands.
Clean profile / incognitoExtensions inject their own scripts and long tasks. Capture without them or you profile the extensions, not your site.
Disable cacheTick it for the first-visit story; a warm cache hides the real loading cost. Leave it on for the repeat-visit story.
Screenshots onThe filmstrip lets you line each metric up with what the user actually saw on screen.
Recording modeReload for a load profile; wrap record / stop around a click or scroll for an interaction (INP) profile. Match it to your question.
  1. Capture like your users

    Get the pre-flight above right first: throttling, a clean profile, the recording mode that matches your question. A fast desktop hides the very jank your users feel.

  2. Scope the range

    Drag-select from Nav to LCP in the overview. Everything below recomputes to that window.

  3. Summary, then Insights

    Read the category pie: mostly Scripting → CPU-bound, mostly Loading → network/parse. Then let the Insights sidebar triage for you: LCP by phase, render-blocking requests, forced reflow, DOM size, duplicated JS. It turns reading into a checklist.

  4. Bottom-up by self time

    Sort functions by self time, group by URL. The top row is your offender and its owner (1st vs 3rd party). This is the real “flame graph”.

  5. Walk the long tasks

    Every red-cornered task >50 ms: click, read the stack, attribute it, and follow its initiator to the real cause.

  6. Cross-check the network

    Is the thread idle against a bar (network) or busy while nothing loads (CPU)? Confirm the diagnosis.

  7. Read under the markers

    What executes beneath the FCP/LCP lines, and check Frames & Layout Shifts for jank and CLS.

  8. Verify the fix

    Record before, change one thing, record after, and compare. Confirm the metric you targeted actually moved; a greener flame chart that did not move LCP or INP fixed nothing.

Pro habits

  • Dim or ignore third parties to see only the code you own; add noisy frames to the ignore list and the flame chart collapses to what matters.
  • Glance at the Thread pool and GPU tracks: work there is already off the main thread, so it is rarely your INP problem.
  • Save the trace (the download icon) to diff two runs, share a repro, or reload it later; a profile is a document, not a moment.

The Insights sidebar: your triage checklist

Recent Chrome folded Performance Insights into the panel. The Insights sidebar runs these checks on your trace automatically and ranks them by estimated savings, so the first pass is a checklist, not a hunt. The ones you will lean on:

InsightWhat it flagsWhere it points
LCP by phaseWhich of the four LCP phases dominates the metricThe resource-late vs render-late split; see the LCP section.
LCP request discoveryThe LCP image was found late or not prioritizedPreload it and set fetchpriority="high".
Render-blocking requestsCSS or JS that delays the first paintDefer or inline it; read the waterfall for the chain.
Network dependency treeLong chains of critical requestsFlatten the chain, preconnect to key origins.
Document request latencySlow TTFB, redirects, or missing compressionBackend and CDN, not the front end.
Forced reflowJavaScript reading layout right after writing itBatch reads before writes; see Recalculate & Layout.
DOM sizeA DOM large enough to slow style and layoutTrim and flatten nodes; see Recalculate & Layout.
Layout shift culpritsThe exact elements that movedReserve their space; see Frames & CLS.
Third partiesHow much load time third-party code costsDefer and prune; see Request signatures.
Duplicated JavaScriptThe same module shipped more than onceDedupe the bundles; attribute it in Bottom-up.

Each insight estimates the metric it would save, so you triage by impact: open the biggest one, read its detail, then jump to the flagged event in the flame chart. It is the fastest way into a trace you have never seen.

13

Reading function-name signatures

Speed comes from recognizing code by its frame names, then attributing it by URL in Bottom-up. Filter by where the code lives; a few you will meet constantly:

Frames you seeWhat it isSo what
Frameworks & app code
performUnitOfWork · beginWork · commitRootReact reconcilerRender then commit. Long here means big re-renders; memoize and split state.
workLoopSync · flushWork · performWorkUntilDeadlineReact SchedulerThe time-slicing loop; bursts under interactions. Check what scheduled the update.
invokeTask · runTask · drainMicroTaskQueue · ZoneZone.js (Angular)Change detection wrapping every callback.
patch · flushJobs · reactiveEffectVue reactivityRe-render from reactive deps; long means a wide reactive graph.
hydrateRoot · hydrate · commitRootSSR hydrationThe cost of making server HTML interactive, a classic “slow before interactive” INP trap.
jQuery.fn.init · dispatch · handlejQuery eventsLegacy event handling, often buried in old plugins.
Third-party
gtag · ga · (gtm.js)Google Analytics / Tag ManagerTags firing on load and on interaction; defer and prune them.
AppMeasurement · s.t · s.tlAdobe Analytics (AppMeasurement)Beacon fires on load and on each tracked link; batch calls and defer the non-critical ones.
_satellite · satelliteLib · getVarAdobe Launch / DTM (tag manager)Rules engine that injects other tags; long here means a heavy rule fired on load or interaction.
__tcfapi · OneTrust · Cookiebot · DidomiConsent management (CMP)Often gates everything until consent resolves; load it lean and early.
mbox · at.js · adobe.targetAdobe Target (A/B & personalization)Can block render to avoid flicker; a slow mbox delays LCP and the first paint.
optimizely · activate · _vwo_$A/B testing & experiments (Optimizely, VWO)Anti-flicker snippets hide the page until they resolve; watch for blocked paints.
fbq · fbeventsMeta Pixel (conversion tracking)Fires on load and on custom events; defer until after the page is interactive.
hj · _hjSettings · FS · claritySession replay & heatmaps (Hotjar, FullStory, Clarity)Records DOM mutations and input; a steady main-thread cost on interaction-heavy pages.
analytics.track · analytics.pageCustomer data platform (Segment)Fan-out layer that feeds many destinations; one call multiplies into downstream work.
Intercom · zE · driftChat & support widgetLoads its own late bundle and fonts; a large deferred payload, often self-inflicted INP.
googletag · defineSlot · pubadsAd tech (GPT)Slot setup and refresh; long tasks plus layout shift.
processDNA · loadFlash · newColFuncDevice fingerprinting / anti-botAkamai/BioCatch-style sensor. Heavy, deferrable.
Browser & runtime
Evaluate Script · Compile Code · (anonymous)Top-level module executionBootstrap cost. Attribute by URL in Bottom-up.
Recalculate Style · LayoutRendering (purple)Style and reflow; layout thrashing if interleaved with script in a loop.
Parse HTML · Parse StylesheetLoading & parseBig DOM or CSS; Parse Stylesheet is what feeds Recalculate Style.
Function Call → setTimeout / requestAnimationFrameScheduled workTimer-driven tasks; common INP offenders. Trace the initiator.
Minor GC · Major GCGarbage collection (V8)Memory pressure; frequent Major GC means allocation churn.
JSON.parseDeserializationLarge state or hydration payloads parsed on the main thread.

Don’t recognize a stack? Group Bottom-up by URL to see the owner (1st vs 3rd party), or right-click a frame and Add script to ignore list to collapse code you don’t own. The frame name plus its owner is the whole diagnosis.

14

Reading request signatures

Frame names identify code in the flame chart; request origins identify it in the waterfall. Recognize the vendor by domain, then judge it by where the request sits: on the critical path, render-blocking, or a spare connection you can safely defer. Filter by what the request does:

Domain you seeWhat it isSo what
Analytics & tags
googletagmanager.com · google-analytics.comGoogle Tag Manager / GA4GTM loads early then injects child tags; load it async and audit what it pulls in, the container is only as light as its tags.
assets.adobedtm.com · *.omtrdc.net · *.demdex.netAdobe (Launch, Analytics, Audience Manager)demdex/omtrdc do ID-sync and beacons on separate origins; preconnect if they gate personalization, otherwise defer.
cdn.segment.com · api.segment.ioSegment (CDP)Loads analytics.js then fans out to destinations; one snippet multiplies into many requests, so watch the cascade.
Consent (CMP)
cdn.cookielaw.org · geolocation.onetrust.comOneTrustOften gates other tags until consent resolves; it sits on the critical path, so load it early and lean.
consent.cookiebot.com · consentcdn.cookiebot.comCookiebotSame gate pattern; a slow CMP response delays everything downstream, including analytics and pixels.
sdk.privacy-center.org · app.usercentrics.euDidomi / UsercentricsRender-blocking if loaded sync; confirm the banner script isn’t holding the first paint.
Experiments / A-B
cdn.optimizely.com · logx.optimizely.comOptimizelyAnti-flicker snippet hides the page until it decides; measure the hidden time against LCP.
dev.visualwebsiteoptimizer.comVWOSame synchronous hide pattern; a slow response stalls the paint for every visitor.
*.tt.omtrdc.net · at.jsAdobe Targetmbox calls can block render to avoid flicker; keep the mbox round-trip off the critical path where you can.
Marketing pixels
connect.facebook.net · facebook.com/trMeta PixelLoads fbevents.js then fires /tr beacons; defer until after interactive, it rarely needs to be early.
analytics.tiktok.com · static.ads-twitter.comTikTok / X pixelsConversion beacons; none are on the critical path, so batch and delay them.
bat.bing.com · googleads.g.doubleclick.netBing / Google AdsRemarketing tags, often duplicated via GTM; prune the duplicates and load late.
Session replay
static.hotjar.com · script.hotjar.comHotjarLoads a recorder plus settings; a steady main-thread cost, sample or disable it on heavy pages.
edge.fullstory.com · rs.fullstory.comFullStoryRecords DOM mutations continuously; a persistent CPU tax during interaction, watch INP.
*.clarity.msMicrosoft ClaritySame recorder pattern; lighter, but still uploads on interaction and adds listeners.
Media & embeds
youtube.com/embed · i.ytimg.com · *.googlevideo.comYouTube embedPulls an iframe, player JS, and thumbnails; use a facade (lite embed) that loads the player only on click.
player.vimeo.com · f.vimeocdn.comVimeo embedSame iframe weight; facade or lazy-load it below the fold.
maps.googleapis.com · maps.gstatic.comGoogle MapsHeavy JS plus map tiles; show a static map image and load the interactive map on interaction.
Fonts & CDN
fonts.googleapis.com · fonts.gstatic.comGoogle FontsCSS on one origin, files on another; preconnect to gstatic or self-host to cut two extra hops.
use.typekit.net · p.typekit.netAdobe Fonts (Typekit)Blocking CSS that then fetches font files; preload the CSS and set a fallback to avoid FOIT.
cdn.jsdelivr.net · unpkg.com · cdnjs.cloudflare.comPublic JS/CSS CDNsA third-party origin on the critical path; self-host critical libs to drop the extra connection.
Payments
js.stripe.com · m.stripe.networkStripeLoads Stripe.js and background fraud signals; only load it on checkout, not site-wide.
paypal.com/sdk/js · paypalobjects.comPayPal SDKA large SDK; gate it behind the cart/checkout route, not the global bundle.
google.com/recaptcha · gstatic.com/recaptchareCAPTCHALoads on every page if included globally; scope it to the forms that actually need it.

In the Network panel, add the Domain column (right-click the header) or the 3rd-party filter to group these in one pass; in the Performance panel, hover a network bar to read its URL. Origin plus purpose is the whole attribution.

Interactive field guide for reading the Chrome DevTools Performance panel. The interactive figures are stylized recreations for teaching; the real DevTools screenshots come from a synthetic client-side-rendered login SPA captured for this guide. Colors follow the panel’s activity categories.