- Evidence
- /assets/index-BYeI0AHq.js is 3.1 MB compressed and 9.3 MB unpacked (0.34–0.76 s); 1.55 s of main-thread work starts in the react chunk; 19 long tasks add up to 3.2 s before trade-ready. On the 3 October warm reload, with the files cached, two tasks still block for 327 ms (at 0.60 s) and 314 ms (at 2.31 s). In the warm campaign loads the first panels show at 2.8 to 2.9 s at the earliest.
- Gap observed
- 3.2 s of main-thread time across 19 long tasks
- Holds back
- Start-up as a whole; which panel waits for this work is not established.
- Waterfall rows
- row 2: 3.1 MB received, 9.3 MB unpacked, max-age=2592000
- Fix
- Split index-BYeI0AHq.js by route and move wallet, chain and other-page code out of the trading page's first load.
- Where
- Vite build (manualChunks, dynamic imports).
- Verify
- DevTools → Coverage: unused bytes in index-*.js on the trade page.
ApeX: 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 4.8 s, range 4.6 s–4.9 s; loads: 4.9 s, 4.6 s, 4.8 s, 4.8 s, 4.9 s |
| Uncached reload | median 5.1 s, range 4.7 s–5.2 s; loads: 5.1 s, 5.2 s, 5.1 s, 4.7 s, 5.0 s |
| Standing | 18 of 27 by median; possible rank 13 to 20. 2.4 s behind the fastest, Thalex (2.4 s): 2.0 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 |
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 (library.js); two Ethereum libraries (viem and ethers) plus the MetaMask SDK and WalletConnect; Google reCAPTCHA; AppsFlyer.
Delivery
omni.apex.exchange served by nginx over HTTP/2; HTML no-cache; app files cached 30 days; 45 static files without a cache-control header; three fonts shipped as uncompressed TTF.
| Host | Role | CDN edge seen | Connect | Response |
|---|---|---|---|---|
| omni.apex.exchange | page | — | 12 ms | 174 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.18 s | HTML arrives. |
| 0.34–0.76 s | index-BYeI0AHq.js downloads: 3.1 MB compressed, 9.3 MB unpacked; with it viem (455 KB unpacked), ethers (431 KB) and CSS. The react chunk later starts 1.55 s of main-thread work. |
| 0.77 s | reCAPTCHA (recaptcha__nl.js, 829 KB unpacked) loads; it is loaded a second time at 1.41 s. |
| 1.16 s | MetaMask SDK (539 KB unpacked). |
| 1.86 s | Page header shows the market. |
| 1.91–2.64 s | A 3.1 MB response from omni.apex.exchange sent without compression, probably the zklink WebAssembly module. |
| 2.02 s | WalletConnect wallet registry (1.2 MB unpacked); fetched again at 3.68 s, taking 1.7 s. |
| 2.15–2.75 s | The chart library (library.js, 2.4 MB unpacked). |
| 2.22 s | First paint. |
| 4.51 s | Price and order book show. |
| 6.74 s | Chart drawn, 2.2 s after the book: trade-ready in this load. 19 long tasks took 3.2 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.2 s | HTML arrives |
| 1.4 s | First market-data request |
| 2.0 s | First frame sent on a live feed |
| 2.6 s | First frame received on a live feed |
| 3.5 s | Price ready |
| 3.5 s | Order book ready |
| 3.5 s | Recent trades ready |
| 3.5 s | Order form ready |
| 3.5 s | positions ready |
| 3.5 s | assets ready |
| 3.5 s | margin ready |
| 4.1 s | Chart ready |
| 4.8 s | Trade-ready |
340 requests before trade-ready; 10 long main-thread tasks (over 50 ms) before trade-ready, longest 327 ms; 122 script requests before the first panel; 0 requests over HTTP/1.1.
Request waterfalldiagnostic capture 1 October, uncached
69 of the 318 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. Shaded rows are named in a finding below, tagged with its ID.
PageScriptImageData requestwaiting for the serverdownloading
Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached
- Evidence
- 1 October capture: omni.apex.exchange sends 3.1 MB over the wire for one response at 1.91–2.64 s, with no content-encoding. The timed loads name a file of exactly that size: /assets/zklink-sdk-web_bg-xmAq9B20.wasm (3,110,973 bytes, 2.65–3.19 s uncached). The match is by size and host, from a different load. On a warm reload the file comes from the cache. The two cross-load matches here are by size and host. Another response, probably /api/v3/symbols, is 757 KB unpacked (41 KB compressed, marked no-cache).
- Gap observed
- 0.73 s from requesting the 3.1 MB file to its completion
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Serve .wasm files with brotli or gzip (WebAssembly usually shrinks to less than half), and load the zklink SDK when a signature is needed, not at start-up.
- Where
- nginx compression types (add application/wasm); the import of the zklink SDK.
- Verify
- DevTools → Network, filter "wasm": content-encoding br or gzip; the request starts after the first panels show.
- Evidence
- 1 October capture: reCAPTCHA is loaded twice (849 KB unpacked, 357 KB compressed each, at 0.77 and 1.41 s), WalletConnect's wallet list twice (1.2 MB each; the first at 2.02 s, before first paint at 2.22 s, the repeat at 3.68 s), and two Ethereum libraries (viem and ethers) plus the MetaMask SDK (0.55 MB) load during start-up. The timed loads also show /api/v3/stable-token-price and /spot-tokens-price each requested twice 10 ms apart, and data-api.polymarket.com/value twice.
- Measured
- two 849 KB reCAPTCHA loads, two 1.2 MB wallet-list loads, and overlapping Ethereum libraries
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Load reCAPTCHA and the WalletConnect registry once, only when needed; keep one Ethereum library.
- Where
- Captcha and wallet provider initialisation.
- Verify
- DevTools → Network: one recaptcha__ and one explorer-api request per load, none before the first price paint.
- Evidence
- 1 October capture: price and book at 4.51 s, chart at 6.74 s, although library.4350c821a355451bc08e.js finished downloading at 2.75 s. The timed loads show the candle request (/api/v3/klines) going out late: 6.24–6.43 s uncached with the chart at 6.58 s, and 5.86–6.06 s warm with the chart at 6.25 s; in the 3 October recording 3.72–3.92 s with the chart at 4.12 s. Each time the chart follows the candles by about 0.2 s. In the 1 October capture 22 chart script requests also fail at 3.3–3.7 s and are requested again. All 5 warm campaign loads have the chart last (median 4.2 s). In 4 of the 10 ranked loads the chart canvas appears, disappears again for 0.6 to 0.75 s (for example from 2.65 to 3.24 s in one warm load) and comes back before the candles pass, which suggests the chart widget is built, torn down and built again.
- Gap observed
- 2.23 s between price/book and the chart
- Holds back
- Chart (median 4.2 s in the timed loads).
- Fix
- Send the /api/v3/klines request with the other market data at start-up; the chart appears about 0.2 s after it answers. Check whether the chart widget is mounted twice.
- Where
- Chart datafeed and mount conditions.
- Verify
- DevTools → Network, filter "klines": the request starts before 2 s.
- Evidence
- All 10 ranked loads have the chart last, and in every one the chart panel still has a running animation between the chart having its data and trade-ready (0.64 to 0.75 s on the warm loads). Recorded warm load: chart at 4.12 s, trade-ready at 4.80 s, all 7 samples in between; in one a font is also still loading.
- Gap observed
- 0.64 to 0.75 s between the chart and trade-ready in the five warm loads
- Holds back
- Trade-ready itself: the page counts as ready only when nothing in a panel is still animating or loading.
- Fix
- Record a reload with DevTools → Animations and find what still animates in the chart after the candles are drawn, for example a toolbar loading spinner or a fade-in; end it when the data is in.
- Where
- The chart panel and its loading states.
- Verify
- DevTools → Animations during a reload: nothing in the chart container animates after the candles appear.
- Likely saving
- Up to ~0.7 s
- Evidence
- 1 October capture: HarmonyOS_Sans_Regular.ttf, Medium and Bold from omni.apex.exchange are 147 KB each (440 KB together), sent without compression, at 1.27 and 1.93 s. The page already uses one WOFF2 font (35 KB).
- Measured
- three TTF fonts of 147 KB each
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Ship the three HarmonyOS Sans files as WOFF2 subsets.
- Where
- The font files and @font-face rules in the app build.
- Verify
- DevTools → Network, filter "font": all fonts are woff2, each well under 100 KB.
- Likely saving
- Small on a reload; about 0.3 MB less on a first visit
- Evidence
- 1 October capture: four images from static-pro.apex.exchange of 241, 218, 195 and 151 KB (0.8 MB together) are requested at 4.7 and 5.0 s and arrive by 5.05 s, between the price (4.51 s) and the chart (6.74 s). Their paths are masked, so the capture does not say what they show.
- Measured
- 0.8 MB of images between the price and the chart
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Load the images after trade-ready, or where they are displayed, and serve them at display size in a modern format.
- Where
- The image assets on static-pro.apex.exchange requested at start-up.
- Verify
- DevTools → Network, filter "static-pro": no image over 100 KB before the chart is drawn.
- Likely saving
- Small
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 (this page's stored loads were scored again afterwards with one change: the order form is checked on Market, with one input field, as it was during 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.