0 cookies0 requests to our api before a clickunlimited teammatesreply from slack, discord or our phone app

/blog / tests

we loaded 7 chat widgets and weighed them

· by kuba · 12 min read

A seesaw scale with a tall dark block weighing down one side and the startled Spookat cat on the light side, with kb written below.

We put seven chat widgets on the same page and measured everything each one downloads before a visitor touches anything. Spookat made 1 request and transferred 2.0 kb. The others made up to 23 requests and transferred up to just over 1 mb, depending on the widget and on whether it was allowed to boot. Here are the numbers, where they came from, and what they mean for your page.

the short version

Every number below is the median of 7 runs, measured on 2026-09-27 in Chrome 153 with an empty cache and a fresh profile each time. Spookat’s row was measured again later that morning, after three changes: the widget got fixes that made it half a kilobyte heavier (a chat that opens faster, never covers a shop’s bottom bar and passes axe clean), then a launcher that carries only the site’s own style instead of all eight, and leaner response headers. Together they took the launcher from 2.1 kb to 2.0. “Transferred” is what came over the network, compressed. “Uncompressed” is what the browser had to unpack, parse and in the case of JavaScript, run.

On our test page, with the page itself not counted:

widget requests transferred uncompressed booted on our page
Spookat 1 2.0 kb 3.4 kb yes
Tawk.to 18 (+1 websocket) 255.8 kb 730.1 kb yes
Tidio 10 (+1 websocket) 510.9 kb 1,166.4 kb yes
Intercom 6 285.6 kb 1,192.9 kb no, domain not allowed
Crisp 6 (+1 websocket) 102.6 kb 406.9 kb no, “Invalid website”
LiveChat 4 38.1 kb 114.3 kb no, domain not allowed
Chatwoot 2 6.2 kb 21.5 kb no, iframe blocked

On the vendor’s own site, counting only the widget’s own requests:

widget requests transferred uncompressed
Intercom (intercom.com) 19 (+1 websocket) 1,006.3 kb 4,473.7 kb
LiveChat (livechat.com) 23 (+1 websocket) 358.0 kb 1,138.6 kb
Crisp (crisp.chat) 12 (+1 websocket) 121.1 kb 444.4 kb
Chatwoot (chatwoot.com help center) 27 (+1 websocket) 864.1 kb 3,079.8 kb

The two tables measure different things, and the next section explains why there are two at all.

why there are two tables

We used each vendor’s install snippet from its own docs and a widget id the vendor itself runs on its own homepage. We didn’t create accounts anywhere. How we test has the full setup.

Four of the six rivals check the domain they’re running on, and the ids we could use belong to the vendors’ own sites. So on our test page, Crisp shows a red “Invalid website” label, Intercom’s server answers 403, LiveChat logs that the domain is not allowed and disables itself, and Chatwoot’s chat window is blocked by its own security policy. What those four load before they give up is real, and it’s in the first table. It’s a floor, though, not the full weight.

So for those four we also loaded the vendor’s own homepage, as any visitor would, and counted only the requests that go to the widget’s own addresses. Every counted URL is listed in the raw data. That’s the second table. It comes with the vendor’s own settings: Intercom’s site pushes a message to new visitors, LiveChat’s shows a greeting card, and a plain install on your site may load a bit less or a bit more.

Crisp needs a footnote. On crisp.chat its widget stopped booting for our machine after a few visits in a row. With 90 seconds between visits it booted every time, and the 7 runs in the table came out identical.

spookat: 1 request, 2.0 kb

The launcher is one script from cdn.spookat.com: 3,478 bytes of JavaScript, 1,772 bytes over the wire with brotli, 2.0 kb including the response headers. It draws a button in a shadow root, keeps it off the page’s own bottom bar with a layout check at most every 300 ms, and waits.

That’s the whole list. No second request, no websocket, no font, no image, no call to api.spookat.com. The chat panel (16.8 kb gzipped) and the style’s CSS (about 2 kb) load only when someone presses the button, and the connection to our server only on the click. Nothing is written to cookies, localStorage, sessionStorage or IndexedDB before the click, or after it until the visitor sends a message. Then it’s one localStorage key with a random chat token, and still no cookie.

One honest detail: cdn.spookat.com is ours. When it serves a launcher, it tells our API directly that a launcher with that site key loaded, so we can tell the site’s owner the snippet is live. The visitor’s browser makes no request to our API for that, and no IP address is stored.

tawk.to: 18 requests, 255.8 kb

Tawk.to boots anywhere, so this is its full weight on a real page. The first request is a 1 kb loader. After it come twk-main.js (121 kb transferred, 325 kb uncompressed), two language files, a widget-settings call and a session start at va.tawk.to, then eight more script chunks, four stylesheets and an SVG for the “we are here” bubble. It also opens a websocket to a tawk.to server.

Nine of the 18 requests are JavaScript: 213 kb transferred, 580 kb for the browser to parse. Before any click, Tawk.to sets three cookies on the page’s own domain (one of them kept 180 days) and writes two localStorage keys and one sessionStorage key.

tidio: 10 requests, 510.9 kb

Tidio’s own widget is set to stay hidden on tidio.com, so our page called Tidio’s documented tidioChatApi.show() before any click to get a normal launcher. Everything else is a stock install.

Tidio is the heaviest widget that boots on our page. Two big scripts (228 kb and 154 kb transferred), two web fonts (27 kb and 73 kb), and two sound files (7.5 kb and 28.6 kb), the notification sounds, downloaded before anything could make a sound. One websocket to socket.tidio.co. No cookies; one localStorage key with the widget’s state.

intercom: 285.6 kb refused, 1 mb on intercom.com

On our page Intercom loads a 2.5 kb loader, then frame-modern.js (192 kb transferred, 858 kb uncompressed) and vendor-modern.js (95 kb, 358 kb), asks for its launcher settings and gets a 403 on its ping: the domain is not allowed. That’s 285.6 kb before it stops, and 1.19 mb of JavaScript for the browser to parse.

On intercom.com it boots fully: five more large script bundles, images, a websocket, and at about 14 seconds the message Intercom’s site pushes to new visitors, with its own script and a notification sound. In total 19 requests, 1,006.3 kb transferred and 4.47 mb uncompressed, 4.38 mb of it JavaScript. Some of that is the pushed message. Most of it isn’t: by 3 seconds, before the message, the widget had already pulled more than 950 kb.

Before any click on intercom.com, the widget set three cookies on the site’s domain: an id and a device id kept 270 days, and a session kept 7 days.

crisp: 102.6 kb refused, 121.1 kb on crisp.chat

Crisp is the lightest of the rivals that we saw boot. On our page it loads its loader, a 76 kb script, a 14 kb stylesheet (130 kb uncompressed), a language file and a font, opens a websocket, then shows “Invalid website”. On crisp.chat the full boot adds the site’s settings, a second font weight and four small avatar images: 12 requests, 121.1 kb.

Before any click on crisp.chat it set one cookie on the site’s domain, kept 182 days, and four localStorage keys.

livechat: 38.1 kb refused, 358 kb on livechat.com

LiveChat’s loader is 36 kb. On our page it fetches its region and configuration, sees the domain isn’t allowed, and stops at 38.1 kb. On livechat.com it goes on: localization, an iframe from secure.livechatinc.com, 15 more widget scripts and a customer token: 23 requests, 358 kb transferred, 1.14 mb uncompressed. One websocket.

Its cookies live on accounts.livechatinc.com, a third-party domain from the site’s point of view: two partitioned cookies kept 400 days, set before any click. The widget’s iframe also writes four localStorage keys and one sessionStorage key.

chatwoot: 6.2 kb refused, 864 kb on chatwoot.com

On our page Chatwoot loads its 6 kb SDK and tries to open its chat iframe, which its own frame-ancestors policy blocks. That’s the whole 6.2 kb.

Chatwoot’s homepage didn’t load its widget in any of our three tries, so we used Chatwoot’s help center on the same domain, which loaded it every time. There the widget pulls its SDK, its iframe document, 15 scripts and 2 stylesheets from a CloudFront CDN, five calls to its widget API, an avatar and one websocket: 27 requests, 864.1 kb transferred, 3.08 mb uncompressed.

Two items make up most of that. The widget’s stylesheet is 153 kb transferred and 1.57 mb uncompressed. And 491 kb is a single JSON file of help-center articles, which the widget fetches to show six of them on its home screen; with the widget blocked, the page never asks for it. Five of the scripts had already been loaded by the help center itself, which is built on Chatwoot too, so they came from cache and count as 0 transferred. Treat this row as a floor.

Before any click Chatwoot set a cookie kept 365 days on the site’s domain, a session cookie on app.chatwoot.com, and wrote six localStorage keys.

what the bytes are made of

Split by type, for every widget in the state where it fully booted (our test page for Spookat, Tawk.to and Tidio, the vendor’s page for the rest), transferred / uncompressed:

widget JavaScript CSS fonts sounds and images API calls
Spookat 2.0 / 3.4 kb none none none none
Tawk.to 213 / 580 kb 24 / 101 kb none 7 / 22 kb 11.5 / 28 kb
Tidio 377 / 1,034 kb none 99 / 98 kb 35 / 35 kb none
Crisp 83 / 272 kb 14 / 130 kb 11 / 10 kb 5 / 4 kb 8 / 29 kb
Intercom 938 / 4,379 kb none none 54 / 53 kb 14 / 42 kb
LiveChat 355 / 1,136 kb none none none 3 / 3 kb
Chatwoot 203 / 1,042 kb 150 / 1,533 kb none 13 / 12 kb 484 / 480 kb

JavaScript is the bulk everywhere, which matters because it’s the only kind of byte that costs CPU time on top of download time. Two rows break the pattern: Tidio spends about a quarter of its weight on fonts and notification sounds, and Chatwoot’s biggest single items are a stylesheet and a list of help articles.

when the bytes arrive

Weight is only half of it. The other half is when it lands, because a widget downloading while your page is still loading competes with your own images and scripts for the same connection.

On our test page Tawk.to made its first request 98 ms after navigation started and its last at 925 ms. Tidio started at 28 ms and finished at 787 ms. Both load in the same second as the page itself. On the vendors’ own sites the timing is up to the site: intercom.com starts its widget at 1.4 s (Intercom’s snippet waits for the page’s load event), livechat.com at 0.26 s, chatwoot.com’s help center at 1.2 s, and crisp.chat holds its widget back until about 8.7 s. Intercom’s last request came at 14.3 s, when the pushed message arrived.

Spookat’s single request came at 107 ms (the median of the 7 runs) and nothing followed it.

how 2 kb is enough

A launcher only has to do two things before a click: show a button and know what to load when it’s pressed. Ours does exactly that.

  • The button and its CSS are inside the one script, drawn in a shadow root so the page’s styles can’t reach it and its styles can’t leak out.
  • The site’s look (style, colour, side, label) is filled into that script by our CDN for each site, so there’s no settings request. So is the CSS of the site’s own style: the script carries the default style plus that one, not all eight.
  • The script knows the file names of the chat panel and of your style’s stylesheet. When someone presses the button, it starts both. That’s the first moment anything else is downloaded.
  • Our build measures the launcher the way visitors get it, filled in and gzipped, and fails if it goes over 4 kb.

what the numbers mean for your page

Uncompressed JavaScript is the expensive part. Transferred bytes are what your visitor’s data plan pays. Uncompressed JavaScript is what their phone’s CPU pays: every byte gets parsed and compiled, and startup code runs on the same main thread that handles taps and scrolling. 730 kb of JavaScript from Tawk.to or 4.4 mb from Intercom on its own site is work a mid-range phone does on every page view, including for every visitor who never opens the chat.

Every new domain costs a connection. A request to a domain the page hasn’t talked to yet needs a DNS lookup, a TCP connection and a TLS handshake before the first byte. On a phone on a weak signal, each of those is a round trip. Intercom’s and LiveChat’s full boots each talk to four hosts.

A websocket before the click is a connection kept open for nobody. Every rival opened one before any click, once it was allowed to boot. Spookat opens none until the click. It’s small in bytes, but it holds a connection and wakes the page up for every frame.

Storage before the click has consequences. Cookies and local storage written before anyone interacts with the widget are what your consent banner has to think about. We’ll go through that properly in the cookie test.

what the next tests cover

Whether any of this slows your page down in a way users feel is a different question. Bytes don’t equal milliseconds, especially when a widget loads after the page is already interactive. That’s the Lighthouse test. How fast each chat opens once someone does click is the click-to-open test. Both are coming in this series.

check it yourself

The raw data is at /research/weigh-in-2026-09.json: every run, every request with its host, path, type, status and sizes, every websocket, the exact snippets and widget ids, Chrome and tool versions. The cookie and storage side is at /research/cookies-2026-09.json.

The scripts that produced it are at /research/widget-lab/: bun run weigh-in repeats this whole test. You don’t need them to check a widget, though. Open the vendor’s site or your own staging page, open DevTools, tick “Disable cache”, reload, and filter the Network tab by the widget’s domains. Wait 25 seconds before you read the total: several widgets keep loading long after the page looks finished. Our guide to adding live chat without slowing your site shows where to look, step by step.

so, which one

If weight before the click matters to you, the gap is big. Spookat’s 2.0 kb is less than the loader alone of every widget we tested except Tawk.to’s, whose first request is 1 kb: Intercom’s is 2.5 kb, Tidio’s 3.0, Crisp’s 3.3, Chatwoot’s 6.4 and LiveChat’s 36. And it’s 60 to 510 times less than what the others transfer once they’re running. That’s not an accident of this test: the launcher has a 4 kb budget that fails our build, and everything else waits for a click.

Put the 2 kb version on your site: 14 days free, no card.

liked it? get on the list.

invites go out in small batches. when yours lands: 14 days free, no card.