Variational: 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 2.6 s (median of 5 loads), 2 of 27 perpetual trading screens, possible rank 1 to 5; after an uncached reload 3.0 s. The largest bottleneck: Four feed sockets, each needing 0.9 to 1.3 s to connect. First change to try: Open the feed sockets from the first script, check whether the four can share one connection, and measure where the 0.9 to 1.3 s of each handshake goes before moving the server.

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 2.6 s, range 2.4 s–3.5 s; loads: 2.4 s, 3.5 s, 2.8 s, 2.6 s, 2.6 s
Uncached reloadmedian 3.0 s, range 2.8 s–3.2 s; loads: 2.8 s, 2.8 s, 3.0 s, 3.2 s, 3.0 s
Standing2 of 27 by median; possible rank 1 to 5. 0.1 s behind the fastest, Thalex (2.4 s): 1.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 ready5 of 5 warm, 5 of 5 uncached
Notesits screen has no order book, so it has fewer panels to load

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 s
Price0.7 s
Chart2.0 s +1.3 s
Quote2.6 s +0.6 s
Trade-ready2.6 s +0.0 s settling

Stack

SvelteKit app from omni.variational.io; TradingView chart library (library.js) from assets.variational.io; Web3Modal and WalletConnect; Google Tag Manager; Cloudflare Insights; own monitoring at mon.variational.io.

Delivery

omni.variational.io through Cloudflare over HTTP/3; the HTML comes from the edge cache (first byte after 18 ms); app and chart files cached a year, immutable; live feed from omni-ws-server.prod.ap-northeast-1.variational.io, a host named for the AWS Tokyo region.

HostRoleCDN edge seenConnectResponse
omni.variational.iopageCloudflare Amsterdam13 ms19 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.10 sHTML arrives from Cloudflare's edge cache.
0.11–0.66 sApp chunks: 76 scripts from omni.variational.io (73 of them before 0.5 s), the largest 438 KB (1.9 MB unpacked).
0.50 sPage shows the market; first paint at 0.52 s.
0.83–1.29 sNine requests to the Web3Modal API.
1.00–2.56 sA 496 KB (1.7 MB unpacked) response from omni.variational.io, not served from the CDN cache.
1.03 sThe first two feed sockets are created; their handshakes finish at 1.97 s and 2.27 s.
1.59–1.80 sTradingView chart library (667 KB, 2.8 MB unpacked), the first of 32 chart files from assets.variational.io.
2.63 sChart drawn.
2.91 sThe fourth feed socket finishes its handshake; first message at 3.17 s.
3.21 sLast panel has data: end of this capture. Six long main-thread tasks total 1.0 s.

Request waterfalldiagnostic capture 1 October, uncached

67 of the 246 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 fileImagewaiting for the serverdownloading

0 s0 s1 s1 s2 s2 s3 s3 s1omni.variational.io9 kB344 ms2omni.variational.io/2.Bhy…89 kB365 ms3omni.variational.io/BkACD…438 kB388 ms4omni.variational.io/DA_x7…815 B367 ms5omni.variational.io/DLKhd…7 kB368 ms6omni.variational.io/bMN1C…2 kB368 ms7omni.variational.io/MM4EH…7 kB370 ms8omni.variational.io/CI85U…6 kB369 ms9omni.variational.io/CP-yA…724 B369 ms10omni.variational.io/Cduec…5 kB369 ms11omni.variational.io/JhqsG…1 kB371 ms12omni.variational.io/CM2IT…4 kB371 ms13omni.variational.io/BuDTV…2 kB372 ms14omni.variational.io/BCOjo…517 B372 ms15omni.variational.io/CVAH3…470 B372 ms16omni.variational.io/CuZoS…537 B373 ms17omni.variational.io/BqU3c…1 kB373 ms18omni.variational.io/Bx9Oa…735 B373 ms19omni.variational.io/CcUK6…525 B374 ms20omni.variational.io/C-JxD…787 B374 ms21omni.variational.io/DKcQc…561 B374 ms22omni.variational.io/10.NQ…613 B375 ms23omni.variational.io/Dba6P…86 kB381 ms24omni.variational.io/DdY_r…86 kB383 ms25omni.variational.io/qRYGk…4 kB375 ms26omni.variational.io/CqRAw…5 kB376 ms27omni.variational.io/hyn3Q…1 kB376 ms28omni.variational.io/CwPFE…2 kB377 ms29omni.variational.io/CLJr1…4 kB378 ms30omni.variational.io/DUo4v…891 B377 ms31omni.variational.io/CODDS…889 B378 ms32omni.variational.io/DU6Xs…981 B378 ms33omni.variational.io/DKDPa…6 kB379 ms34omni.variational.io/Bnn6v…940 B378 ms35omni.variational.io674 B391 ms36omni.variational.io213 B388 ms37omni.variational.io496 kB1561 ms38assets.variational.io/Int…350 kB65 ms39omni.variational.io2 kB386 ms40omni.variational.io411 B408 ms41omni.variational.io143 kB59 ms42arb1.arbitrum.io147 B455 ms43omni.variational.io445 B442 ms44mon.variational.io623 B399 ms45omni.variational.io2 kB1071 ms46omni.variational.io450 B378 ms47omni.variational.io273 B375 ms48omni.variational.io329 B375 ms49omni.variational.io80 kB165 ms50omni.variational.io450 B493 ms51omni.variational.io50 kB1246 ms52assets.variational.io/lib…667 kB210 ms53mon.variational.io623 B422 ms54mon.variational.io623 B422 ms55mon.variational.io621 B418 ms56mon.variational.io625 B422 ms57mon.variational.io623 B604 ms58omni.variational.io567 B565 ms59omni.variational.io565 B564 ms60mon.variational.io624 B581 ms61mon.variational.io623 B576 ms62mon.variational.io624 B575 ms63mon.variational.io622 B570 ms64mon.variational.io624 B545 ms65omni.variational.io150 kB44 ms66mon.variational.io626 B409 ms67verify.walletconnect.com785 B373 msprice 3.2 schart 2.6 s
Download this figure as an image:

Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached

VR-1Four feed sockets, each needing 0.9 to 1.3 s to connect.grade B
Evidence
1 October capture: sockets to omni-ws-server.prod.ap-northeast-1.variational.io are created at 1.03, 1.03, 1.34 and 1.71 s; their handshakes finish at 1.97, 2.27, 2.63 and 2.91 s. The last one delivers its first message at 3.17 s, 0.04 s before the last panel has data. In the campaign the quote panel is the last panel in all 5 warm loads (median 2.6 s, 0.6 s after the chart); in one of them the chart is still animating for 0.3 s after the quote. The host name suggests the AWS Tokyo region; the captures do not measure where the server is.
Gap observed
0.9 to 1.3 s for each of four socket connections
Holds back
Probably the quote (median 2.6 s in the timed loads): the timing points to it, but the captures do not show that this panel waits for it.
Fix
Open the feed sockets from the first script, check whether the four can share one connection, and measure where the 0.9 to 1.3 s of each handshake goes before moving the server.
Where
Feed client start-up and the omni-ws-server deployment.
Verify
DevTools → Network → WS: sockets created before 0.6 s; handshake under 0.3 s.
VR-2A 1.7 MB animation player (WebAssembly) takes 1.6 s and bypasses the CDN cache.grade B
Evidence
1 October capture: requested at 1.00 s, first byte at 1.29 s, finished at 2.56 s (496 KB transferred, 1.7 MB unpacked). The timed loads and a logged-out follow-up name a file of exactly that size, /_app/immutable/assets/dotlottie-player.B5Wh5MC6.wasm, the Lottie animation player; the match is by size, host and order. It carries a one-year immutable cache header, but Cloudflare reports it as DYNAMIC, so the edge passes each request to the origin; a returning browser has it cached. A second response, /api/settlement_pools/leverage (50 KB, 747 KB unpacked), runs from 1.53 to 2.77 s, also DYNAMIC.
Gap observed
1.56 s from requesting the 1.7 MB animation player to its completion
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Load the Lottie player only when an animation is about to be shown, and let Cloudflare store the .wasm file, which is already marked immutable.
Where
The import of dotlottie-player in the app shell; the Cloudflare cache rule for /_app/immutable/assets/*.wasm.
Verify
DevTools → Network, filter "wasm": no request before the quote panel shows; cf-cache-status HIT.
VR-3The chart library is requested 1.1 s after the page is up.grade C
Evidence
The page shows the market at 0.50 s; library.js is requested at 1.59 s and the chart is drawn at 2.63 s. In the campaign the chart follows the price by 1.3 s on a warm reload (0.7 s to 2.0 s).
Gap observed
1.09 s between the market header appearing and the chart-library request
Holds back
Chart (median 2.0 s in the timed loads).
Fix
Request the chart library with the app chunks, for example with a preload in the HTML.
Where
Chart loader and the document head.
Verify
DevTools → Network: library.js requested before 0.6 s.
VR-4The wallet stack loads before the market data.grade C
Evidence
Nine Web3Modal API requests go out in two batches at 0.83 s and 1.11 s, two WalletConnect RPC calls at 1.06 s and 1.25 s, and a WalletConnect relay socket at 1.58 s. All of them start before any feed socket has finished its handshake (1.97 s), so they share the network and the main thread with the market data.
Measured
nine Web3Modal API requests, two WalletConnect RPC calls and a relay socket
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Start the wallet libraries after the first price and quote are on screen, or when the user opens the wallet dialog.
Where
Web3Modal and WalletConnect start-up.
Verify
DevTools → Network: no api.web3modal.com or walletconnect.com request before the first feed message.
VR-5Eighteen monitoring requests go out before the last panel has data.grade C
Evidence
1 October capture: 18 requests to mon.variational.io; one fails at once, 17 succeed, all starting between 1.30 s and 2.71 s and each waiting 0.3 to 0.5 s for its first byte, in the same window as the feed handshakes and the chart library. The follow-up names them: /_mon/api/v2/rum (page monitoring) and /_mon/api/v2/replay (session replay); a timed warm load sends 15 monitoring requests and one replay upload (2.74–3.15 s) before trade-ready.
Gap observed
0.3 to 0.5 s from each monitoring request to its first byte
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Queue monitoring events and send them in one batch after the page is trade-ready; start session replay after trade-ready.
Where
The mon.variational.io client (rum and replay).
Verify
DevTools → Network: no mon.variational.io request before the quote panel shows.
VR-6The same API addresses are called up to six times at start-up.grade C
Evidence
1 October timed loads, uncached and warm: /api/settlement_pools/leverage is requested six times (1.32 to 2.78 s; one answer is 747 KB unpacked, the others 1 to 5 KB), /api/metadata/supported_assets three times (one answer 475 KB) and /api/banner twice. The request parameters are not recorded, so the calls may ask for different things.
Measured
one endpoint called six times and another three times per load
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Check which of the six leverage calls and three supported_assets calls differ, share the identical ones, and ask the large ones only for the market on screen.
Where
The callers of /api/settlement_pools/leverage and /api/metadata/supported_assets.
Verify
DevTools → Network, filter "settlement_pools/leverage": fewer requests, none over 100 KB unpacked before the quote shows.
Likely saving
Unknown
VR-7A 350 KB font file, next to a second copy of Inter from Google.grade B
Evidence
1 October capture: assets.variational.io/Inter-Variable.woff2 is 350 KB (1.03 s), and a 48 KB Inter file from fonts.gstatic.com loads at 0.95 s.
Measured
one 350 KB font file and a second Inter file
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Ship one subset of Inter with the ranges and weights in use.
Where
The @font-face rules of the app and of the chart.
Verify
DevTools → Network, filter "woff2": one Inter file, under 100 KB.
Likely saving
Small on a reload; about 0.3 MB less on a first visit
VR-8Three blocking start-up tasks (0.41 s together) run before the feed sockets are opened.grade C
Evidence
Logged-out follow-up recording: tasks of 110 ms (at 0.16 s), 153 ms (at 0.33 s) and 150 ms (at 0.63 s) on the main thread, before the first feed sockets are created at 0.79 s. The CPU profile places them in the app's own chunks: 33 ms in CVfNU1ZZ.js, 73 ms in the initialiser of DKkrxZ2C.js, and 28 and 22 ms in CSJ1lJwL.js and CSaMAIZ7.js. That recording stopped at a captcha, so it has no panel times.
Gap observed
0.41 s of blocking tasks before the first socket is created, in the follow-up recording
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Open the feed sockets before this initialisation runs, and look at what the three chunks set up synchronously at start-up.
Where
The app shell's start-up order: socket creation against the initialisers of DKkrxZ2C.js, CVfNU1ZZ.js, CSJ1lJwL.js and CSaMAIZ7.js.
Verify
DevTools → Network → WS: sockets created before 0.3 s; Performance: no task over 100 ms before them.
Likely saving
Up to ~0.4 s on the feed start

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.