WebRTC without a signaling server

WebRTC is incredibly exciting, and is starting to see significant deployment: it’s available by default in Chrome and Firefox releases now. Most people think of WebRTC as an API for video calling, but there’s a general purpose method for directly sharing data between web browsers (even when they’re behind NAT) in there if you look harder. For example:

  • P does peer-to-peer mesh networking in JavaScript.
  • TowTruck allows you to add collaboration features (collaborative text editing, text chat, voice chat) to websites.
  • PeerCDN forms a network from a site’s visitors, and uses it to offload serving up static content away from the web server and on to the networked peers.
  • The Tor Project is interested in using WebRTC to enable volunteers with JavaScript-enabled web browsers to become on-ramps onto the Tor network for users under censorship, as part of the Flash Proxies project. The idea is that censoring organizations may block the public Tor relays directly, but they can’t easily block every random web browser who might route traffic for those relays over WebRTC, especially if each web browser’s proxy is short-lived.

All of this activity means that we might finally be close to solving — amongst other important world problems — the scourge of xkcd.com/949:

xkcd: File Transfer, used under CC-BY-NC 2.5.

I wanted to experiment with WebRTC and understand its datachannels better, and I also felt like the existing code examples I’ve seen are unsatisfying in a specific way: it’s a peer-to-peer protocol, but the first thing you do (for example, on sites like conversat.io) is have everyone go to the same web server to find each other (this is called “signaling” in WebRTC) and share connection information.

If we’re going to have a peer-to-peer protocol, can’t we use it without all visiting the same centralized website first? Could we instead make a WebRTC app that just runs out of a file:/// path on your local disk, even if it means you have to manually tell the person you’re trying to talk to how to connect to you?

It turns out that we can: I’ve created a serverless-webrtc project on GitHub that decouples the “signaling server” exchange of connection information from the WebRTC code itself. To run the app:

  • download Firefox Nightly.
  • git clone git://github.com/cjb/serverless-webrtc.git
  • load file:///path/to/serverless-webrtc/serverless-webrtc.html

You’ll be asked whether you want to create or join a channel, and then you’re prompted to manually send the first party’s “WebRTC offer” to the second party (for example, over an instant message chat) and then to do the same thing with the second party’s “WebRTC answer” reply back. Once you’ve done that, the app provides text chat and file transfer between peers, all without any web server. (A STUN server is still used to find out your external IP for NAT-busting.)

There are open issues that I’d be particularly happy to receive pull requests for:

#1: The code doesn’t work on Chrome yet. Chrome is behind Firefox as far as DataChannels are concerned — Chrome doesn’t yet have support for binary transfers, or for “reliable” (TCP, not UDP) channels (Firefox does). These are both important for file transfers.

#2: Large file transfers often fail, or even hang the browser, but small transfers seem to work every time. I’m not sure whose code is at fault yet.

#3: File transfers should have a progress bar.

Thanks for reading this far! Here’s to the shared promise of actually being able to use the Internet to directly share files with each other some time soon.


  1. Thanks for the comment. I’m interested in knowing the answer too, because I want to help Tor use WebRTC for flashproxies, and immediately leaking your IP address to a public STUN server is a privacy dealbreaker there.

    I haven’t tried to stop the STUN interaction happening yet, but the test should be simple (although it needs to be carried out on each browser separately in case they have differing implementations): the cfg argument to RTCPeerConnection() allows an iceServers attribute that takes stun: addresses. What happens if you pass the empty string there, or (say) a IP?

    I agree that the spec ought to be modified to allow disabling STUN/TURN altogether. It looks like IceTransport="none" is another idea to try.

  2. Would it be possible to eliminate the need for a STUN server by using IPv6, assuming you are behind some state tracking firewall… that would be a killer feature for IPv6 🙂

    The “discovery” of other users still seems like a drag, could this be somehow fixed by using Webfinger or similar technologies?

  3. checkout sharefest.me a webrtc file sharing site.
    easy p2p file transfer with no sign-in that support the big swarm many-to-many use case like bit-torrent.

  4. Do you accept cryptocurrency, for support of you spending more time with this than with other priorities? If not:

    People start flooding this person with EUR, USD, CNY etc donations. Then we may actually have something working and not some(yet again) centralised crap in the future.

  5. I have been working on a serverless WebRTC based P2P filesharing network since a year ago (ShareIt!), and seems we get the same conclusion 🙂 Currently I’m using anonimous XMPP public servers (the same as the IM example you propose, but automatically :-P) and a PubNub backend server, and I’m thinking the future would be Push API. Do you know if the SDP last enough time to be posted on a forum or Tweet? I have asked for it and everybody told me it just last for some minutes before it expires… Any idea here?

    • Hey how are you using xmpp servers fro signaling.How are you sending SDP over xmpp?
      I have got stuck at this problem. Any help will be welcome to this newbie. 🙂

  6. I modified the code to include video chat. The problem that i get is, in the remote peer that always accepts the initial offer (Clicking the Join button) i’m able to see videos of both peers. But in the peer that creates the inital offer (Clicking the Create button) i’m only able to see the local video.
    getRemoteStream on that particular peerconnection object returns a MediaStream with time 0

    >>> pc1.getRemoteStreams()
    [MediaStream { currentTime=0, getAudioTracks=function(), getVideoTracks=function()}]

    What could be the possible reason for this ?

  7. You don’t need to use an ICE server – you can just use
    var pc_config = {“iceServers”:[]};
    as the configuration for calling RTCPeerConnection .

  8. thanks (all of you) for your work and comments on this. i (like many others — whether they know it or not) am very interested in decoupling webrtc from “outside” servers. i am presently working on a chrome extension to share tabs and would love the option of things staying local to the users. in particular, i dont mind my use case being limited to a shared lan, so it seems like STUN would be unnecessary. (imagine the case of popping a tab open on a co-workers computer when they are in the next room over…. the initial exchange part could be done by simply yelling the starting sequence over a cubicle wall?? haha)

    anyway, mostly just letting you know thanks and i will be watching this thread.

  9. This is a cool and worthy goal, Chris, but honestly the title of the project and post is a bit misleading. You’re really just using a different signaling server here — the IM chat or whatever else you’re using to transfer the SDP offers and answers. Sure, you could theoretically jot those down on a piece of paper and walk them to the person you’re trying to communicate with, in which case your body would be the signaling server, but in practice by the time you did that the NAT port mappings would likely have timed out unless the person is sitting in the next room.

    Getting rid of STUN also would be a real trick — you not only need your public IP, you need the public port mapping of the port you’re binding to. If it were just the public IP there are some nice tricks, but the port’s where it gets hairy and likely infeasible.

    That said, who knows? Would be sweet and good luck!

  10. If the desire is to add WebRTC to an already existing web application that is maintaining session information with regards to the users that are currently using the system, is there still a need for the signaling server or can the web app itself be used as long as the WebRTC offer is stored against the user when they log into the application. Apart from storing the offer (and the STUN/TURN servers if required), what else would be required from the signaling server? The flow would be the agent logs in, an offer is created and stored against the user. If they want to communicate they can raise their hand. Another user logs in (and their offer is stored). They see the raised hand and initiate the process to initiate a WebRTC communication. What additional signaling would be required?

  11. I am working on this too and I managed to set up a connection between two different browser tabs. But I keep failing when I try to make the app non-local. Any idea which IP’s I need to modify in the SDP or IceCandidate to be able to execute non-locally?

  12. It’s 6 months later, but I just came across this while browsing for things, and thought I’d throw my twopenny in. (I’ve been writing peer-to-peer signalling systems and WebRTC code.)

    The need for a central signalling server isn’t an accident, and it’s not something that needs to be ‘circumvented’. It IS annoying and inconvenient at the moment, but that’s mostly because all our hosting providers are stuck in a PHP mentality. The servers need to go up a level to allow websockets, or at the very least long-poll.

    The basic issue is that you can’t allow ‘random open’ peer-to-peer connections because it’s insanely insecure for arbitrary web pages that get to slide past the firewall to open TCP ports to whatever is on your local network. (“bigbooty.com accessing admin:admin@…”) That turns every web page into a trojan virus, and that would be BAD.

    Hence the ‘handshake’ process, mediated by a trusted coordinator. If you don’t have someone willing to introduce you, perhaps you’re not meant to talk. 🙂

    The nice thing is that the signalling method is undefined. So long as the tokens get exchanged somehow, it doesn’t matter exactly how. Once you get a connection to _any_ peer, (via websockets or comet or REST polling or email or carrier pigeon) they can be the mediator for future connections to other peers.

    I have even managed to write a REST-polled ‘relay’ that acts as a temporary ‘bulletin board’ for clients attempting to make a peer connection. It take a long time (30 seconds to a minute) to establish the initial connection though. WebSockets and a node.js server are far superior though, and are how it’s meant to work. All the HTML5 technologies are supposed to be complimentary.

  13. Pingback: Chris Ball » Serverless WebRTC, continued

  14. Pingback: Serverless WebRTC using QR codes | Franklin Ta

  15. While I agree re the insanity of allowing arbitrary addresses to connect, there are cases in which the white-list is known. Like in a closed community behind a firewall.

    I’d like very much to see an example implementation example reflecting such a case. And yes, PHP applies here.

  16. very interesting post. After 2 years, any progress on audio and video part?

    from a central signalling to p2p, it is not technology, it is about people’s habit and mindset, the way of dialog/communication.

    I am very interested on P2P/Webrtc framework, and I will write something in couple of weeks

  17. Hello Chris,
    This is quiet an interesting idea. I am playing around with your implementation. I have one question if you could please enlighten me.
    If i save the offer and answers in local storage. Can i use them after page refresh?

    • Oh, that’s a good question. I think probably not — the NAT router opens up a port for you and it’s not going to stay open forever. I haven’t experimented to see how long it might work for, but I’d guess it’s limited by the routers involved, not any WebRTC software.


Leave a Reply

Your email address will not be published. Required fields are marked *