WFWD

Deep dive into Adapter.js

Introduction

In the beginning of 2018, Tsahi Levent Levi and Philipp Hancke wrote an article about What is WebRTC adapter.js and Why do we need it.

This great article ends up with a question if one day we will be able to no longer rely on adapter.js.

More than 3 years later and without keeping suspense, I can definitively say that I still need it. That's why I decided to spend some time looking into that library to understand why.

This article has some common parts with the original one but then try to explain the library in details.

The problem

In fact, problem looks like the same but just x10 more!

As a developer, I used to develop my Web application in my favorite browser and then to test in others to check that there are no differences graphically and functionally. Hopefully, we have a lot of help today with the plethora of JavaScript and CSS frameworks.

When developing WebRTC, it is a bit different because you need at least 2 instances of your application running at the same times for having a call. It could be 2 instances of the same browser or 2 different browsers. Things can be more complex if you are developing a conference server...

So when something works between Chrome and Firefox, that doesn't want to say that it will work between Chrome and Safari or between Safari and Firefox. And depending on which browser initiates the call, result can be different... Then version of the browser can impact the result. Last but not least, mobile and not compliant browsers are other cases to take into account.

That end-to-end development's matrix is something to keep in mind to be sure to deliver the best application but instead of fearing it when testing a new device, we as a developer can rely on the experience gained by the first developers

The promise

adapter.js is a shim to insulate apps from spec changes and prefix differences in WebRTC. The prefix differences are mostly gone these days but differences in behavior between browsers remain.

Goal is clearly defined here: Adapter hides some differences in the way browsers handle WebRTC. Behavior could be different in Chrome than in Firefox or than in Safari.

If you are not convinced, try to implement a simple peer to peer call and try to deal with the connectionState event for example...

History

Remember the origin

The very first commit into the repository has been written in Sept 20th 2013. At that time, goal was not to write a library to handle differences between Chrome and Firefox but rather to offer some great demos and samples to developers in order for us to understand how to use the WebRTC API.

The initial README explained it clearly:

Readme: « All of these examples use adapter.js, a shim to insulate apps from spec changes and prefix differences. In fact, the standards and protocols used for WebRTC implementations are highly stable, and there are only a few prefixed names. »

:-) :-) :-) If we knew the whole story at this time then...

This very first version had only about 154 lines of code and was dedicated to Chrome and Firefox only. As explained, goal was to have working demos and so to centralize the WebRTC stuff management in one file: adapter.js in order to not replicate that part in each demo.

Note: I think this is still a good practice today to keep the management of the WebRTC stuff in a common part somewhere in your code instead of having for example call to getUserMedia API x times in x different files...

Road to adapter

During the first months and up to the middle of 2014, Sam Dutton from Google was the main contributor then helped by Justin Uberti from Google too. Goal was more to build demos and then AppRTC a first real full demo app that anyone can use from his browser to test with a friend. There was not a lot of commits to adapter.js. Focus was on demos.

During that second step (from mid 2014 to mid 2015), a new tool called TestRTC has been designed to let people test their WebRTC environment such as their microphones and cameras as well as the network compatibility. Main contributor was Christoffer Jansson. Still, a lot of contributions to AppRTC too.

Finally, starting February 2015, the repository was purged from the demos in order to keep only the library adapter.js. In consequence, the number of commits decreased a lot. Philipp Hancke started to become the main contributor to the library. He still continues today, hopefully for many of us.

Contributions to adapter.js

Here is the list of the major versions and changes.

Date Version Interesting changes
May 2021 v8.0.0 Remove Edge legacy code
Dec 2018 v7.0.0 ES6 compliant
Nov 2017 v6.0.0 Adapt to Chrome M61
Aug 2017 v5.0.0
June 2017 v4.0.0
Jan 2017 v3.0.0 Get stats rework
Aug 2016 v2.0.0 Fix Edge issues
Feb 2016 v1.0.0 Separate shim per browser
June 2015 v0.1.0 First public version

Today

As this time of writing, there are 76 contributors to the repository but for 2021, only 3 are still alive for the moment. The size of the source-code is about 150ko (when not optimized).

The repository have more than 2.8k stars which is very high and 166 developers are watchers.

If you have a look to the number of issues opened, the number is very low (less than 10).

Regarding the number of downloads coming from the NPM platform, adapter.js is more and more used in development moving from about 90k downloads per week last year to about 140k-150k today.

But as noticed by Philipp Hancke in that issue, 20% of the current downloads come from versions 6.0 or older which have been released more than 3 years ago... Not sure of the result!

Using adapter.js

Back to development, in order to use adapter.js, we need to know what is a shim and so how this library works.

A shim

adapter.js is a shim: A library that modifies the current JavaScript API proposed by the navigator and accessible from your application.

Here is a more commonly used definition of a shim extracted from Wikipedia

In computer programming, a shim is a small library that transparently intercepts an API, changing the parameters passed, handling the operation itself, or redirecting the operation elsewhere. Shims typically come about when the behavior of an API changes, thereby causing compatibility issues for older applications that still rely on the older functionality. In these cases, the older API can still be supported by a thin compatibility layer on top of the newer code. Shims can also be used to run programs on different software platforms than they were developed for.

So in fact, from the application point of view, the WebRTC implementation which is the API to call or the event to listen is implemented the same way in Chrome, Firefox, and Safari without having to take care of the differences. The library makes the glue for you.

This is the deal!

Different kind of shims

adapter.js don't have a monolithic architecture but rather relies on the browser in which the library is loaded to activate only the shims needed by that environment.

When the library starts, 2 kinds of shims are loaded: the common shims that have a common implementation in all browsers and the specific shims associated to the browser.

As seen previously, a shim modifies the way an API works and each shim has a dedicated role and acts independently of the others.

Mainly, In adapter.js, shims play several roles:

  • Redirect a depreciated API: For example if the application tries to call the API navigator.getUserMedia, the shim catches that request and instead calls the API navigator.mediaDevices.getUserMedia.
  • Define an API (property, method) not yet supported: For example when adding the property connectionState to the prototype of RTCPeerConnection. This is done using the method Object.defineProperty(...).
  • Augment a WebRTC object: When results returned by the browser is not aligned with the W3C specification: For example to homogenize the result of the API getStats.
  • Ease developer's life: Such as when parsing ICE parameters from the candidate string received and offered a pretty JSON object.

Others kinds of shims exist that change the behavior or adapt it to avoid any issues when mixing browsers and versions in the same call.

Looking into adapter.js, we can see that there is still about 15 shims that are loaded for each browser.

Browser Common shims Specific shims Total Shims loaded
Chrome 6 9 15
Firefox 5 11 16
Safari 5 8 13

And what about Edge and other browsers ?

adapter.js has a little part of code that deals with the browser recognition. Based on some WebRTC API found, adapter.js is able to detect which browser is used and so to load the shims accordingly.

All Chromium based browsers are recognized as Chrome and so use the same shims as Chrome. This is the case for Edge and Opera for example.

For all other browsers not based on Chromium or Firefox, there is no support of adapter.js, which means that if you load the library from these browsers, no shims are executed. It becomes more and more difficult to find such a desktop browser. But on Android on mobile devices, they are specific browsers such as UC Browser that has its own dedicated engine and that is not WebRTC compliant. For those, adapter.js can do nothing.

In the code

Using adapter.js is extremely simple because you have just to load the library and that's all.

Once loaded, shims are executed and so API are "ready".

If you don't have a part of code that detect the underlying engine, you can use some convenient properties such as adapter.browserDetails.browser and adapter.browserDetails.version that gives the WebRTC engine name and the associated version of the browser.

What is not adapter.js ?

As seen, adapter.js homogenizes the WebRTC API in term of API naming and in term of behavior. But adapter.js is not a library that abstracts WebRTC. As a developer, you still have to understand WebRTC concepts and know how to set-up a WebRTC call, etc...

So WebRTC skills are still needed. But you don't need to know how the library works or what shims adapter.js brings. Just to use the standardized WebRTC EcmaScript APIs.

Recommendation

If I want to integrate WebRTC in my application or if I start a new WebRTC project today, do I still need to use adapter.js ?

Some developers want to limit the use of third party libraries whereas other add without counting external libraries in their source code. But when dealing with WebRTC, I think it is a good choice to add adapter.js and to rely on it.

It was like using JQuery some years ago without having to deal with browsers differences in term of methods to access the DOM, etc... And so, to avoid having specific code in your application for each browser.

So, I definitively think that this library help you to avoid some common pitfalls which means when you use adapter.js you less have to know if an API or an event is managed by a browser. The library should close the gap in most of the cases. For example, instead of trying to understand why the connectionState event is not fired in Firefox which is because this event is still not implemented, you will not realize that a 'fake' event is fired when using adapter.js and so that it works transparently. A lot of time saved!

So as a developer, you are free to choose to use it or not, but if you are a beginner in WebRTC, my recommendation is to don't ask yourself, use it. Each time I start a new WebRTC project, one of the first external libraries I add to my package.json is adapter.js.

It is for sure possible to develop without using it but it is up to you to know the pitfalls and to overcome them.


Olivier Anguenot

Written by Olivier Anguenot who lives and works in Strasbourg, France building WebRTC stuff.