- Evidence
- 1 October capture: seven api.backpack.exchange responses over 45 KB start at 2.17–2.18 s and total 8.1 MB unpacked (the largest 2.7, 2.2 and 1.9 MB; six are compressed and come from the CDN cache with lifetimes of 5 to 600 s). Five route payloads of about 0.8 MB each add 3.8 MB at 2.40–2.90 s. A timed load names the routes: /en/markets.json, /en/lend.json and the trade pages for BTC_USD, BTC_USD_PERP and MU.US_USD_PERP, so four of the five (3.1 MB) are for other pages or markets. It also names the API calls at start-up: /api/v1/tickers, /markets, /assets, /depth and /klines. The order book shows at 5.01 s.
- Measured
- about 12 MB of JSON before the book appears: 8.1 MB from the API and 3.8 MB of route payloads, 3.1 MB of those for other pages
- Holds back
- Probably the book (median 3.4 s in the timed loads): the timing points to it, but the captures do not show that this panel waits for it.
- Fix
- Stop prefetching the route data for markets, lend and other instruments before trade-ready, and ask the tickers, markets and assets endpoints only for what the trade page shows.
- Where
- Next.js link prefetching for /_next/data/…/en/*.json; the callers of /api/v1/tickers, /markets and /assets.
- Verify
- DevTools → Network, filter "_next/data": one route payload before the book shows; total JSON before the book under 3 MB.
Backpack: where its trading screen loses time, and what to fix
Measured from the Netherlands in Chrome, logged out, 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: 4 warm and 4 uncached reloads
| Warm reload | median 4.0 s, range 3.7 s–4.4 s; loads: 4.4 s, 4.1 s, 3.9 s, 3.7 s |
| Uncached reload | median 4.4 s, range 4.1 s–4.9 s; loads: 4.3 s, 4.4 s, 4.9 s, 4.1 s |
| Standing | 10 of 27 by median; possible rank 7 to 13. 1.5 s behind the fastest, Thalex (2.4 s): 1.6 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 | 4 of 4 warm, 4 of 4 uncached |
| Notes | measured logged out (fewer panels to load); 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
Next.js, bundled with Turbopack and rendered on the server; TradingView chart library (library.js, chart-widget-gui); Inter font from rsms.me.
Delivery
HTML rendered on the server and cached at CloudFront for 10 minutes (s-maxage=600), a miss in this load; market data from api.backpack.exchange; the chart library is served with max-age=0, while the app's own chunks are immutable for a year. On a warm reload 91 of 303 responses are re-checked with the server.
| Host | Role | CDN edge seen | Connect | Response |
|---|---|---|---|---|
| backpack.exchange | page | CloudFront Amsterdam | 12 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.47 s | HTML starts after a CloudFront miss (server-rendered, 753 KB unpacked). The 3 October recorded load: 0.51 s. |
| 0.63–1.20 s | Eleven script chunks, up to 1.0 MB unpacked each. |
| 0.99 s | Page header shows the market. |
| 1.63 s | The live feed opens; handshake at 2.43 s, 0.8 s later; first message at 2.94 s. |
| 2.17–2.87 s | Five api.backpack.exchange responses: 2.7, 2.1, 1.9 and 0.5 MB unpacked, plus 96 KB uncompressed: about 7 MB of JSON. |
| 2.27 s | First paint. |
| 2.40–2.90 s | Five more backpack.exchange responses of about 750 KB unpacked each (3.8 MB together), three of them the same size. |
| 2.71–4.40 s | The chart library (library.js, 2.75 MB unpacked, max-age=0) takes 1.7 s; chart-widget-gui follows at 4.65–5.83 s. |
| 2.91 s | Price shows. |
| 5.01 s | Order book shows, 2.1 s after the feed's first message. |
| 5.91 s | Chart drawn: trade-ready in this load. |
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.5 s | HTML arrives |
| 1.3 s | First market-data request |
| 1.8 s | Price ready |
| 1.8 s | First frame sent on a live feed |
| 2.2 s | First frame received on a live feed |
| 3.3 s | Chart ready |
| 3.8 s | Order book ready |
| 3.8 s | Order form ready |
| 3.8 s | Trade-ready |
285 requests before trade-ready; 10 long main-thread tasks (over 50 ms) before trade-ready, longest 275 ms; 77 script requests before the first panel; 0 requests over HTTP/1.1.
Request waterfalldiagnostic capture 1 October, uncached
70 of the 253 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 fileStylesheetwaiting for the serverdownloading
Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached
- Evidence
- 1 October capture: socket to ws.backpack.exchange created at 1.63 s, connected at 2.43 s, first message at 2.94 s; book at 5.01 s. All 20 older timed loads request api.backpack.exchange/api/v1/depth twice with the same address. The requests take 0.25 to 1.1 s; in one load 0.9 s each (2.84–3.73 s, then 3.82–4.73 s, with the book at 5.22 s), and the 3 October recording shows a similar pair (1.31–2.21 s and 2.36–3.32 s, book at 3.78 s). In those two loads the second depth request ends about 0.5 s before the book shows. In the campaign the warm medians are price 2.5 s, book 3.4 s, order form 3.7 s and chart 4.0 s; the order form is the last panel in 2 of the 4 warm loads.
- Gap observed
- 2.07 s between the first feed message and the order book
- Holds back
- Book (median 3.4 s in the timed loads).
- Fix
- Request the depth snapshot once, at the same time as the socket is opened, and check why /api/v1/depth sometimes takes about 1 s; draw the book when the first snapshot is in.
- Where
- Order-book start-up: the two callers of /api/v1/depth and the depth endpoint itself.
- Verify
- DevTools → Network, filter "depth": one request, started before 1.5 s; book paint within 200 ms of its response.
- Evidence
- 1 October capture: library.8fdacc60e5256d6fcc84.js (2.8 MB unpacked, 0.7 MB compressed) is served with "public, max-age=0" and was a CDN miss, taking 1.7 s (2.70–4.40 s); chart-widget-gui then takes another 1.2 s; chart at 5.91 s. The app's own chunks are served with max-age=31536000, immutable, so only the chart files have this problem. In the campaign the chart is last in all 4 uncached loads and in 2 of the 4 warm ones.
- Gap observed
- 1.7 s for library.js and 1.2 s for chart-widget-gui
- Holds back
- Chart (median 4.0 s in the timed loads).
- Fix
- Serve the TradingView files with content hashes and max-age=31536000, immutable, and preload library.js on the trade route.
- Where
- Static asset headers for /charting_library/.
- Verify
- DevTools → Network: library.js from disk cache on reload.
- Evidence
- 474 ms to the first byte on 1 October, 480 ms on 3 October. The page already has s-maxage=600 with stale-while-revalidate, but was a CloudFront miss in the capture.
- Gap observed
- 0.47 s to the HTML first byte on 1 October; 0.48 s on 3 October
- Holds back
- Every panel: nothing is shown before this ends.
- Fix
- Find why the trade page misses the CloudFront cache despite s-maxage=600 (per-user cookies or query strings in the cache key are the usual cause), or serve a static shell.
- Where
- CloudFront cache key and policy for the trade route; Next.js route caching.
- Verify
- DevTools → Network → document: x-cache Hit, under 100 ms.
- Evidence
- 1 October capture: InterVariable.woff2 from rsms.me, 353 KB, requested at 2.03 s (2.03–2.19 s). It needs its own connection to another host and is the whole variable font.
- Measured
- one 353 KB font from a third-party host
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Host a subset of Inter on the app's own domain and preload it.
- Where
- The @font-face rule that points at rsms.me.
- Verify
- DevTools → Network, filter "woff2": the font comes from the app's own host and is under 100 KB.
- Likely saving
- Small on a reload; about 0.3 MB and one extra connection less on a first visit
- Evidence
- 1 October capture: 147 script requests, with groups of 27 at 4.5 s and 26 at 5.5 s after the first 78. 3 October warm recording: 66 between 3.5 and 4.0 s, of which 53 start before the order book is ready at 3.78 s. All 10 timed loads show the same early and late groups. One warm task still blocks for 275 ms at 1.03 s.
- Measured
- two late groups of about 26 script requests each
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Preload the chunks of the later waves on the trade route, or bundle them with the first group.
- Where
- Bundler chunking for the trade route; the HTML head.
- Verify
- DevTools → Network, filter JS: no new group of script requests after 2 s.
- Likely saving
- Unknown
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: 4 warm and 4 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.