WebRTC conference sample with custom elements

I created a WebRTC conference example with custom elements (often called webcomponents) for rendering. The behavior is similar to the simple example from the W3C WebRTC spec.

My main goal was to NOT have two applications (one in Elm, one in JS) that need to synchronize state, but instead keep all state inside the Elm app, use ports to mutate the opaque JSON values, and display video streams using custom elements.

When comparing it to a solution without custom elements, it is obvious that I was able to make do with fewer ports.

I definitely liked that it allowed me to react to DOM events emitted by the custom elements instead of needing to use ports. Which would either listen to many more events at a time than actually needed, or where I had to create many different subscriptions depending on the app state.

Instead I could write it like this inside the view functions to e.g. wait until the custom elment creates a new RTCPeerConnection object.

-- in ./src/Active/View.elm
viewPending : Model.PendingUser -> Html Msg
viewPending user =
    H.node "webrtc-media"
        [ ...
        , onCustomEvent "new-peer-connection" (Msg.UserUpdated user.id) Msg.peerConnectionDecoder
        ]
        []

And then use a port to add the local media stream to it, and create an SDP offer.

-- in ./src/Active/Update.elm
updatePendingUser : Msg.UserId -> Model.Stream -> Msg.Updated -> Model.PendingUser -> ( User, Cmd msg )
updatePendingUser ownId localStream msg user =
    case msg of
        Msg.NewPeerConnection pc ->
            ( Model.User ...
            , if ownId < user.id then
                -- executes `initiateSdpOffer/3 in ./src/index.js
                Ports.Out.createSdpOfferFor user.id pc localStream
              else
                .. -- in this case we would expect to receive an SDP offer from the other peer
            )
        ...

This offer will then be sent over the signaling server to the other browser and start the negotation process.

Elm code and a simple signaling server are available on github.

I would also love to hear feedback, especially if it makes sense in your point of view to rely on custom element communication or not, or if someone noticed problems and would think ports would offer a cleaner approach.

5 Likes

Do you need ports at all? Would it not be better to only use one or more custom elements?

I hadn’t thought about that, but I would think it is possible.

You could create a web-socket custom element for the server communication. A con would be that you need to store the queue of all outgoing messages in elm and also have to drain the queue using events from the element. Similar to the two-way handshake of tcp. A pro would be that we could handle failing or rejected messages.

Adding ICE candidates and SDP using attributes should work well, right now the user element is always rendered anyway. But then the custom element would own the peer connection state (and storing it in Elm too would not make sense). Definitely makes sense for a reliable solution, but I wanted Elm to hold all state in this example.

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.