Overview
WebRTC.rs is an async-friendly WebRTC implementation in Rust, originally inspired by and largely rewriting the Pion stack. The async webrtc crate is a clean, ergonomic, runtime-agnostic rewrite on top of a Sans-I/O core; it ships with Tokio and smol runtime backends, and any other runtime can be plugged in by implementing one trait.
Architecture:
- rtc: Sans-I/O protocol core with complete WebRTC stack (95%+ W3C API compliance)
- webrtc (this crate): a thin async layer over
rtc:PeerConnection— the user-facing async API handle; all operations (create offers/answers, add tracks, create data channels) areasyncPeerConnectionDriver— an internal background event loop, spawned automatically, that owns the sockets, drives the Sans-I/Ortccore, handles timeouts, and dispatches eventsRuntime— a trait abstracting timers, task spawning, and sockets, so the crate is runtime-agnostic
📖 Learn more: Read our architecture blog post for design details and roadmap.
Getting Started
[]
= "0.20"
Or with the smol runtime instead of Tokio:
[]
= { = "0.20", = false, = ["runtime-smol"] }
Feature flags:
| Feature | Default | Description |
|---|---|---|
runtime-tokio |
✅ | Timers, task spawning and sockets via Tokio |
runtime-smol |
The same, via smol | |
runtime-mock |
MockRuntime, a deterministic virtual-clock runtime for tests (no I/O) |
The runtime features are additive: each one only makes a built-in runtime available, so enabling several is safe and a single process can drive different connections on different runtimes.
Bringing your own runtime. The built-ins are not privileged — implement webrtc::runtime::Runtime and pass it per connection with with_runtime, with no #[cfg] edits and no fork. See the custom-runtime example, which runs the full stack on async-executor + async-io with --no-default-features (neither Tokio nor smol compiled in).
Build a peer connection and create an offer:
use Arc;
use ;
use TokioRuntime;
// 1. Implement the PeerConnectionEventHandler trait to handle events
;
async
build() returns an opaque impl PeerConnection. PeerConnection is an object-safe trait, so when you need to store
the connection in a struct or share it across tasks, wrap it:
let pc: = new;
Either way no runtime or interceptor type parameters leak into your own types.
Next steps: browse the API docs or the 35 runnable examples — data channels, media playback, simulcast, ICE restart, insertable streams, and more.
🚨 v0.17.x → v0.20.0: the Sans-I/O rewrite
v0.20.0 is the first stable release of the new Sans-I/O, runtime-agnostic architecture. It supersedes the
Tokio-coupled v0.17.x line, which is now in bug-fix-only maintenance.
Current Status
v0.20.x(master): The current line, and the recommended choice for all new projects. Runtime-agnostic, Sans-I/O, with thePeerConnectionhandle + background driver design described above.v0.17.x: Receives bug fixes only (no new features). Still a valid choice if you have an existing Tokio-coupled integration you are not ready to migrate.
Note that v0.20.0 is a breaking change from v0.17.x — the event-handler traits replace callbacks, and the API is
async throughout. While the version is 0.x, a minor bump may carry breaking changes
(see Semantic Versioning).
What v0.20.0 delivers
The rewrite resolves the core pain points of v0.17.x — callback hell and Arc explosion, resource leaks in
callbacks, and tight Tokio coupling:
✅ Runtime independence
- Runtime-agnostic via a Quinn-style
Runtimeabstraction (timers, task spawning, sockets, DNS) - Feature flags:
runtime-tokio(default) andruntime-smol, additive rather than mutually exclusive - Any third-party runtime works today: implement
Runtime, inject it per connection withwith_runtime. The custom-runtime example does exactly that onasync-executor+async-io, with neither built-in runtime compiled in runtime-mockgives tests a deterministic virtual clock, so timing-dependent behaviour is testable instantly and without sockets
✅ Clean event handling
- One trait-based event handler (
PeerConnectionEventHandler) replaces per-event callback registration, with default no-op methods so you implement only what you need - No more callback
Arccloning orBox::new(move |...| Box::pin(async move { ... })) - Centralized state: the handler is shared as a single
Arc<MyHandler>instead of anArc::cloneper callback. Methods take&self, so mutable handler state goes behind one lock rather than being captured per closure
✅ Sans-I/O foundation
- Protocol logic completely separate from I/O (via the rtc core)
- Deterministic testing without real network I/O
- A thin async driver (
PeerConnectionhandle + backgroundPeerConnectionDriver) over the core
How to Provide Feedback
We welcome your input as v0.20.x grows:
- Review the architecture blog post
- Join discussions on GitHub Issues
- Chat with us on Discord
New projects: start on v0.20.
Migrating from v0.17.x? Open an issue if you hit a gap — migration reports directly shape what we prioritise.
Building and Testing
# Update rtc submodule first
# Build the library
# Run tests
# Build documentation
# Run examples
Semantic Versioning
This project follows Semantic Versioning:
- Patch (
0.x.Y): Bug fixes and internal improvements with no public API changes. - Minor (
0.X.0): Backwards-compatible additions or deprecations to the public API. - Major (
X.0.0): Breaking changes to the public API.
While the version is 0.x, the minor version acts as the major — i.e., a minor bump may include breaking changes. Once
1.0.0 is released, full semver stability guarantees apply.
Pre-release versions are published with the following suffixes, in order of increasing stability:
-alpha.N: Early preview. API is unstable and may change significantly.-beta.N: Feature-complete for the release. API may still have minor changes.-rc.N: Release candidate. No further API changes are expected unless critical issues are found.
For example: 1.0.0-alpha.1 → 1.0.0-beta.1 → 1.0.0-rc.1 → 1.0.0.
Open Source License
Dual licensing under both MIT and Apache-2.0 is the currently accepted standard by the Rust language community and has been used for both the compiler and many public libraries since ( see https://doc.rust-lang.org/1.6.0/complement-project-faq.html#why-dual-mitasl2-license). In order to match the community standards, webrtc-rs is using the dual MIT+Apache-2.0 license.
Contributing
Contributors or Pull Requests are Welcome!!!