Of the JavaScript on the median mobile page in the 2024 Web Almanac, 168 kilobytes was first-party and 375 kilobytes was third-party.1 More than half of the script on an ordinary page, by weight, was written by someone other than the site's owner. The Almanac's chapter on third parties found them on 92 percent of pages, with script the most common type of third-party request.2 An ad network's script, the one a publisher adds to carry its ads, is one of those requests. What that script costs the page at load time depends less on what it does than on how the element that loads it is written.
The browser stops for a script element that carries no attribute
Under the HTML Living Standard, a browser has three ways to handle an external script, and they differ only by an attribute on the element.3 The first, with neither attribute, is the default: the script "is fetched and evaluated immediately, blocking parsing until these are both complete." The second, defer, fetches the file in parallel with parsing and evaluates it "when the page has finished parsing." Under the third, async, the file is also fetched in parallel but evaluated "as soon as it is available (potentially before parsing completes)." Only the first mode holds the parser while the file crosses the network, and nothing after the element is drawn until the file arrives.
Google's Lighthouse audit turns the distinction into a test a publisher can run on their own page. It flags a script element in the head of the document that carries neither defer nor async.4 The report "lists all URLs blocking the first paint of your page." Size is not part of that definition, and neither is purpose. An analytics script, an ad script, and a font loader are treated alike if they sit in the head without an attribute.
Third-party script is the case the loading guidance was written for
The guidance on web.dev is direct about scripts from outside the site. Synchronous scripts, it says, "delay DOM construction and rendering," the DOM being the browser's working model of the document.5 The rule that follows is to "always load third-party scripts asynchronously unless the script has to run before the page can be rendered." Async suits a script that must run early in the loading process; defer suits "less critical resources," its example being a video player below the fold. An ad script that works after the text is on screen falls under the less critical heading by that test.
The practice has spread, though not to every page. The 2024 Web Almanac found async on 87 percent of pages, up from 76 percent in 2022, and defer on 47 percent, up from 42 percent.1 Those figures describe pages as a whole. A page whose own scripts are deferred can still carry one blocking element that a third party supplied. An install snippet, unless the publisher edits it, loads the way the network wrote it.
A script that waits for the DOM has no first paint to block
The browser marks the moment the parser finishes building the DOM with an event. According to MDN, DOMContentLoaded "fires when the HTML document has been completely parsed, and all deferred scripts" have downloaded and executed.6 The event does not wait for images, subframes, or async scripts. A script that begins its work at that moment therefore starts after the text exists, without waiting for the last image.
The AdBubbles embed, adbubbles.js, loads after the page's DOM is ready. It is one static file, served through Cloudflare's cache and written in plain JavaScript with no dependencies. The per-site data it reads is also a static file, regenerated when a campaign changes and cached at the edge. A page view therefore costs the ad server nothing, and the only dynamic request is a small beacon. The embed scans only the scope the publisher configured, and skips headings, links, code, preformatted text, form controls, navigation, header, footer, and anything marked data-noads. The installation notes describe the scope setting.
Opening a publisher account costs nothing, and so does adding the embed to a site. A publisher who adds a site can request a crawl showing which of its words are currently sellable. The scope, the maximum bubbles per page, and the excluded advertiser categories are chosen from the same account.
The beacon sent when a reader leaves is the other half of the cost
The moment a reader leaves is a script's other chance to slow them down. Analytics code that reports at unload once relied on techniques such as "a blocking synchronous XMLHttpRequest call."7 MDN records that all of those methods "block unloading the document, which slows down navigation to the next page." The sendBeacon method replaces them. It sends a small HTTP POST request asynchronously. The browser transmits the data "when the user agent has an opportunity to do so, without delaying unload or the next navigation."
The embed reports with sendBeacon, and it sends three kinds of beacon. One view is sent per page load. One open is sent when a bubble has been visible for one second, and one click is sent when a reader follows the link inside a bubble. Nothing else leaves the page.
AdBubbles sets no cookie, stores no identifier, and collects no personal data from readers. The reader's country is read from a Cloudflare request header rather than from a stored IP address. Matching words on a publisher's pages to the advertiser who bought them is the whole of what AdBubbles does. AdBubbles has no say in which people see a bubble, never follows a reader from one page or site to the next, and promises no result. The privacy position is published in full.
Browsers now count what an ad costs and act on the total
Chrome's Heavy Ad Intervention shows what one browser vendor considers too much. An ad counts as heavy only "if the user has not interacted with it (for example, has not tapped or clicked it)."8 Beyond that, any one of three thresholds is enough. The first is more than 4 megabytes of network bandwidth. The second is more than 15 seconds of main-thread time in any 30 second window, and the third is more than 60 seconds in total.
When the intervention fires, the content of the ad frame is "replaced with an Ad removed message," and the mechanism acts on ad-tagged iframes.8 The measure is bytes and main-thread time; the ad's content plays no part. A bubble is rendered inside a Shadow DOM within the publisher's own document rather than in an iframe, so this mechanism is aimed at a different kind of ad. What the embed adds to a given page, in bytes and in main-thread time, is something that page's own measurements will show.
The check a publisher can make on their own page needs nothing beyond a browser. The page source lists every script element in the head, and each one either carries async or defer or carries neither. Lighthouse's Opportunities section lists the ones with neither as URLs blocking the first paint.4 A network's snippet on that list is blocking the page by the standard's definition, whatever the network's documentation says about its weight. Whether an ad script joins that list is decided by the attribute on its element, and the attribute is written into the snippet before any publisher pastes it.