- Evidence
- 1 October capture: 726 ms to the first byte, served from S3 through CloudFront with no-store and x-cache Miss. 3 October recorded load: 696 ms. A timed load shows the same 0.7 s on other small requests to the same host: version.json (45 bytes) takes 0.71 s, and the page address /trade/BTC-USD is fetched two more times at 0.69 and 0.71 s each. One capture does not prove every visit misses the cache.
- Gap observed
- 0.73 s to the HTML response
- Holds back
- Every panel: nothing is shown before this ends.
- Fix
- Let CloudFront cache the HTML shell (short max-age with stale-while-revalidate) instead of no-store, and fetch the page address and version.json once, off the start-up path.
- Where
- S3 object metadata or CloudFront cache policy for index.html and version.json; the code that re-fetches /trade/BTC-USD.
- Verify
- DevTools → Network → document Timing: waiting for server response under 100 ms; x-cache Hit; one request for the page address.
Extended: where its trading screen loses time, and what to fix
Measured from the Netherlands in a logged-in Chrome, October 2026.
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 reload | median 5.1 s, range 4.6 s–6.1 s; loads: 5.3 s, 5.1 s, 4.7 s, 4.6 s, 6.1 s |
| Uncached reload | median 5.4 s, range 5.1 s–5.8 s; loads: 5.4 s, 5.8 s, 5.4 s, 5.4 s, 5.1 s |
| Standing | 21 of 27 by median; possible rank 13 to 24. 2.6 s behind the fastest, Thalex (2.4 s): 2.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 ready | 5 of 5 warm, 5 of 5 uncached |
| Notes | 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.
Stack
React, built with Vite; TradingView chart library from cdn.extended.exchange; two wallet SDKs (Dynamic and Privy) plus WalletConnect/Reown, viem and starknet-crypto; a service worker.
Delivery
HTML from AWS S3 through CloudFront, marked no-store; in the capture it missed the edge cache (x-cache: Miss). 225 of the app's static files carry no cache-control header. On a warm reload a service worker answers 236 of the 414 requests made before trade-ready.
| Host | Role | CDN edge seen | Connect | Response |
|---|---|---|---|---|
| app.extended.exchange | page | CloudFront Amsterdam | 13 ms | 10 ms |
| api.starknet.extended.exchange | market data | — | 242 ms | 233 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.
| When | What |
|---|---|
| 0.73 s | HTML arrives: a CloudFront miss to S3 (no-store). The 3 October recorded load measured 0.70 s. |
| 0.91–1.47 s | Startup scripts download in parallel: dynamic-labs (0.9 MB compressed, 3.4 MB unpacked), viem (2.3 MB), privy (2.3 MB), starknet-crypto (1.4 MB), components (1.1 MB), reown (0.5 MB), walletconnect (0.45 MB): about 12 MB unpacked, most of it wallet and chain code. |
| 1.87 s | First paint. Over the whole load, 1.29 s of main-thread work starts in index.js. |
| 2.12 s | The live feed opens (app.extended.exchange); handshake done at 2.62 s; its first message arrives only at 5.95 s. |
| 2.50 s | A 2.1 MB file from iconic.dynamic-static-assets.com (Dynamic's icons); the same 2.1 MB file is fetched again at 5.65 s. |
| 2.96 s | The Privy iframe loads its script chunks. |
| 4.15 s | A 1.0 MB JSON response sent without compression (no-store); the same 1.0 MB response is requested again at 5.60 s. |
| 5.67 s | The page header shows the market; price at 6.31 s; order book at 7.42 s. |
| 7.76 s | Chart drawn: trade-ready in this load. 23 long tasks took 3.4 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.
| When | What |
|---|---|
| 0.7 s | HTML arrives |
| 1.2 s | First market-data request |
| 2.7 s | First frame sent on a live feed |
| 2.8 s | Price ready |
| 2.8 s | account-info ready |
| 3.0 s | First frame received on a live feed |
| 4.2 s | Order form ready |
| 4.3 s | Chart ready |
| 4.3 s | Order book ready |
| 4.7 s | positions ready |
| 5.7 s | Trade-ready |
410 requests before trade-ready; 10 long main-thread tasks (over 50 ms) before trade-ready, longest 189 ms; 251 script requests before the first panel; 0 requests over HTTP/1.1.
Request waterfalldiagnostic capture 1 October, uncached
70 of the 308 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.
PageScriptFont, media, other fileData requestImagewaiting for the serverdownloading
Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached
- Evidence
- All requested at 0.91 s, before the first paint at 1.87 s: dynamic-labs-O19mXtmw.js 3.5 MB unpacked, viem-IUfPZCfD.js 2.4 MB, privy-Cs_vOTKE.js 2.3 MB, starknet-crypto-_hw84zza.js 1.5 MB, components-C8PHz8Zq.js 1.2 MB, reown-BzSNX7oM.js 0.5 MB and walletconnect-OZZDLUeQ.js 0.5 MB. Dynamic and Privy are two wallet SDKs on the same page. The WalletConnect wallet list (1.2 MB unpacked) follows at 2.54 s.
- Measured
- about 12 MB of startup JavaScript, including two wallet SDKs and chain libraries
- Holds back
- Start-up as a whole; which panel waits for this work is not established.
- Fix
- Keep one wallet SDK, and load it and the chain libraries after the trading screen is shown or on the first wallet action.
- Where
- Wallet provider set-up in the app shell; Vite manual chunks for dynamic-labs, privy, viem, starknet-crypto, reown and walletconnect.
- Verify
- DevTools → Network: dynamic-labs, privy and viem start after the first chart paint.
- Evidence
- All 20 older timed loads request /api/v1/info/markets twice (990 KB, 0.4 s apart) and the Dynamic icon file (icons/sprite.svg, 2.1 MB unpacked, cached for only 10 minutes) twice. In the 1 October capture the two 1.0 MB JSON responses are marked no-store and sent without compression (4.15 and 5.60 s). In the 3 October recorded warm load two responses of that size stay open from 2.5 and 2.9 s until 5.4 s, after the chart and book are ready. The name comes from different loads than the headers, matched by host, size and order.
- Measured
- two 1.0 MB market-list responses and two 2.1 MB icon-file fetches
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Request /api/v1/info/markets once and reuse the result; serve it with br or gzip. Load the Dynamic icon file once, or only when the wallet modal opens.
- Where
- The code that requests /api/v1/info/markets (it goes out twice); compression settings for /api/v1; Dynamic icon loading.
- Verify
- DevTools → Network, filter "info/markets": one request, with content-encoding br or gzip.
- Evidence
- Content-hashed app files (220 scripts, 3 fonts, 1 stylesheet, 1 image) are served without cache-control, so each browser decides for itself how long to keep them. The CDN does hold them (x-cache Hit) and the service worker serves them on a warm reload, so this costs most on a first visit and after a new release.
- Measured
- 225 static files without a cache-control header
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Serve hashed assets with cache-control: max-age=31536000, immutable.
- Where
- S3 metadata or CloudFront response headers policy for /assets/.
- Verify
- DevTools → Network → Headers on dynamic-labs-*.js: the cache-control header is present.
- Evidence
- 1 October capture: feed handshake at 2.62 s, first message at 5.95 s, price at 6.31 s. The price follows the first message by 0.36 s, which suggests the wait is for the message and not for drawing. 3 October recorded warm load: the first frame is sent at 2.68 s and the first one received at 3.01 s, and the price shows at 2.78 s, before that socket has delivered anything, so on a warm load the first price probably comes from another source. The captures do not keep the frames, so they do not show when the subscription is sent.
- Gap observed
- 3.3 s between the feed handshake and its first message; the price follows 0.36 s later
- Holds back
- Probably the price (median 2.9 s in the timed loads): the timing points to it, but the captures do not show that this panel waits for it.
- Fix
- Send the market subscription as soon as the socket is open, and find why no message arrives for 3.3 s after the handshake on an uncached load.
- Where
- Feed client start-up; the code that sends the first subscription.
- Verify
- DevTools → Network → WS → Messages: the subscription is sent within 100 ms of the socket opening and the first message arrives within 0.5 s.
- Evidence
- All 10 ranked loads have the positions panel last (warm medians: chart and book 4.3 s, positions 4.6 s, trade-ready 5.1 s; median wait inside the loads 0.78 s). During that wait the positions panel is still animating or not yet filled in all 10, the chart is still animating in all 10, and images are still loading in 5. Recorded warm load: chart and book at 4.35 s, the chart's last animation gone by 5.03 s. That load's trade-ready of 5.68 s is 0.65 s late because of a pause in our own sampling, so it is not the page's time.
- Gap observed
- 0.78 s median between chart and book and trade-ready inside the ten ranked loads
- Holds back
- Trade-ready itself: the page counts as ready only when nothing in a panel is still animating or loading.
- Fix
- Find what the positions panel waits for after the market panels are shown, and record a reload with DevTools → Animations to see what still animates in the chart and the positions panel after their data is drawn.
- Where
- The positions panel and its data; the chart panel's loading states.
- Verify
- DevTools → Performance: the positions panel fills within 0.3 s of the order book; nothing in the chart animates after the candles appear.
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.
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 (this page's stored loads were scored again afterwards with one change: a dash in the positions table is read as no positions, where it was first read as still loading); 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.