Coinbase native: where its trading screen loses time, and what to fix

Measured from the Netherlands in a logged-in Chrome, October 2026.

In short. On a warm reload the screen is trade-ready after 7.6 s (median of 5 loads), 26 of 27 perpetual trading screens, rank 26 is the only one the measurements leave open; after an uncached reload 8.4 s. The largest bottleneck: The price appears about 5.7 s after the live feed delivers. First change to try: Draw the price, book and order form once their own code and the first feed message are in; do not wait for the rest of the app. Likely saving if fixed: About 4–5 s faster on a warm reload, from 7.6 s to roughly 3 s, mostly by cutting the code-loading waves before the trading panels draw (CBN-1, CBN-2).

Three measurements appear on this page, and their times differ. Rank and results come from the campaign's timed reloads. The diagnostic capture and the recorded reload are single loads made with recording switched on, which slows the page: read them for the order of events and the gaps, not for totals.

Resultscampaign: 5 warm and 5 uncached reloads

Warm reloadmedian 7.6 s, range 7.1 s–7.6 s; loads: 7.6 s, 7.6 s, 7.3 s, 7.1 s, 7.6 s
Uncached reloadmedian 8.4 s, range 7.9 s–9.6 s; loads: 8.4 s, 8.4 s, 9.6 s, 7.9 s, 8.7 s
Standing26 of 27 by median; rank 26 is the only one the measurements leave open. 5.1 s behind the fastest, Thalex (2.4 s): 3.1 times as long. The possible rank is the range the measurements leave open: it counts only the exchanges that clearly beat this one and those it clearly beats. Exchanges whose ranges overlap cannot be told apart.
Loads ready5 of 5 warm, 5 of 5 uncached
Notesorder book or trades drawn as a picture; checked as drawn and updating, values not read; chart checked as drawn and updating, prices not read; recent trades not measured: inactive tab

Where the time goescampaign medians, warm reload

Panels in the order they first showed real, usable data on a warm reload. The coloured part of each row is the time that panel added after the one before it. The last row is trade-ready: every panel is ready and the page has stopped moving.

0 s1 s2 s3 s4 s5 s6 s7 s
Price6.3 s
Order book6.3 s +0.0 s
Order form6.3 s +0.0 s
Account panel7.2 s +1.0 s
Chart7.3 s +0.1 s
Trade-ready7.6 s +0.3 s settling

Stack

React app split into over a thousand small chunks (c_*.js), with Coinbase's own chart; Sentry boot module; OneTrust banner; Google Pay script.

Delivery

www.coinbase.com through Cloudflare over HTTP/2; the HTML is marked no-store and is not cached at the CDN (cf-cache-status DYNAMIC); app files cached for a year. Market data from Coinbase (ws-retail, drb.coinbase.com) and Deribit (www.deribit.com, history.deribit.com). In the 3 October recorded reload Coinbase answered the first page request with HTTP 429 (too many requests) and the page loaded a second time; none of the 10 timed loads did.

HostRoleCDN edge seenConnectResponse
www.coinbase.compageCloudflare Amsterdam13 ms17 ms
api.coinbase.commarket dataCloudflare Amsterdam14 ms13 ms

Median of up to 10 attempts from the test machine. Response time includes the server's own time, so it is not a distance.

What happened, in orderdiagnostic capture 1 October, uncached

From one uncached diagnostic load recorded on 1 October 2026 (cache disabled, browser extensions active), so totals are slower than the campaign's timed loads; file names, sizes, headers, order of events and main-thread times are what the findings rely on. A main-thread time given for a script counts all the work that starts in that script, including code it calls in other files, so it shows where start-up work begins, not what one library costs. Endpoint paths were masked when the capture was saved.

WhenWhat
0.17 sHTML arrives (no-store, not cached at the CDN).
0.39–0.78 sapp-shell.js (0.7 MB compressed, 3.4 MB unpacked) and boot scripts; 1.36 s of main-thread work later starts in c_boot-vendor and 0.68 s in c_boot-sentry.
1.65–2.61 sA 5.1 MB (unpacked; 116 KB compressed) response from www.deribit.com marked no-store, plus four no-store responses from www.coinbase.com of 1.1, 0.5, 0.4 and 0.3 MB.
1.66–1.90 sLive feeds open (ws-retail, streams.drb, drb.coinbase.com); first messages at 2.16–2.62 s; 203 messages on streams.drb before trade-ready.
1.69–8.20 sOne www.coinbase.com request stays open for 6.5 s.
1.9–6.9 sChunks keep arriving: c_CO0lZDQN.js (1.0 MB compressed, 5.4 MB unpacked) at 1.90 s, c_B_3SNTlO.js (0.8 MB, 3.3 MB) at 3.72 s, c_DJwp1o7e.js (0.4 MB, 1.4 MB) at 5.92 s; 1,284 requests to www.coinbase.com before trade-ready.
2.04 sFirst paint.
8.09 sPrice shows.
9.83 sChart drawn: trade-ready in this load. 25 long tasks took 3.3 s of main thread.

Recorded warm reload, 3 October

One warm reload recorded with Chrome's performance trace. Recording slows the page, so read the order and the gaps, not the total.

In this recording the first page request got HTTP 429, and the page loaded a second time 3.2 s later. The times below are from that second load, so the full wait was 10.8 s.

WhenWhat
0.2 sHTML arrives
1.1 sFirst frame sent on a live feed
1.2 sFirst frame received on a live feed
6.9 sPrice ready
6.9 sOrder book ready
6.9 sOrder form ready
6.9 sAccount panel ready
7.7 sChart ready
7.7 sTrade-ready

1324 requests before trade-ready; 10 long main-thread tasks (over 50 ms) before trade-ready, longest 471 ms; 1152 script requests before the first panel; 0 requests over HTTP/1.1.

Request waterfalldiagnostic capture 1 October, uncached

69 of the 1330 requests (the page itself, the longest and the largest) started before market data was ready in the 1 October uncached capture, in start order. The outlined part of a bar is waiting for the server; the solid part is downloading. Rows are numbered so a request can be pointed to. The numbers on the right are the size received and the total time. Vertical lines mark when a panel had data in this capture.

PageScriptData requestFont, media, other filewaiting for the serverdownloading

0 s0 s1 s1 s2 s2 s3 s3 s4 s4 s5 s5 s6 s6 s7 s7 s8 s8 s9 s9 s1www.coinbase.com6 kB24 ms2www.coinbase.com/app-shel…715 kB386 ms3www.deribit.com116 kB952 ms4www.coinbase.com27 kB861 ms5www.coinbase.com6515 ms6www.coinbase.com/a_RAxmqt…275 kB75 ms7api.coinbase.com1 kB989 ms8www.coinbase.com/c_CO0lZD…1.0 MB192 ms9www.coinbase.comopen10www.coinbase.com398 B972 ms11login.coinbase.com2 kB1425 ms12www.coinbase.com7 kB1516 ms13www.coinbase.com12 kB1514 ms14www.coinbase.comopen15www.coinbase.comopen16www.coinbase.com410 B1242 ms17www.coinbase.com/c_B_3SNT…853 kB346 ms18cdn.cookielaw.org/otBanne…132 kB76 ms19www.coinbase.com/c_Fbj2LB…137 kB145 ms20www.coinbase.comopen21www.coinbase.com/c_DJvjbR…106 kB389 ms22www.coinbase.com/c_DJwp1o…405 kB951 ms23www.coinbase.com/c_CXy0GX…38 kB770 ms24www.coinbase.com/c_C7an1S…47 kB772 ms25www.coinbase.com9 kB765 ms26www.coinbase.com/c_CVAdx8…95 kB745 ms27www.coinbase.com/c_53EK4B…6 kB771 ms28www.coinbase.com/c_DOIQcd…783 B773 ms29www.coinbase.com/c_3aSqHz…1 kB774 ms30www.coinbase.com/c__ZIQ2k…4 kB774 ms31www.coinbase.com/c_Bi5WvB…1 kB772 ms32www.coinbase.com/c_QpTqLp…2 kB777 ms33www.coinbase.com/c_B3y5Fl…2 kB778 ms34www.coinbase.com/c_DxXcaU…2 kB780 ms35www.coinbase.com/c_CCEr6f…1 kB760 ms36www.coinbase.com/c_I6BfE5…2 kB753 ms37www.coinbase.com/c_2FcWFP…2 kB754 ms38www.coinbase.com/c_BZ6qHc…2 kB766 ms39www.coinbase.com/c_Djr7za…1 kB764 ms40www.coinbase.com/c_gQKk9Q…2 kB767 ms41www.coinbase.com/c_Bklw59…5 kB765 ms42www.coinbase.com/c_ZZqhBz…2 kB761 ms43www.coinbase.com/c_DSAcWB…1 kB766 ms44www.coinbase.com/c_B1JkPN…1 kB768 ms45www.coinbase.com/c_BrCiTn…1 kB769 ms46www.coinbase.com/c_D0iTvc…3 kB774 ms47www.coinbase.com/c_CkQ1Xl…2 kB768 ms48www.coinbase.com/c_CiMetu…925 B771 ms49www.coinbase.com/c_ZU0SjJ…887 B769 ms50www.coinbase.com/c_DdDeQG…2 kB770 ms51www.coinbase.com/c_DN0C2G…2 kB771 ms52www.coinbase.com/c_9u34Rr…1 kB782 ms53www.coinbase.com/c_CbRTB3…1 kB789 ms54www.coinbase.com/c_BBAU9x…1 kB790 ms55www.coinbase.com/c_iT6ry6…1 kB773 ms56www.coinbase.com/c_BNdnmy…1 kB785 ms57www.coinbase.com/c_Dj22Eg…3 kB783 ms58www.coinbase.com/c_C1JrO6…1 kB777 ms59www.coinbase.com/c_J3Y5mB…6 kB782 ms60www.coinbase.com/c_qQT4Vb…6 kB778 ms61www.coinbase.com/c_DnZPnJ…1 kB783 ms62www.coinbase.com/c_DloXlH…6 kB778 ms63www.coinbase.com/c_C5Dg07…12 kB787 ms64www.coinbase.com/c_C-SAte…1 kB786 ms65www.coinbase.com/c_T2fnpI…16 kB790 ms66www.coinbase.com/c_lVu2iy…1 kB780 ms67www.coinbase.com/c_BgXD6C…1 kB755 ms68www.coinbase.com30 kB815 ms69www.coinbase.comopenprice 8.1 schart 9.8 s
Download this figure as an image:

Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached

CBN-1The price appears about 5.7 s after the live feed delivers.grade C
Evidence
3 October recorded warm reload: first feed message at 1.2 s, price at 6.9 s. In between, the app loads its code files in waves until 5.5 s (CBN-2) and sends its last data requests from 6.4 s. Coinbase's servers answer the 90 data requests in a median 0.14 s, so the wait is in the page, not the servers. 1 October uncached capture: first messages at 2.16 s, price at 8.09 s.
Gap observed
5.7 s between the first feed message and the price, in the 3 October recording
Holds back
Price (median 6.3 s in the timed loads).
Fix
Draw the price, book and order form once their own code and the first feed message are in; do not wait for the rest of the app.
Where
Trading route start-up: the modules the trading panels wait for.
Verify
DevTools → Performance: price paint within 200 ms of the first ticker message.
Likely saving
Up to ~4 s, together with CBN-2
CBN-2About 1,150 code files load in waves before anything is usable, even from the browser cache.grade B
Evidence
3 October recorded warm reload: 1,152 script requests before the first panel, 1,151 of them from the browser cache, in bursts at 1.0 s, 2.0–3.5 s and 4.0–5.5 s with pauses between them: each batch runs before the next is requested. 1 October uncached capture (cache disabled): 1,284 requests to www.coinbase.com (8.2 MB compressed) before trade-ready; large chunks still start at 3.7 s and 5.9 s. 1 October timed loads (same page, SPCX market): 1,146 or 1,147 script requests before trade-ready in all 9 loads that completed, all under /assets/sw-cache/, with a last wave of several hundred files between 5 and 6 s; the largest are c_B_WsmIIU.js (5.5 MB unpacked) at 1.59 s, c_C1e8gZ1B.js (3.4 MB) at 3.22 s and c_DgywP-YC.js (1.4 MB) at 5.10 s.
Gap observed
4.5 s across the recorded warm-load script waves
Holds back
Every panel: nothing is shown before this ends.
Fix
Build one bundle (or a handful) for the perpetuals route, so the files of the late waves at 3.2 s and 5–6 s are requested at the start. Until then, list the route's chunks as modulepreload links in the HTML so the browser fetches them in one go.
Where
Bundler chunking (the c_*.js split) and the perpetuals route's imports; the HTML head for the preload links.
Verify
DevTools → Network, filter JS: under 100 script requests before the price, and no new wave of script requests after 2 s.
Likely saving
Part of the ~4 s in CBN-1; more on a first visit
CBN-3About 7 MB (unpacked) of uncacheable JSON on every load: the full Deribit instrument list and five product-list requests.grade B
Evidence
1 October uncached capture: www.deribit.com answers with 5.1 MB unpacked (116 KB compressed), cache-control "no-store, public, max-age=10", at 1.65–2.61 s; four no-store responses from www.coinbase.com of 1.1, 0.5, 0.4 and 0.3 MB run alongside. Compressed, the five together are only 250 KB, so the cost is parsing, not download. The 1 October timed loads (same page, SPCX market) name them: /api/v2/public/get_instruments on www.deribit.com, and /api/v3/brokerage/products on www.coinbase.com, requested 5 times within 0.3 s (1.2, 0.5, 0.4, 0.3 and 0.02 MB unpacked). The names come from a different load than the sizes and headers, matched by order and size.
Measured
about 7 MB of no-store JSON: 5.1 MB from Deribit and 1.1, 0.5, 0.4 and 0.3 MB from Coinbase
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Call get_instruments with the currency and kind the page shows, not the full list. Merge the five /api/v3/brokerage/products requests into one that returns only the perpetuals. Give both a cache lifetime of minutes: the Deribit response already says max-age=10 but no-store cancels it.
Where
The get_instruments call to www.deribit.com and its cache-control header; the product-list loader behind /api/v3/brokerage/products.
Verify
DevTools → Network, filter "get_instruments" and "brokerage/products": one request each, under 100 KB unpacked, or "(disk cache)" on a normal reload.
Likely saving
Not separable from the start-up wait: about 7 MB less JSON to parse on every load
CBN-4About 2 s of main-thread work starts in two boot scripts.grade C
Evidence
1 October uncached capture: work that starts in c_boot-vendor takes 1.36 s and work that starts in c_boot-sentry 0.68 s. These figures count everything a script sets off, not the time spent in its own code, so they show where the start-up work begins, not what the error-reporting library itself costs. 3 October recorded warm reload: the longest single task is 471 ms at 1.90 s.
Gap observed
1.36 s and 0.68 s of main-thread work that starts in the two boot scripts
Holds back
Start-up as a whole; which panel waits for this work is not established.
Fix
Record a start-up profile and group it by script to find the functions behind the 1.36 s and 0.68 s; then start error reporting after the first price paint.
Where
c_boot-vendor and c_boot-sentry, loaded at 0.39 s.
Verify
DevTools → Performance → Bottom-up, group by URL: time under each boot script before the first paint; no single task over 200 ms.
Likely saving
Unknown until profiled; at most the 0.68 s of work that starts in c_boot-sentry
CBN-5The chart is drawn 1.0 s after the price, although its candle data arrives at 1.4 s.grade C
Evidence
Campaign warm medians: price 6.3 s, chart 7.3 s; the last panel is the chart in 2 of the 5 warm loads, the account panel in 2, and both together in 1. 1 October timed loads (same page, SPCX market): the candle request to history.deribit.com (get_tradingview_chart_data) is sent at 1.29 s and answered by 1.36 s, then sent again at 1.90 s; the chart settings (/api/v3/brokerage/user_chart_config, for the market and for __GLOBAL__) are requested at 4.39 s and again at 7.19 s; the price shows at 6.63 s and the page is trade-ready at 8.07 s.
Gap observed
1.0 s between the price median and the chart median (two separate medians, not a wait measured inside one load)
Holds back
Chart (median 7.3 s in the timed loads).
Fix
Request user_chart_config once, together with the candle request at 1.3 s, and mount the chart with the price panel, not after it.
Where
Chart component loading; the user_chart_config loader, which runs twice.
Verify
DevTools → Network, filter "user_chart_config": one request per key, starting before 2 s; the chart appears within 200 ms of the price.
Likely saving
~1 s (chart and account panel at 7.2–7.3 s, price at 6.3 s)
CBN-625 GraphQL requests and 16 analytics requests are spread over the load, most of them before the price.grade C
Evidence
1 October timed loads (same page, SPCX market): 25 requests to www.coinbase.com/graphql/query between 0.71 and 6.96 s and 16 to as.coinbase.com (13 to /amp, 3 to /metrics) before trade-ready at 8.07 s; 21 and 14 of them start before the price shows at 6.63 s. Which of the GraphQL requests the trading panels wait for is not visible from outside.
Measured
25 GraphQL and 16 analytics requests before trade-ready; 21 and 14 before the price
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Combine the GraphQL queries the trading page needs into one request sent at start-up, and send analytics after trade-ready.
Where
The GraphQL client (batching) and the as.coinbase.com analytics client.
Verify
DevTools → Network, filter "graphql": 5 or fewer requests before the price; filter "as.coinbase.com": none before the price.
Likely saving
Unknown: it depends on which queries the panels wait for
CBN-7The consent banner and Google Pay load before the price shows.grade B
Evidence
1 October uncached capture: OneTrust otSDKStub.js at 3.67 s and otBannerSdk.js (0.5 MB unpacked) at 4.42 s; pay.google.com/pay.js (0.24 MB unpacked) at 6.92 s; the price shows at 8.09 s. The 1 October timed loads (same page, SPCX market) show the banner fetching its preferences, its layout file and its logo twice each. Their own main-thread cost is small (OneTrust about 16 ms in the 3 October recording); they add requests and scripts to a start-up that is already crowded.
Measured
about 0.8 MB of consent-banner and Google Pay script before the price
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Load the consent banner and pay.js after trade-ready, and pay.js only where a payment can be made.
Where
The OneTrust loader (otSDKStub.js) and the Google Pay loader.
Verify
DevTools → Network, filter "cookielaw" and "pay.google": no requests before the price shows.
Likely saving
Small: about 0.8 MB less script before the price
CBN-8Coinbase rejects some of its own start-up requests with HTTP 429, and the page keeps retrying.grade B
Evidence
1 October timed loads (same page, SPCX market): in 3 of the 9 loads that completed, coinbase.app_picker.AppPickerService/GetApplicationsV2 is answered with HTTP 429 (too many requests) 6 times, about once a second between 1.7 and 7.3 s; /session and a second request for the page itself also get 429. In the 3 October recorded reload the first page request got 429 and the page loaded a second time. These loads were made minutes apart by our test, so a normal visitor may see this less often.
Measured
6 rejected GetApplicationsV2 requests per load, in 3 of 9 loads
Holds back
Not established: the captures do not show which panel waits for this.
Fix
On HTTP 429, wait for the Retry-After time and back off instead of retrying every second; check why a normal page load reaches the rate limit.
Where
The AppPickerService client and the rate-limit rules for /session and the page route.
Verify
DevTools → Network, filter "status-code:429": none during a page load; after a forced 429, retries are spaced out.
Likely saving
None measured on trade-ready; it removes a second page load when the page request itself is rejected
CBN-9Scripts force the browser to recalculate layout 74 times during the load.grade C
Evidence
3 October recorded warm load: 74 forced layout recalculations costing 189 ms in total. The logged-out follow-up recording (which captured the TradingView chart) names the callers: the largest, 78 ms at 1.74 s, is inside function A of assets/sw-cache/e_CxV4naul.js, and a 33 ms one at 2.85 s is inside chart-bottom-toolbar.js. The same recording shows a 158 ms task at 0.98 s in which about 47 ms is spent inserting elements into the page (appendChild) from e_CxV4naul.js, which looks like the loader that adds each code chunk to the page.
Measured
74 forced layout recalculations, 189 ms, in the recorded load
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Batch the geometry reads and DOM writes in e_CxV4naul.js and the chart toolbar so the browser lays out once; check whether the chunk loader inserts elements one by one.
Where
e_CxV4naul.js (the function the profile labels A) and chart-bottom-toolbar.js.
Verify
DevTools → Performance: no "forced reflow" warnings during the load; the chunk loader's task under 50 ms.
Likely saving
Up to ~0.2 s of main thread

Grade B: the capture shows the problem directly. Grade C: the order of events in the capture points to it; the dependency is for the team to confirm. The order is our judgement of each finding's effect on trade-ready, largest first; it is not sorted by gap. A gap is the time between two observed events or the time one piece of work takes; gaps overlap and do not add up. Where a finding is about size or count and no time was measured, the row says what was measured instead. Holds back names the panel the finding delays. Nothing here is graded higher, because no fix was confirmed by changing a page and measuring again.

Savings are estimates from the timeline, not measured after a change. They overlap, so they do not add up; the summary gives the combined estimate.

How this was measured

Timings: 5 warm and 5 uncached reloads in a seeded random order across all exchanges, each judged against a checklist frozen for this page before the campaign; trade-ready is the first moment every checklist panel shows real data, nothing is still loading, and the layout holds still for 0.5 s. The findings on this page come from separate recorded loads, which are never part of the timings. Full method.