Using the Permissions API to Detect How Often Users Allow or Deny Camera Access

9 May update: we've updated the TTA & Permission API numbers to reflect the same 100k getUserMedia() calls dataset. The updated numbers result in the same conclusions.


While building our new getUserMedia() Logs section two questions came up:

  1. How often is the permissions dialog shown in Chrome and Firefox?
  2. When it IS shown, how often do users deny/allow access or dismiss it altogether?

To calculate the above stats we knew exactly how many of the getUserMedia() attempts were denied or allowed but we did not know whether the response was the result of human interaction with the permissions dialog or an automatic response as a result of a previously given persistent permission.

The browsers (Chrome & Firefox) did not give us any obvious information so we decided to use the time passed between calling getUserMedia() and receiving a success or failure promise. We called this interval Time To Action.

Using getUserMedia() response times to detect whether or not the permission dialog is shown

With this measurement in place, the next question was where do automatic responses end and manual interactions start? On a scale from 0 ms to 20.000 ms where do we draw the line in the sand between them?

Our first attempt at setting a limit was pretty simple: in our testing 1000ms was about the fastest we could interact with the permission dialog so we decided to consider everything below 1000ms  an automatic response based on a previous allow/deny permission and consider everything above... a manual human interaction with the Chrome/Firefox permission dialog.

But the data did not suggest such a limit existed:

This chart plots the time it takes for a getUserMedia()call to return any kind of response.

When plotting the response times of our last 100k getUserMedia() calls there's no obvious line in the sand. We hoped to see a big dip between 200-300ms (where we imagined automatic responses would end) and 1.2-1.5 seconds (where we expected manual interactions to start) but no such dip exists.

Venturing to use a 1000ms limit to distinguish between who gets the prompt and who doesn't results in the following conclusions when using the data in the graph above.

Chrome:

  • 67% of the responses are under 1000ms => they're not getting the prompt.
  • 33% of the responses are above 1000ms => they're getting the prompt.

Firefox:

  • 35% of the responses are under 1000ms => they're not getting the prompt.
  • 65% of the responses are above 1000ms => they're getting the prompt.

As we'll see these percentages are relatively far from the real ones.

The data does confirm the differences between Chrome's and Firefox's respective permission persistence. Chrome's permissions are persistent while Firefox's Allow permission is not persistent by default. This is why Firefox responses are heavily distributed more to the right with about 50% requests taking more than 2 seconds to resolve.

Permission Chrome Firefox
Allow persistent not persistent
Deny persistent temporarily persistent
Dismiss not persistent you can't dismiss in Firefox

The Permissions API

Uncertain that we could present reliable numbers we've looked for other solutions, that's when we discovered the Permissions API.

As opposed to the binary info (prompt/no prompt) we tried to deduct above, the Permissions API gives us accurate granular information about the state of both the microphone and camera permissions BEFORE calling getUserMedia().

More exactly it gives us 3 values for each permission (camera/microphone):

  • prompt: the browser has no record of a persistent permission having been given so the user will be shown a permissions dialogue
  • granted: the user has previously given permission and the answer is persistent
  • denied: the user has previously denied permission and the answer is persistent

On top of that the camera and microphone parts of the Permissions API have been implemented since Chrome 64 (January 2018) so they are now widely supported by the Chrome user base.

Here's some sample code to run in the Chrome console:

navigator.permissions.query({name:'camera'}).then(function(result) {
    alert(result.state);
    if (result.state === 'granted') {
        //permission has already been granted, no prompt is shown
    } else if (result.state === 'prompt') {
       //there's no peristent permission registered, will be showing the prompt
    } else if (result.state === 'denied') {
       //permission has been denied
    }
});

Looking at the Permissions API data from Chrome from the same 100k getUserMedia() calls we can confidently say that:

  • only about 21% of the getUserMedia() calls on Chrome on our platform resulted in a prompt. This number will strongly depend on your use case. Our platform does video recordings and our clients' use case favors repeat video recordings during a session (and thus repeat getUserMedia() calls). End users will only be prompted for permission to access the camera and microphone once - the 1st time they use the recorder - as Chrome's permissions are persistent.
  • of those who did get the permissions prompt 10% denied access or dismissed the dialog (dismissing is possible in Chrome) resulting in a 2.3% percentage across our last 100k getUserMedia() calls. This number is more steady between use cases.

The 21% number we now got on Chrome is very different than the 33% we got by drawing a line in the sand at 1000ms. Big difference! The cause? Camera & microphone initialization times vary a lot as we'll see in a blog post I'm preparing.

Since launching the new getUserMedia() Logs section we've made these stats available to our users. The Pipe platform will show in the account area:

  • how often your Chrome users get to see the prompt
  • how often they dismiss the dialog
  • how often they deny access

Here's an example where in 12.1% of cases the dialog is shown and in 9.15% of those cases the user denies access or dismisses the dialog (1.1% out of 26799):

getUserMedia() stats shown in the Pipe account area