A Ground-Up Rewrite of Our Calling Client
We're introducing a new video calling library, rsys, across our products, including Messenger, Instagram, Portal, and Workplace chat. The library replaces a seven-year-old fork of WebRTC that was originally built to support native audio calling. That initial implementation served us well, but it was written when our focus was a single platform and a single set of features. Since then, video calling, group calling, video chat heads, and interactive AR effects have been layered on top, and the resulting complexity made it difficult to support new products or adapt to changing requirements.
The old library carried a significant amount of Messenger-specific code, which limited its usefulness for other apps. It also relied on separate signaling protocols for peer-to-peer and group calling, which meant writing features twice and created a deep divide in the codebase. Keeping the WebRTC fork current with upstream improvements was another ongoing burden, and we found ourselves falling short of delivering reliable service on low-powered devices and low-bandwidth networks.
Rewriting the entire client core was a substantial undertaking that involved engineers from across the company. The goal was a generic, extensible framework that could serve all of our calling use cases. rsys now runs on Android, iOS, macOS, Windows, and Linux. It's roughly 20 percent smaller, making it easier to integrate into size-constrained platforms such as Messenger Lite. The new codebase has around 90 percent unit test coverage and a thorough integration testing framework covering our major calling scenarios.
Driving Down Binary Size
At its peak, the binary size had reached as much as 20 MB. Trimming a few sections of code helped, but hitting our size targets required a complete rewrite. We chose not to sacrifice features that users rely on, such as AR effects; instead we focused on optimizing the library's architecture to cut bloat without cutting functionality.
We introduced a plug-and-play framework using Bazel selects to compile features selectively only into apps that need them, and we adopted a generic framework for writing new features based on the Flux architecture. We also moved away from heavily templated libraries such as Folly toward more optimally sized options such as Boost.SML. These choices helped reduce the core binary size from approximately 9 MB to about 7 MB, a roughly 20 percent reduction.
Unified Architecture
Our principal aim was to minimize code complexity and redundancy. A shared architecture allows for global optimization instead of local, feature-by-feature fixes, and it makes code reuse straightforward. We tackled this on four fronts:
- Signaling: We introduced a state machine to unify protocol semantics for peer-to-peer and group calling. Protocol-specific details are abstracted away from the rest of the library, leaving signaling responsible solely for negotiating shared state between call participants. This removes duplicate code, so features are written once and calls behave the same way whether peer-to-peer or in a group.
- Media: The same state machine approach was applied to the media stack, this time capturing the semantics of open source WebRTC APIs. In parallel, we replaced our forked WebRTC version with the latest upstream code, keeping only product-specific optimizations. Because the state machine depends only on API semantics, future WebRTC updates are available as regular pulls with no downtime.
- SDK: For feature-specific states, we used Flux architecture to manage data and provide an API akin to React JS–based applications. Every API call routes actions through a central dispatcher, then through reducer classes that emit model objects. Those models go to bridges containing feature-specific business logic, which may in turn produce further actions. Model updates ultimately reach the UI, where they're rendered as platform-specific views. This structure means a feature is fully defined by its reducer, bridge, actions, and models, and it can be configured at runtime differently for different apps.
-
OS: We abstracted OS-dependent functions behind generic interfaces. Platform-specific code for tasks like creating hardware encoders and decoders or handling threading abstractions is still necessary, but the interfaces let different platforms plug in implementations through proxy objects. Since modern desktop platforms like macOS and Windows had to be supported alongside their mobile counterparts, we made heavy use of the
cxx_libraryrule in Buck to handle platform-specific compiler flags and linker arguments.
With the new architecture in place, the library is no longer bound to Messenger-specific solutions or a single signaling protocol. This, in turn, removes the burden of maintaining a forked WebRTC codebase and gives us a foundation designed for cross-app calling and remote presence in the future. We expect the work to let our calling features scale across products and platforms, without visible disruption for users; the experience remains familiar, and is just built to last longer.



