← Back to list

GraphRTC: A Decentralised Alternative to Traditional SFU-Based Video Conferencing

An experimental exploration of browser relays, decentralised media forwarding, and why proving an architecture matters more than drawing…

Paras verma · 2026-08-06 13:55 · 0 claps · 21.1 min read
#webrtc #decentralized #video-conferencing #graphrtc
Open on Medium ↗
Wiki topics: FT · Fine-tuning & Adaptation 🔬 · Science · General 🖊️ · Illustration & Drawing 🏛️ · Architecture

GraphRTC: A Decentralised Alternative to Traditional SFU-Based Video Conferencing

An experimental exploration of browser relays, decentralised media forwarding, and why proving an architecture matters more than drawing one.

Around two Weeks ago, I made this LinkedIn post.

At the time, I genuinely had no idea what I was doing.

I had decided to build an application around a technology I barely understood, convinced myself it couldn’t be that difficult, and quickly discovered just how wrong I was.

Everything in that post was true.

I didn’t really understand WebRTC.

I had already spent countless hours debugging signaling problems I couldn’t explain. Video calls would sometimes connect, sometimes fail, and sometimes behave differently for reasons I couldn’t identify. Every fix seemed to create two new bugs somewhere else.

Looking back now, I’m glad I published that post.

Not because the application was impressive, but because it captured exactly where this project started.

There was no elegant architecture.

There were no benchmarks.

There wasn’t even a reliable video call.

Just curiosity, a broken codebase, and an idea that refused to leave me alone.

Today, that project has become GraphRTC, an experimental WebRTC architecture that explores decentralized media forwarding using browser relays instead of relying on a centralized SFU for media forwarding.

Getting from that LinkedIn post to a working implementation took far longer than I expected, involved rebuilding the project from scratch, and taught me more about distributed systems than I ever planned to learn.

This article is the story of that journey.

It started with a question, not a product

When people hear “video conferencing application,” they usually assume the goal was to build another Google Meet or Zoom clone.

That was never my goal.

The project actually started with a question that kept coming back every time I read about WebRTC architectures.

Almost every production-scale video conferencing platform eventually converges on the same design.

Whether it’s Google Meet, Zoom, Microsoft Teams, LiveKit, mediasoup, Janus, or Jitsi, they all introduce a Selective Forwarding Unit (SFU) somewhere in the system.

At first, I accepted that as simply “how WebRTC is done.”

Eventually I realized it was actually an engineering tradeoff.

That realization completely changed how I looked at the problem.

Why almost everyone builds an SFU

To understand GraphRTC, it’s worth understanding why SFUs exist in the first place.

Imagine you’re in a video call with just one other person.

The architecture is beautifully simple.

Your browser sends media directly to theirs, and their browser sends media directly back to you.

No media server.

No forwarding.

Just a peer-to-peer connection.

As soon as more people join, things become more complicated.

In a mesh network, every participant sends their video stream to every other participant.

That means the publisher’s upload bandwidth increases every time someone joins the meeting.

A simplified example looks like this.

The problem isn’t just bandwidth.

Every additional connection means another encoder, more CPU usage, more memory, and more work for the browser.

Eventually, the publisher becomes the bottleneck.

That’s exactly why modern conferencing platforms rely on SFUs.

Instead of uploading the same video stream multiple times, every participant uploads one copy of their media to a central server.

The SFU receives those streams and forwards them to everyone else.

The browser uploads once.

The server handles distribution.

It’s a remarkably elegant solution, and after spending time working with WebRTC, I completely understand why it’s become the industry standard.

But I couldn’t stop asking one question

While reading about SFUs, one thought kept coming back to me.

Modern browsers are far more capable than they were a decade ago.

They have access to hardware video encoders, multiple CPU cores, detailed WebRTC statistics through getStats(), and internet connections that are dramatically faster than they used to be.

So I started wondering.

Could browsers temporarily forward encrypted media for each other instead of relying on a centralised SFU for media forwarding?

Not forever.

Not for meetings with hundreds of participants.

Not as a replacement for production systems like LiveKit or mediasoup.

Just as an experiment.

Instead of introducing a dedicated media-forwarding server, what if capable browsers could cooperate?

One browser could act as a relay.

If that browser left the meeting, another capable participant could take over.

Media forwarding would become distributed instead of centralized.

That single question eventually became GraphRTC.

It’s important to understand what GraphRTC is not trying to prove.

This project isn’t arguing that SFUs are obsolete.

If anything, building GraphRTC made me appreciate why production systems rely on them.

GraphRTC explores a different design point.

Rather than asking,

“How do we build another SFU?”

it asks,

“How far can browser-to-browser media forwarding go before a centralized media server becomes necessary?”

That distinction shaped every engineering decision that followed.

What GraphRTC actually is

One misconception I want to avoid early is calling GraphRTC serverless.

It isn’t.

There is still a server.

Browsers need somewhere to discover each other.

They still need signaling.

They still need room management.

GraphRTC uses a lightweight signaling server to exchange SDP offers, answers, ICE candidates, and topology updates.

What it deliberately avoids is a centralized media-forwarding server.

The signaling server never forwards RTP packets.

Video and audio stay inside the browser network.

When forwarding becomes necessary, selected browsers temporarily relay encrypted media for other participants.

In other words, GraphRTC separates the system into two planes.

Control Plane

  • Room management
  • Signaling
  • SDP exchange
  • ICE candidate exchange
  • Topology coordination

Media Plane

  • Browser-to-browser WebRTC connections
  • Browser relay forwarding
  • Adaptive routing
  • Decentralized media distribution

That separation became one of the core ideas behind the project.

My first attempt failed for exactly the reason you’d expect

Once I had the idea, I made what is probably the most common mistake in software engineering.

I tried to build everything at once.

The application wasn’t supposed to be just a proof of concept. I wanted it to include every feature I could think of from the beginning.

That meant building:

  • Authentication
  • Meeting rooms
  • Chat
  • Audio and video
  • Screen sharing
  • Adaptive bitrate
  • Relay election
  • Dynamic routing
  • Topology management
  • Connection recovery
  • UI polish

On paper, it looked like rapid progress.

Git commits kept piling up.

The UI looked increasingly complete.

New features appeared almost every day.

The application even compiled.

The problem was that every subsystem depended on several others.

If video disappeared, was the problem signaling? ICE negotiation? SDP? Media tracks? Relay routing? Browser permissions? Or had I simply misunderstood how peer connections behaved?

Every bug had half a dozen possible explanations.

Sometimes two peers connected successfully.

Sometimes they didn’t.

Sometimes refreshing the page fixed the issue.

Sometimes it made everything worse.

The application looked convincing enough that someone glancing at it might have assumed it worked.

I knew better.

I couldn’t confidently explain why it worked when it did.

That realization bothered me more than the bugs themselves.

Looking back, WebRTC wasn’t the real problem.

My development process was.

Starting over became the best engineering decision I made

Eventually I accepted something I had been avoiding for weeks.

The existing codebase wasn’t improving.

Every patch solved one problem while introducing another.

Instead of continuing to build on unstable foundations, I deleted most of the project and started again.

At the time, it felt like admitting defeat.

In hindsight, it was probably the best engineering decision I made during the entire project.

This time, I deliberately reduced the scope.

Before thinking about browser relays, adaptive bitrate, topology management, or routing algorithms, I wanted to build the smallest possible video conferencing application that worked consistently.

My checklist became surprisingly simple.

  • Two browsers should be able to join the same room.
  • Audio and video should connect reliably.
  • ICE negotiation should complete every time.
  • Media should flow correctly in both directions.
  • Refreshing the page shouldn’t permanently break the meeting.

Nothing more.

No relay election.

No adaptive routing.

No browser relays.

No benchmarking.

Just a stable foundation.

Only after those requirements became boringly reliable did I allow myself to introduce another layer of complexity.

That small change in mindset completely transformed the project.

Instead of debugging ten unfinished systems simultaneously, I was solving one engineering problem at a time.

Every feature had to prove that it worked before another feature was introduced.

At the time, I thought I was simply writing cleaner code.

What I was actually doing was establishing a philosophy that would shape the rest of GraphRTC.

Build evidence first. Add complexity second.

I didn’t know it yet, but that philosophy would eventually become far more important than any single algorithm or architectural decision I made.

A working video call wasn’t the destination. It was the starting line.

By the time I had a stable two-person video conferencing application, it was tempting to call the project finished.

Two browsers could join the same room.

Audio worked consistently.

Video connected every time.

ICE negotiation completed successfully.

Refreshing the page no longer destroyed the meeting.

For a lot of side projects, that’s where the story ends.

For me, it was where the interesting part finally began.

The original question still hadn’t been answered.

Could browsers relay media for each other without relying on a centralized SFU?

Everything I’d built so far was simply preparing the foundation to explore that question.

Separating the control plane from the media plane

One of the biggest misconceptions people have when they hear “decentralized video conferencing” is assuming there are no servers involved.

That’s not how GraphRTC works.

In fact, GraphRTC still depends on a signaling server.

Without one, browsers wouldn’t know:

  • who is joining
  • which room they’re in
  • how to exchange SDP offers and answers
  • where to send ICE candidates
  • when the topology changes

Trying to eliminate signaling entirely would create a much harder discovery problem that wasn’t relevant to the experiment.

Instead, I separated the system into two independent planes.

Control Plane

The control plane is centralized.

Its responsibilities include:

  • Room creation
  • Participant discovery
  • Signaling
  • SDP exchange
  • ICE candidate exchange
  • Relay election messages
  • Network topology updates

Notice what’s missing.

Media.

The signaling server never forwards video or audio.

It only coordinates browsers.

Media Plane

The media plane is completely different.

Instead of every participant uploading media to one central SFU, browsers establish direct WebRTC connections with each other.

Media remains browser-to-browser.

If forwarding becomes necessary, another browser temporarily acts as the relay.

The signaling server doesn’t see RTP packets.

It doesn’t decode video.

It doesn’t forward media.

Its job ends once browsers know how they’re supposed to connect.

That separation became one of the most important architectural decisions in GraphRTC.

Browser relays instead of media servers

Imagine a meeting with four participants.

A traditional SFU architecture looks roughly like this.

             SFU
          /  |  |  \
         /   |  |   \
        A    B  C    D

Every participant uploads one stream.

The SFU forwards everything.

GraphRTC looks different.

          Source
             |
             |
      Browser Relay
      /     |      \
     /      |       \
    B       C        D

Instead of forwarding media through a dedicated server, one browser becomes the relay.

The source uploads once.

The relay forwards copies to everyone else.

Publisher upload bandwidth stays nearly constant while forwarding work is distributed inside the browser network.

That sounds simple.

Actually deciding which browser should become the relay is where things become interesting.

Choosing the relay isn’t random

Suppose four people join a meeting.

Which browser should become the relay?

The first participant?

The newest participant?

The one with the fastest internet?

The one closest to everyone else?

Choosing randomly would work.

It would also perform terribly.

Instead, GraphRTC continuously evaluates every participant using several metrics.

Some examples include:

  • Estimated available upload bandwidth
  • Round-trip latency
  • Packet loss
  • Number of existing forwarding responsibilities
  • Browser capability
  • Connection stability

Each participant receives a score.

The highest-scoring participant becomes the preferred relay.

If network conditions change, relay election can happen again.

This isn’t meant to compete with sophisticated production scheduling algorithms.

The goal is simply to make browser relay selection deterministic and measurable.

Rather than saying

“Browser A became the relay.”

the system should be able to explain

“Browser A became the relay because it currently has the highest relay score.”

That distinction matters.

If you can’t explain architectural decisions, debugging them becomes almost impossible.

The relay graph

One relay isn’t enough forever.

Meetings change.

People join.

People leave.

Network conditions fluctuate.

A browser that was the best relay five minutes ago might become the worst choice after switching to a slower connection.

GraphRTC therefore represents the meeting as a graph rather than a fixed topology.

Participants become nodes.

Peer connections become edges.

Relay relationships become directed forwarding paths.

Instead of asking

“Who is connected to whom?”

the system asks

“What is currently the cheapest path for media?”

That small shift opens the door for routing algorithms instead of hardcoded forwarding rules.

Routing media through the graph

Once the network is represented as a graph, routing becomes a graph problem.

Every participant knows:

  • who its neighbors are
  • which browser is acting as relay
  • where media should flow

Whenever the topology changes, routes are recomputed.

The implementation borrows ideas from shortest-path algorithms rather than inventing an entirely new routing system.

In practice, that means:

  • minimizing unnecessary forwarding
  • avoiding loops
  • recovering from relay failures
  • maintaining deterministic paths

The implementation isn’t trying to compete with Internet routing protocols.

It’s solving a much smaller problem inside a browser network.

Still, treating media forwarding as a routing problem instead of a collection of special cases made the implementation dramatically simpler.

Keeping everyone synchronized

One challenge appeared almost immediately.

Every browser had a slightly different view of the network.

Someone joins.

Another participant disconnects.

A relay changes.

ICE reconnects.

Without coordination, different browsers could make different routing decisions.

That’s obviously a problem.

To solve this, GraphRTC periodically exchanges topology information.

Instead of assuming every browser already knows the current network, participants share updates whenever meaningful changes occur.

You can think of it as a lightweight gossip protocol.

Not because browsers are chatting with each other.

Because network state gradually spreads throughout the graph until everyone agrees on the current topology.

Eventually, every participant converges on the same understanding of the meeting.

Adapting to changing networks

Real networks aren’t static.

Bandwidth changes.

Latency spikes.

Packet loss appears.

Browsers expose a surprising amount of information through the WebRTC statistics API.

Using getStats(), GraphRTC can observe metrics such as:

  • Available outgoing bitrate
  • Packet loss
  • Round-trip time
  • Frames encoded
  • Frames dropped
  • Quality limitation reason
  • Encoder bitrate
  • RTP throughput

Those statistics become feedback.

Instead of assuming the network is healthy, GraphRTC continuously measures it.

When available bandwidth decreases, encoder bitrate can be reduced.

When conditions improve, bitrate increases again.

The goal isn’t perfect congestion control.

Browsers already implement sophisticated congestion control internally.

The goal is allowing higher-level topology decisions to react to observable network conditions instead of static assumptions.

Screen sharing introduced another problem

Video conferencing isn’t only about webcams.

Eventually I implemented screen sharing.

At first glance, it seemed straightforward.

Replace the camera with the shared screen.

Done.

Except that’s not what users expect.

Applications like Google Meet, Teams, and Zoom continue transmitting your camera while simultaneously sharing your screen.

That meant GraphRTC needed two independent video tracks.

One for the camera.

One for the screen.

Rather than replacing the camera stream, screen sharing became a dedicated sender.

Starting screen sharing simply added another outgoing video track.

Stopping screen sharing detached only that sender while leaving the camera untouched.

It sounds like a small implementation detail.

In practice, it required rethinking how media tracks were managed throughout the application.

The architecture finally existed

After weeks of rebuilding, something interesting happened.

GraphRTC finally looked like the system I had imagined at the beginning.

There was a signaling layer coordinating participants.

Browser relays were elected dynamically.

Media flowed through relay nodes.

Topology updates propagated through the network.

Adaptive quality decisions reacted to changing conditions.

Screen sharing worked alongside camera video.

On paper, everything looked finished.

There was just one problem.

I had absolutely no proof that any of it was actually happening.

Browsers were exchanging media.

Video appeared on the screen.

The UI looked convincing.

But appearances are deceptive.

Was media really flowing through the relay?

Or had browsers quietly established direct peer-to-peer connections behind my back?

Were bitrate adaptations actually taking effect?

Or was I simply watching numbers change without affecting RTP traffic?

Had I built a decentralized media-forwarding system?

Or had I built an application that merely looked like one?

That realization completely changed the direction of the project.

I stopped adding features.

Instead, I started asking a different question.

How do I prove the architecture is behaving exactly the way I think it is?

That question eventually led me to build an automated browser benchmarking and verification framework that became just as important as GraphRTC itself.

Trusting Measurements Instead of the UI

At this point, GraphRTC looked finished.

Participants could join rooms.

Audio and video worked reliably.

Screen sharing functioned as expected.

Relay election was producing sensible topologies.

The UI showed media flowing through what appeared to be browser relays.

If I had stopped there, I probably would have called the project complete.

But distributed systems have a habit of looking correct long before they actually are.

The question that kept bothering me wasn’t whether the application worked.

It was whether it was working for the reasons I thought it was.

The UI can lie.

When you build a web application, the UI is usually enough to tell you whether something works.

A button changes color.

A message appears.

A request succeeds.

WebRTC is different.

Suppose two participants can see each other’s video.

That tells you almost nothing about how the media reached them.

Maybe it went through the relay.

Maybe the browsers established a direct connection.

Maybe renegotiation silently bypassed the topology you carefully constructed.

The video still appears either way.

The UI can’t tell you.

I realized something uncomfortable.

I had spent weeks implementing relay election and routing algorithms, yet I had no objective evidence that media was actually following those routes.

I wasn’t building confidence.

I was building assumptions.

That wasn’t good enough.

Every architectural claim needed evidence

Around this point, I stopped thinking like someone building features.

Instead, I started thinking like someone writing an experiment.

Every claim I wanted to make about GraphRTC needed measurable evidence.

For example, I wanted to claim:

  • Media is forwarded through browser relays.
  • Relay routing reduces publisher upload bandwidth.
  • Bitrate adaptation actually changes encoder output.
  • Relay forwarding scales better than mesh.
  • Browser relays remain stable as viewer count increases.

Those sound like reasonable statements.

But none of them could be supported by screenshots.

They needed measurements.

So before writing another feature, I built something entirely different.

A browser benchmarking framework.

Ironically, that framework became one of the most important parts of the project.

Building a verification pipeline

Instead of manually opening browsers and watching videos play, I wanted reproducible experiments.

Every benchmark should answer one engineering question.

Every run should produce the same artifact.

Every claim should be backed by browser statistics.

That eventually became the benchmark harness.

It wasn’t glamorous.

Nobody notices benchmarking infrastructure when it works.

But without it, every result in this article would simply be an opinion.

The idea was simple

Rather than running one successful test and declaring victory, every scenario would execute multiple times.

Five runs.

Identical conditions.

Same browser.

Same machine.

Same synthetic media.

Then aggregate everything.

Outliers become obvious.

Flaky behavior becomes obvious.

Patterns become obvious.

Suddenly I wasn’t looking at one successful demo.

I was looking at data.

Why synthetic media?

One question people often ask is:

“Why didn’t you benchmark using a webcam?”

Because webcams introduce variables I didn’t want.

Lighting changes.

Exposure changes.

Motion changes.

Compression changes.

Instead, every benchmark used synthetic canvas-generated media.

The workload remained identical across every execution.

The benchmark environment looked like this:

  • Windows 11
  • Chromium 149
  • Playwright 1.61
  • VP8 video
  • 640×360
  • 30 FPS
  • Headless browser
  • Same machine
  • STUN only
  • No TURN

That environment doesn’t represent the real world.

It represents a controlled laboratory.

And that’s exactly what I wanted.

The goal wasn’t to simulate the Internet.

The goal was to isolate GraphRTC’s behavior.

Five gates, one purpose

Rather than one enormous integration test, I split verification into several independent gates.

Each gate answered one question.

Gate 0 — Media Path Verification

The first question was surprisingly basic.

Is media actually flowing through the relay?

This benchmark compared:

  • Source outbound bytes
  • Relay inbound bytes
  • Relay outbound bytes
  • Viewer inbound bytes

If relay forwarding worked correctly, those numbers should line up exactly as expected.

If they didn’t, the architecture was wrong.

Simple.

Objective.

Repeatable.

Gate 2 — Bitrate Adaptation

The second question focused on encoder behavior.

Could GraphRTC actually reduce video bitrate?

Using browser statistics, I compared baseline encoder bitrate against a constrained configuration.

Across repeated runs, the encoder dropped from roughly:

1.63 Mbps

to approximately

101 Kbps

That’s about a 94% reduction.

The interesting part wasn’t the percentage.

The interesting part was knowing the encoder had actually changed behavior rather than simply believing it had.

Gate 3 — Relay Fan-Out

Next came the experiment I had been waiting for.

If one browser becomes the relay, what happens as more viewers join?

The benchmark gradually increased viewer count.

2 viewers.

4 viewers.

6 viewers.

8 viewers.

Instead of looking only at publisher bandwidth, I also measured aggregate relay throughput.

This answered a completely different question.

Not

“Can GraphRTC relay media?”

but

“How much media can one browser relay?”

Those are very different experiments.

Gate 4 — Mesh versus GraphRTC

Finally came the comparison I cared about most.

Mesh.

Versus.

Browser relay.

Same browser.

Same media.

Same workload.

Different architecture.

If browser relays were useful, publisher upload bandwidth should remain roughly constant.

If mesh behaved as expected, upload bandwidth should grow with every additional participant.

That experiment became the strongest piece of evidence in the project.

The results surprised me

One thing I tried very hard to avoid was designing benchmarks that simply confirmed my expectations.

If GraphRTC performed worse than mesh, I wanted to know.

If relay forwarding introduced unacceptable overhead, I wanted to know.

If the architecture collapsed after four viewers, I wanted to know.

Fortunately, the data told a more interesting story.

Publisher upload bandwidth

At four viewers, the comparison was straightforward.

That’s roughly a 75% reduction in publisher upload bandwidth.

Notice what happened.

The source browser uploaded only one stream.

The relay browser duplicated it.

Exactly as intended.

This wasn’t just a diagram anymore.

It was measurable behavior.

Relay capacity

The next benchmark looked at the relay itself.

How much traffic could one browser actually forward?

Across the benchmark suite, the relay sustained approximately

13.17 Mbps

of aggregate outbound traffic while serving eight viewers.

Again, this wasn’t a claim about Internet-scale conferencing.

It simply demonstrated that browser relay forwarding was practical under the tested conditions.

Adaptive bitrate

The adaptation benchmark produced another encouraging result.

When bitrate constraints were applied, encoder output dropped by roughly

94%

while continuing to transmit media.

That showed GraphRTC could react to changing network policies without interrupting the session.

Again, measured.

Not assumed.

But here’s what those numbers do not prove

This is probably the most important section of the article.

These benchmarks do not prove that browser relays can replace SFUs.

They don’t prove GraphRTC scales to hundreds of participants.

They don’t prove production readiness.

In fact, the benchmark environment was intentionally conservative.

Everything ran on:

  • One machine
  • One browser engine
  • Synthetic media
  • Local networking
  • No TURN
  • No packet loss
  • No network impairment

That means the results answer one question.

Does the architecture behave as expected under controlled conditions?

Yes.

They do not answer questions like:

  • How does this behave across continents?
  • What happens on mobile devices?
  • How does relay election respond to severe packet loss?
  • Does Firefox behave differently from Chromium?
  • What happens under TURN-only connectivity?
  • How much latency does multi-hop forwarding introduce?

Those questions remain unanswered.

And that’s okay.

Good engineering is just as much about defining the limits of your conclusions as it is about presenting results.

I ended up trusting numbers more than demos

Somewhere during the benchmark work, I noticed a change in how I evaluated the project.

Early on, success meant seeing video appear on another browser.

Later, success meant browser statistics matched the architecture.

Eventually, I stopped trusting demonstrations entirely.

I trusted measurements.

The UI could lie.

The benchmarks couldn’t.

That shift fundamentally changed how I approached GraphRTC.

Instead of asking,

“Does it work?”

I started asking,

“Can I prove that it works?”

Ironically, that became the most valuable outcome of the entire project.

Because once you start asking that question, architecture diagrams become hypotheses.

Benchmarks become evidence.

And engineering becomes much more interesting.

More Than Just Another WebRTC Project

If you’ve made it this far, you might think this story is about building a decentralized video conferencing system.

In some ways, it is.

But after spending months working on GraphRTC, I don’t think that’s what I’ll remember most.

The thing that stayed with me wasn’t relay election.

It wasn’t routing.

It wasn’t SDP.

It wasn’t even WebRTC.

It was learning how to approach difficult engineering problems.

I still don’t consider myself a WebRTC expert

One thing I’ve noticed after sharing GraphRTC is that people assume I must know WebRTC inside out.

I don’t.

In fact, this project made me realize how much I still don’t know.

There are entire areas I barely touched.

  • Simulcast
  • Scalable Video Coding (SVC)
  • Advanced congestion control
  • Hardware encoders
  • TURN-heavy deployments
  • Mobile browsers
  • Safari interoperability
  • Multi-hop forwarding
  • Large-scale distributed relay selection

Every time I answered one question, three more appeared.

At first, that felt frustrating.

Eventually, I realized that’s simply how engineering works.

The more you understand a system, the more clearly you can see the boundaries of your own knowledge.

And honestly, I think that’s a good thing.

The project taught me how to think differently

When I first started GraphRTC, I thought software engineering was mostly about writing code.

I don’t think that anymore.

The amount of code I wrote certainly increased.

But the biggest improvements didn’t come from writing more code.

They came from asking better questions.

Questions like:

  • Why did this negotiation fail?
  • Which browser statistics actually prove media forwarding?
  • How can I reproduce this bug consistently?
  • What assumptions am I making without evidence?
  • If I couldn’t look at the UI, how would I know the architecture is working?

Those questions forced me to think differently.

Instead of treating features as finished when they appeared to work, I started treating them as hypotheses.

Every hypothesis needed evidence.

Only then could I confidently move on.

Looking back, that philosophy probably had a bigger impact on the project than any specific implementation.

AI changed how I build software, not what software I build

One question I know people will ask after reading this article is:

“How much of GraphRTC was built using AI?”

The honest answer is:

A lot.

Almost every commit involved AI in some way.

Sometimes it generated code.

Sometimes it explained browser behavior.

Sometimes it reviewed architectural decisions.

Sometimes it challenged my assumptions.

Sometimes it simply saved me from reading another hundred pages of documentation.

But there’s something important I learned along the way.

The hardest part of GraphRTC was never typing code.

It was understanding the problem well enough to recognize when the code was wrong.

AI can generate an implementation.

It cannot tell you whether your distributed system actually behaves the way you think it does.

Only measurements can do that.

That’s why the benchmark harness eventually became just as important as the implementation itself.

Without it, I wouldn’t have trusted any of my own conclusions.

Open source felt like the obvious next step

When I finally reached a point where I was happy with GraphRTC, I had a decision to make.

Leave the project on my computer.

Or publish everything.

I chose the second option.

Not because I think GraphRTC is finished.

Quite the opposite.

I think unfinished engineering projects are often more valuable than polished ones.

The repository doesn’t just contain the application.

It contains the entire engineering process.

You’ll find:

  • The GraphRTC implementation
  • Architecture documents
  • Design RFCs
  • Verification plans
  • Browser benchmark harness
  • Benchmark artifacts
  • Experimental results
  • Documentation explaining why certain architectural decisions were made

Hopefully someone reading the code learns something.

Hopefully someone finds mistakes.

Hopefully someone builds something better.

That’s one of the reasons I enjoy open source.

Projects improve when more people look at them.

GraphRTC is available on GitHub

👉 **https://github.com/paras-verma7454/graphRTC**

If you’re interested in WebRTC, browser networking, or distributed systems, feel free to explore the repository.

I’d genuinely appreciate feedback, questions, or even criticism.

Github Repo

Github Repo

Where GraphRTC goes from here

Although the first benchmark suite answered the original research question, there are still many questions left to explore.

The current benchmark environment was intentionally conservative.

Everything ran on:

  • Chromium
  • One machine
  • Local networking
  • Synthetic media
  • VP8
  • STUN only
  • Five benchmark runs per scenario

That made the experiments reproducible.

It also defines the limits of the conclusions.

The next phase of GraphRTC isn’t adding more features.

It’s expanding the experimental boundary.

Some of the things I’d like to investigate include:

  • Cross-device testing instead of multiple browser contexts on one machine.
  • Real network conditions with latency, packet loss, and bandwidth constraints.
  • TURN-based deployments.
  • Additional media profiles, including 720p and 1080p workloads.
  • Cross-browser testing with Firefox and Safari.
  • Better relay scoring algorithms.
  • CPU-aware relay selection.
  • Multi-hop routing experiments.
  • Larger browser relay topologies.

None of these are promises.

They’re simply the next questions I want to answer.

Looking back

When I started this project, I thought I was building a video conferencing application.

Somewhere along the way, it became an experiment in distributed systems.

Eventually, it became an exercise in verification.

That’s probably the biggest surprise.

The hardest part wasn’t implementing browser relays.

The hardest part was proving they actually worked.

That completely changed how I think about engineering.

Today, if someone shows me a distributed system and says,

“It works.”

My first question isn’t

“Can you show me the demo?”

It’s

“How do you know?”

Because architecture diagrams are ideas.

Running software is encouraging.

But reproducible measurements are evidence.

Final thoughts

GraphRTC doesn’t prove that browsers should replace centralized SFUs.

In fact, if anything, building this project made me appreciate just how elegant and practical SFUs really are.

What GraphRTC does show is something much smaller, but also much more defensible.

Under controlled conditions, browser-selected relays can measurably reduce publisher upload bandwidth while maintaining decentralized media forwarding. More importantly, those claims can be backed by reproducible benchmarks instead of architecture diagrams or screenshots.

That was the original question I set out to answer.

Along the way, I learned far more than I expected.

Not just about WebRTC.

Not just about distributed systems.

But about engineering itself.

Building complex software isn’t about writing thousands of lines of code.

It’s about breaking difficult problems into smaller ones, questioning your assumptions, measuring everything that matters, and being willing to rebuild when the foundation isn’t solid.

Looking back at that LinkedIn post now, I smile a little.

At the time, I thought the goal was simply to build a video conferencing application.

I didn’t realize I was really learning how to think like an engineer.

Thank you for reading.

If you found this interesting, I’d love to hear your thoughts, suggestions, or criticisms. If you’re working on WebRTC, distributed systems, or browser networking, let’s connect and learn from each other.

GitHub: https://github.com/paras-verma7454/graphRTC


메타데이터
post_id
8d3a45f7ca55
slug
graphrtc-a-decentralised-alternative-to-traditional-sfu-based-video-conferencing-8d3a45f7ca55
url
https://medium.com/@parasverma7454/graphrtc-a-decentralised-alternative-to-traditional-sfu-based-video-conferencing-8d3a45f7ca55
canonical_url
https://medium.com/@parasverma7454/graphrtc-a-decentralised-alternative-to-traditional-sfu-based-video-conferencing-8d3a45f7ca55
author_url
https://medium.com/@parasverma7454
status
ok
fetched_at
2026-08-12 18:19:41