Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


ScalaJS WebRTC library. Based loosly on SimpleWebRTC.

Getting Started

In order to get webrtc working localy, you will at minimum need a way to signal the other peers. For more information on webrtc read

I provide you with a singaling server implementation in my project here:

Getting webrtc to work in the wild will require that you setup a TURN server. Again more info on this is provided in the above link.


In most cases you will already have a server setup that uses a websocket for your application and some defined Model. This implementation attempts to provide you with the glue required to translating your Model types to the ones required under the hood.

There are of course other options then a websocket and so the only requirment for your singaling setup is to provide an implementation for

trait ModelTransformPeerSignaler[T] extends PeerSignaler{
    val localPeer:PeerInfo

    var receivers = js.Array[(Signaling) => Unit]()

    def toPeerSignaling(model:T):Signaling
    def fromPeerSignaling(s:Signaling):T

    def receive(s:Signaling) = receivers.foreach(_.apply(s))
    override def send(s:Signaling):Unit = sendModel(fromPeerSignaling(s))

    // override ME
    def sendModel(model:T):Unit


Here is a full example that uses the websocket server from my other projects code.

class WebSocketSignaler extends Peer.ModelTransformPeerSignaler[m.RTCSignal]{
  val id = (Math.random() * 1000).toInt.toString
  val `type` = "video"

  val localPeer = PeerInfo(id, `type`)

  var ws = new dom.WebSocket(s"ws://${dom.document.location.hostname}:${dom.document.location.port}/ws/${id}")
  ws.onmessage = { x: MessageEvent =>
    println(s"WS onmessage ${}")
    val msg =[m.Model](
  ws.onopen = { x: Event =>
    println("WS connection connected")
  ws.onerror = { x: ErrorEvent =>
    println("some error has occured " + x.message)
  ws.onclose = { x: CloseEvent =>
    println("WS connection CLOSED !!")

  implicit def modelToPeer(p:m.Signaling.PeerInfo):Peer.PeerInfo = Peer.PeerInfo(, p.`type`)
  implicit def peerToModel(p:Peer.PeerInfo):m.Signaling.PeerInfo = m.Signaling.PeerInfo(, p.`type`)

  override def toPeerSignaling(model:m.RTCSignal):Peer.Signaling = model match{
    case m.Signaling.Room(r, l, name, members) =>
      import js.JSConverters._
      val peers = => Peer.PeerInfo(id =, `type` = p.`type`))
      Peer.Room(r, l, name, peers.toJSArray)
    case m.Signaling.Offer(r, l, offer) =>
      Peer.Offer(r, l, RTCSessionDescription(offer.`type`, offer.sdp))
    case m.Signaling.Candidate(r, l, c) =>
      Peer.Candidate(r, l, RTCIceCandidate(c.candidate, c.sdpMLineIndex, c.sdpMid))
    case m.Signaling.Answer(r, l, answer) =>
      Peer.Answer(r, l, RTCSessionDescription(answer.`type`, answer.sdp))
    case m.Signaling.Error(r, l, error) =>
      Peer.Error(r, l, error)
    case _ =>
      Peer.Error(Peer.PeerInfo("", ""), Peer.PeerInfo("", ""), "Unknown signaling type")
  override def fromPeerSignaling(s:Peer.Signaling):m.RTCSignal = s match{
    case Peer.Join(r, l, name) =>
      m.Signaling.Join(r, l, name)
    case Peer.Room(r, l, name, members) =>
      val peers = => m.Signaling.PeerInfo(id =, `type` = p.`type`))
      m.Signaling.Room(r, l, name, peers.toSet)
    case Peer.Offer(r, l, offer) =>
      m.Signaling.Offer(r, l, m.Signaling.RTCSessionDescription(offer.`type`, offer.sdp))
    case Peer.Candidate(r, l, c) =>
      m.Signaling.Candidate(r, l, m.Signaling.RTCIceCandidate(c.candidate, c.sdpMLineIndex, c.sdpMid))
    case Peer.Answer(r, l, answer) =>
      m.Signaling.Answer(r, l, m.Signaling.RTCSessionDescription(answer.`type`, answer.sdp))
    case Peer.Error(r, l, error) =>
      m.Signaling.Error(r, l, error)
    case _ =>
      m.Signaling.Error(m.Signaling.PeerInfo("", ""), m.Signaling.PeerInfo("", ""), "Unknown signaling type")

  override def sendModel(s:m.RTCSignal) = ws.send(upickle.default.write(s))

// ... Then you declare your WebRTC object like this.

 val signaler = new WebSocketSignaler
 val props = WebRTC.Props(
    rtcConfiguration = RTCConfiguration(
      iceServers = js.Array[RTCIceServer](RTCIceServer(url = ""))

  val webRTC = new SimpleWebRTC(signaler, props)

Now your only requiremnt on the server is that messages from a local peer get routed to their remote peer.