This Month in Ladybird - May 2026 - Ladybird
This Month in Ladybird — May 2026
Hello friends! May was a busy month, with 464 pull requests merged. Ladybird now clears Cloudflare Turnstile, and scrolling has moved onto an out-of-process compositor. Here’s what we’ve been up to.
Welcoming new sponsors
Ladybird is entirely funded by the generous support of companies and individuals who believe in the open web. This month, we’re excited to welcome the following new sponsors:
CodeRabbit with $10,000
wheelofnames.com with $1,000
We’re incredibly grateful for their support. If you’re interested in sponsoring the project, please contact us.
Passing Cloudflare Turnstile
Cloudflare Turnstile is the bot-detection challenge that sits in front of a large number of sites, deciding whether you’re a real browser before it lets you reach the page. Until this month it refused to let Ladybird through, which kept us out of a lot of sites.
Two things got us here. Over the past several months we’ve been chipping away at the many little web compatibility gaps that Cloudflare’s checks expect a real browser to handle. Cloudflare also made a change on their end to help. We can’t say exactly which combination of the two did it, but in our testing Ladybird now clears the challenge and loads the page.
Async scrolling on an out-of-process compositor
Scrolling has historically run on the main thread, so a busy page could make scrolling stutter. Scrolling now runs asynchronously on the compositor, covering viewport scrolling (#9370), nested scrollable boxes (#9407), and viewport scrollbars (#9386). It’s enabled by default (#9413).
The compositor also runs in its own process now (#9564), with backing store management moved across the boundary and crash recovery so a compositor crash doesn’t take down the page (#9589). It’s the default compositor backend (#9604). Scrolling stays smooth even while the main thread is busy with layout or JavaScript.
Here’s IMDb being scrolled while it loads, without and with async scrolling:
Without async scrollingWith async scrolling<br>Media Source Extensions on by default
Media Source Extensions is now on by default after a month behind an experimental flag (#9673). MSE is how streaming sites feed media to the player in chunks, so turning it on lets a lot more video sites work, and unlocks adaptive streaming on YouTube: higher resolutions, higher frame rates, and quality switching. The same PR added SourceBuffer capacity limits, so Ladybird evicts buffered media data instead of holding onto it indefinitely.
Under the hood, LibMedia’s pipeline was rewritten to allow media data processing to be inserted easily, and seeks are now handled as part of that flow instead of being tracked separately (#9288). Forward video seeks are now aware of already-decoded data in the pipeline, making forward frame skipping on YouTube instant (#9495).
The built-in media controls now display buffered ranges, which are accurately reported for MP3, FLAC, Ogg, WAV, Matroska, and MP4 (#9705).
HTMLMediaElement.getStartDate() is implemented (#9218).
On-disk JavaScript bytecode cache
JavaScript bytecode is now cached on disk (#9259). RequestServer stores bytecode cache blobs as sidecar data alongside script HTTP disk cache entries, and warm cache hits can map bytecode from disk instead of keeping it in anonymous heap memory. On large modern websites, that saves hundreds of megabytes while also avoiding recompilation.
The mapped cache is lazily decoded, so warm hits only pay to decode functions, source text, and instruction streams as they’re actually used (#9433, #9453, #9466, #9472, #9486, #9540).
A WebAssembly JIT
LibWasm gained a Cranelift-based just-in-time compiler (#8866). Instead of interpreting WebAssembly bytecode, hot modules are compiled to native code. That shows up in benchmarks: roughly 8x on CoreMark, and 3-4x on function-call microbenchmarks. The JIT supports macOS (#9356) and is enabled by default (#9758).
Content blocking
We rebuilt Ladybird’s content blocker. The old one was a simple URL substring matcher; the new one is backed by brave/adblock-rust, Brave’s mature open-source content-filtering library (#9538, #9587). It reads standard Adblock Plus / EasyList-style filter lists, and beyond network request filtering it now does cosmetic filtering, hiding page elements with CSS, including generic class and id selectors gathered from shadow-including descendants. Block lists are configurable and can be loaded from settings (#9601).
The HTML parser is now written in Rust
Continuing our push to handle untrusted data from the web in memory-safe languages, the HTML parser fully migrated to Rust this month. The tokenizer (#9429), tree-construction parser (#9457), and speculative preload scanner (#9462) are now all Rust. The Rust parser is also about 10% faster than the C++ version it replaced, and was checked against a corpus of around 7,000 websites for DOM parity with the old parser.
The URL...