Method
- One blank page per widget. A heading, a sentence and the vendor's official embed code, placed where the vendor's install guide says. Nothing else.
- The vendor's own chat. Each vendor runs its chat on its own website, so we used the widget ID from that site: Crisp's from crisp.chat, Intercom's from intercom.com, and so on. No other customer's ID. Pidge was tested against our production servers with the ID from pidge.chat.
- Served from the vendor's own address. Several widgets only appear on domains their account allows. Our script answers a request for a made-up page on the vendor's own site, such as
https://crisp.chat/widget-weight-test, with the blank test page, inside the test browser. That request never reaches the vendor. Everything after it, the widget's scripts, settings and connections, comes from the vendor's real servers. In a trial run from a plain localhost page, six of the ten either never appeared or loaded only part of their code. - Cold start every time. A new browser for every run: empty cache, no cookies, no stored chat history.
- Two profiles. Desktop: 1366×768, no throttling, five runs. Phone: 412×823, CPU slowed 4×, network limited to 150 ms round trip, 1.6 Mbps down and 750 kbps up (Lighthouse's mobile settings), three runs. The tables show the median run.
- "On load" ends when no request has been in flight for 3 seconds (5 on the phone profile). Connections that stay open on purpose, like long polling, stop counting after 10 seconds.
- "After opening" calls the vendor's documented JavaScript command to open the chat (for Pidge,
$pidge.push(["do", "chat:open"])), then waits for the same quiet period. The figure includes everything from the load as well. - Bytes are what Chrome received over HTTP, headers included, as DevTools shows under "transferred". Unpacked is the size after the browser decompressed it. Every request counts, including the few that go to a third party such as a font or emoji CDN, because the blank page makes none of its own. WebSocket traffic isn't counted.
- Main-thread time comes from a Chrome performance trace: the time the page's main thread spent on tasks, from navigation until "on load" ended, minus what the blank page alone needed (6 ms on desktop, 35 ms on the phone profile). Widget iframes ran in the same renderer, so their work is counted too.
- Render-blocking is Chrome's own flag on each resource (
renderBlockingStatus). - We never sent a message. Opening a chat is all we did. One widget, Zendesk, registers a visitor and creates an empty conversation as soon as it opens; we refused that one request so the test leaves nothing in Zendesk's inbox. The Zendesk notes say what that changes.
Results
Desktop, no throttling, median of five runs. Sorted by bytes on load.
| Widget | On load | After opening the chat | Main-thread time on load | Render-blocking |
|---|---|---|---|---|
| Pidge | 8.4 KB in 4 requests 19 KB unpacked | 32.3 KB in 5 requests 95 KB unpacked | 26 ms | No |
| Crisp | 129.1 KB in 12 requests 477 KB unpacked | 283.6 KB in 24 requests 1,071 KB unpacked | 59 ms | No |
| Smartsupp | 134.8 KB in 7 requests 412 KB unpacked | 243.6 KB in 11 requests 984 KB unpacked | 66 ms | No |
| tawk.to | 259.8 KB in 19 requests 751 KB unpacked | 380.9 KB in 33 requests 1,285 KB unpacked | 111 ms | No |
| LiveChat | 358.7 KB in 22 requests 1,142 KB unpacked | 480.6 KB in 28 requests 1,538 KB unpacked | 201 ms | No |
| Zendesk | 419.5 KB in 15 requests 1,302 KB unpacked | 621.8 KB in 33 requests 1,952 KB unpacked | 81 ms | No, but it holds up the HTML parser |
| Tidio | 521.2 KB in 9 requests 1,181 KB unpacked | 521.2 KB in 10 requests 1,208 KB unpacked | 47 ms | No |
| Intercom | 945.6 KB in 13 requests 4,239 KB unpacked | 1,339.1 KB in 26 requests 5,722 KB unpacked | 184 ms | No |
| Chatwoot Cloud | 1,220.5 KB in 28 requests 3,404 KB unpacked | 1,220.7 KB in 29 requests 3,404 KB unpacked | 110 ms | No |
| HubSpot | 1,918.9 KB in 56 requests 6,627 KB unpacked | 2,109.6 KB in 62 requests 7,134 KB unpacked | 487 ms | No |
Phone profile, 4× slower CPU and a slow 4G connection, median of three runs. Some widgets load less up front on a phone and fetch the rest when the chat opens. "Long tasks" is the time spent past 50 ms in any single task, when a tap or a scroll would have to wait.
| Widget | On load | Requests on load | After opening the chat | Main-thread time on load | Of which long tasks |
|---|---|---|---|---|---|
| Pidge | 6.0 KB | 3 | 29.9 KB | 62 ms | 0 ms |
| Crisp | 112.4 KB | 6 | 276.1 KB | 256 ms | 0 ms |
| Smartsupp | 134.8 KB | 7 | 243.5 KB | 354 ms | 109 ms |
| tawk.to | 222.5 KB | 12 | 344.4 KB | 252 ms | 22 ms |
| LiveChat | 358.7 KB | 22 | 480.6 KB | 463 ms | 122 ms |
| Zendesk | 419.4 KB | 15 | 621.8 KB | 632 ms | 275 ms |
| Tidio | 521.2 KB | 9 | 521.2 KB | 275 ms | 15 ms |
| Intercom | 945.8 KB | 13 | 1,339.5 KB | 1,666 ms | 1,166 ms |
| Chatwoot Cloud | 1,295.4 KB | 30 | 1,295.6 KB | 502 ms | 22 ms |
| HubSpot | 1,892.3 KB | 53 | 2,089.2 KB | 1,199 ms | 264 ms |
Runs were close to each other: byte counts moved by less than 1% between runs, and main-thread times by up to about a third on desktop. Each widget's numbers depend on how its account is set up. A vendor's own chat often has its newest features switched on, like an AI agent or a help centre, so a plainer setup on your site may well load less. The notes say where we saw that.
Notes per widget
Pidge
On load: the 5.1 KB loader, the website's settings, a presence check, and on desktop a 2.5 KB script for the invite card above the bubble ("Questions? Chat with us"), which Pidge doesn't show on phones. Opening the chat downloads the chat window (23.9 KB) and opens a WebSocket. Pidge has no AI agent, help centre or video calls, so there's less to download. We built it; check our numbers with the steps below.
Crisp
Crisp's own chat, which opens with suggested questions. On load, its main script (82 KB), stylesheet, settings and English strings, plus on desktop its fonts and operator avatars. Opening adds the chat's own script and stylesheet, sounds and avatars, about 155 KB. Our homepage counts Crisp's scripts and styles before opening, measured on another site in October, at 105.5 KB. The same files here come to about 105 KB; the table also counts fonts, images, settings and HTTP headers.
Smartsupp
Smartsupp's homepage didn't load its chat in our browser before cookies were accepted, so we took the key from the tag manager setup its site loads. Its chat greets visitors with the Mira AI assistant. Opening fetches the chat window's scripts and an emoji list (57 KB) from cdn.jsdelivr.net.
tawk.to
tawk.to's own property, which answers with its Apollo AI assistant. tawk.to's homepage didn't load the chat when we looked; its pricing and contact pages did, and we took the ID from there. Opening adds the full chat window, an image and an emoji library (39 KB) from cdn.jsdelivr.net.
LiveChat
LiveChat's own license, which opens with an AI assistant asking for a name and email. Its 22 requests on load are mostly script chunks. On desktop it had the most main-thread time after HubSpot.
Zendesk
The messenger key Zendesk's own site uses (its site loads it through a small script of its
own). Zendesk's embed code is a plain script tag with no async. Placed at the
end of the page as Zendesk says, it doesn't hold up the first paint, but the browser stops
reading the page until the script has downloaded and run. Opening the messenger registers
the visitor and starts a conversation straight away. In every run we refused that request;
in one test run where we let it through, the total after opening was 646 KB instead of
622 KB, so the real figure is about 24 KB higher than the table shows.
Tidio
Tidio's own project hides the chat bubble until its site asks for it, so we called Tidio's
display(true) command just before opening. Tidio downloads everything on load,
including two fonts (99 KB) and two notification sounds, so opening the chat costs almost
nothing more. With the bubble visible from the start, as on most sites, the on-load figure
may be a little higher. We didn't measure that.
Intercom
Intercom's own workspace, which answers with Fin, its AI agent. It loads about 913 KB of script before anyone opens the chat. Opening adds API calls and a 251 KB image of Fin "thinking". On the phone profile it kept the main thread busy for 1.7 seconds, 1.2 of them in long tasks, the most of any widget here.
Chatwoot Cloud
Chatwoot's own website inbox on app.chatwoot.com. Its widget shows popular help-centre articles, and that list arrives as a 544 KB file sent uncompressed, plus a 102 KB image. Those two make up more than half the total, and a Chatwoot inbox without a linked help centre wouldn't load them. Everything downloads on load, so opening adds almost nothing. Self-hosted Chatwoot serves the same widget from your own server; we tested the cloud.
HubSpot
HubSpot's own account. HubSpot chat is installed with the HubSpot tracking code, which also loads HubSpot analytics, an ads pixel and, on this account, a cookie banner (149 KB), which appeared on our test page. The chat turned on spam protection and loaded Google reCAPTCHA twice, about 350 KB each time. An account without the cookie banner or spam protection would load less. We didn't test one.
How to re-run it
- Install Node.js 24, then in an empty folder run
npm install playwright@1.64.1andnpx playwright install chromium. - Make one HTML file per widget: a heading, a sentence and the vendor's embed code from its install guide. Use the widget ID from the vendor's own site, which you'll find in its page source or in the browser's network panel while the site loads.
- For each run, launch a new browser with
--disable-features=IsolateOrigins,site-per-processso the widget's iframes share the page's renderer, and give it an ordinary Chrome user agent. Some vendors' servers refuse the default headless one. - Answer a request for a made-up address on the vendor's site with your test page (
page.route(url, route => route.fulfill({ body: html }))), and go to that address. - Through the DevTools protocol, record
Network.loadingFinished(encodedDataLengthis bytes transferred) andNetwork.dataReceived(uncompressed bytes), and record a performance trace with thedevtools.timelinecategory. For the phone profile, addNetwork.emulateNetworkConditionsandEmulation.setCPUThrottlingRatewith the settings above. - Wait for the network to go quiet, call the vendor's open command, wait again, then add up the top-level tasks on the main thread in the trace.
- Do the same with a page that has no widget, and subtract its main-thread time.
Our script and the ten test pages do exactly this. Ask in the chat on this page and we'll send them. If you get different numbers, tell us and we'll check.
Re-run date
Tested on 2 November 2026 with Chromium 156.0.8078.4 (Playwright 1.64.1) on macOS, from Norway. Vendors change their widgets often, so these numbers will drift. When we run the test again, the new date and numbers go here.
More: How Pidge stays small · Crisp alternatives · Why ad blockers hide chat widgets · Install guides