Generic Sensor API

W3C Candidate Recommendation Draft,

More details about this document
This version:
https://www.w3.org/TR/2024/CRD-generic-sensor-20240222/
Latest published version:
https://www.w3.org/TR/generic-sensor/
Editor's Draft:
https://w3c.github.io/sensors/
Previous Versions:
History:
https://www.w3.org/standards/history/generic-sensor/
Feedback:
public-device-apis@w3.org with subject line “[generic-sensor] … message topic …” (archives)
GitHub
Implementation Report:
https://www.w3.org/wiki/DAS/Implementations
Editor:
Rick Waldron (Invited Expert, formerly on behalf of Bocoup and JS Foundation)
Former Editors:
Mikhail Pozdnyakov (Intel Corporation)
Alexander Shalamov (Intel Corporation)
Tobie Langel (Codespeaks, formerly on behalf of Intel Corporation)
Other:
Test suite, latest version history, previous version history

Abstract

This specification defines a framework for exposing sensor data to the Open Web Platform in a consistent way. It does so by defining a blueprint for writing specifications of concrete sensors along with an abstract Sensor interface that can be extended to accommodate different sensor types.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document was published by the Devices and Sensors Working Group as a Candidate Recommendation Draft using the Recommendation track. This document is intended to become a W3C Recommendation.

If you wish to make comments regarding this document, please send them to public-device-apis@w3.org (subscribe, archives). When sending e-mail, please put the text “generic-sensor” in the subject, preferably like this: “[generic-sensor] …summary of comment…”. All comments are welcome.

Publication as a Candidate Recommendation does not imply endorsement by W3C and its Members. A Candidate Recommendation Draft integrates changes from the previous Candidate Recommendation that the Working Group intends to include in a subsequent Candidate Recommendation Snapshot. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

The entrance criteria for this document to enter the Proposed Recommendation stage is to have a minimum of two independent and interoperable user agents that implementation all the features of this specification, which will be determined by passing the user agent tests defined in the test suite developed by the Working Group. The Working Group will prepare an implementation report to track progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

Further implementation experience is being gathered for the permission request algorithm and specification clarifications informed by this experience are being discussed in GitHub issue #397. The group does not expect to advance this specification beyond CR until [PERMISSIONS-REQUEST] moves beyond incubation. This document is maintained and updated at any time. Some parts of this document are work in progress.

1. Introduction

Increasingly, sensor data is used in application development to enable new use cases such as geolocation, counting steps or head-tracking. This is especially true on mobile devices where new sensors are added regularly.

Exposing sensor data to the Web has so far been both slow-paced and ad-hoc. Few sensors are already exposed to the Web. When they are, it is often in ways that limit their possible use cases (for example by exposing abstractions that are too high-level and which don’t perform well enough). APIs also vary greatly from one sensor to the next which increases the cognitive burden of Web application developers and slows development.

The goal of the Generic Sensor API is to promote consistency across sensor APIs, enable advanced use cases thanks to performant low-level APIs, and increase the pace at which new sensors can be exposed to the Web by simplifying the specification and implementation processes.

A comprehensive list of concrete sensors that are based on Generic Sensor API, applicable use cases, and code examples can be found in [GENERIC-SENSOR-USECASES] and [MOTION-SENSORS] explainer documents.

2. Scope

This section is non-normative.

The scope of this specification is currently limited to specifying primitives which enable exposing data from device sensors.

Exposing remote sensors or sensors found on personal area networks (e.g. Bluetooth) is out of scope. As work in these areas mature, it is possible that common, lower-level primitives be found, in which case this specification will be updated accordingly. This should have little to no effects on implementations, however.

This specification also does not currently expose a sensor discovery API. This is because the limited number of sensors currently available to user agents does not warrant such an API. Using feature detection, such as described in § 3 A note on Feature Detection of Hardware Features, is good enough for now. A subsequent version of this specification might specify such an API, and the current API has been designed with this in mind.

3. A note on Feature Detection of Hardware Features

This section is non-normative.

Feature detection is an established Web development best practice. Resources on the topic are plentiful on and offline and the purpose of this section is not to discuss it further, but rather to put it in the context of detecting hardware-dependent features.

Consider the below feature detection examples:

This simple example illustrates how to check whether the User Agent exposes an interface for a particular sensor type. To handle errors in a robust manner, please refer to this example.
if (typeof Gyroscope === "function") {
    // run in circles...
}

if ("ProximitySensor" in window) {
    // watch out!
}

if (window.AmbientLightSensor) {
    // go dark...
}

// etc.

All of these tell you something about the presence and possible characteristics of an API. They do not tell you anything, however, about whether that API is actually connected to a real hardware sensor, whether that sensor works, if its still connected, or even whether the user is going to allow you to access it. Note you can check the latter using the Permissions API [PERMISSIONS].

In an ideal world, information about the underlying status would be available upfront. The problem with this is twofold. First, getting this information out of the hardware is costly, in both performance and battery time, and would sit in the critical path. Secondly, the status of the underlying hardware can evolve over time. The user can revoke permission, the connection to the sensor be severed, the operating system may decide to limit sensor usage below a certain battery threshold, etc.

Therefore, an effective strategy is to combine feature detection, which checks whether an API for the sought-after sensor actually exists, and defensive programming which includes:

  1. checking for error thrown when instantiating a Sensor object,

  2. listening to errors emitted by it,

  3. handling all of the above graciously so that the user’s experience is enhanced by the possible usage of a sensor, not degraded by its absence.

The following snippet illustrates how an Accelerometer sensor can be created in a robust manner. Web application may choose different options for error handling, for example, show notification, choose different sensor type or fallback to other API.
let accelerometer = null;
try {
    accelerometer = new Accelerometer({ frequency: 10 });
    accelerometer.addEventListener('error', event => {
        // Handle runtime errors.
        if (event.error.name === 'NotAllowedError') {
            console.log('Permission to access sensor was denied.');
        } else if (event.error.name === 'NotReadableError' ) {
            console.log('Cannot connect to the sensor.');
        }
    });
    accelerometer.addEventListener('reading', () => reloadOnShake(accelerometer));
    accelerometer.start();
} catch (error) {
    // Handle construction errors.
    if (error.name === 'SecurityError') {
        console.log('Sensor construction was blocked by the Permissions Policy.');
    } else if (error.name === 'ReferenceError') {
        console.log('Sensor is not supported by the User Agent.');
    } else {
        throw error;
    }
}

4. Security and privacy considerations

The judgement on how to communicate to the user the known threats is up to the implementer. The implementation of the mitigations is mandatory, however.

Sensor readings are sensitive data and could become a subject of various attacks from malicious Web pages. Before discussing the mitigation strategies we briefly enumerate the main types of the sensor's privacy and security threats. The [MOBILESENSORS] categorizes main threats into location tracking, eavesdropping, keystroke monitoring, device fingerprinting, and user identification. This categorization is a good fit for this specification.

The risk of successful attack can increase when sensors are used with each other, in combination with other functionality, or when used over time, specifically with the risk of correlation of data and user identification through fingerprinting. Web application developers using these JavaScript APIs should consider how this information might be correlated with other information and the privacy risks that might be created. The potential risks of collection of such data over a longer period of time should also be considered.

Variations in sensor readings as well as event firing rates offer the possibility of fingerprinting to identify users. User agents may reduce the risk by limiting event rates available to web application developers.

Minimizing the accuracy of a sensor’s readout generally decreases the risk of fingerprinting. User agents should not provide unnecessarily verbose readouts of sensors data. Each sensor type should be assessed individually.

If the same JavaScript code using the API can be used simultaneously in different window contexts on the same device it may be possible for that code to correlate the user across those two contexts, creating unanticipated tracking mechanisms.

User agents should consider providing the user an indication of when the sensor is used and allowing the user to disable it. Additionally, user agents may consider allowing the user to verify past and current sensor use patterns.

Web application developers that use sensors should perform a privacy impact assessment of their application taking all aspects of their application into consideration.

Ability to detect a full working set of sensors on a device can form an identifier and could be used for fingerprinting.

A combination of selected sensors can potentially be used to form an out of band communication channel between devices.

Sensors can potentially be used in cross-device linking and tracking of a user.

4.1. Types of privacy and security threats

This section is non-normative.

4.1.1. Location Tracking

Under this type of threat, the attacks use sensor readings to locate the device without using GPS or any other location sensors. For example, accelerometer data can be used to infer the location of smartphones by using statistical models to obtain estimated trajectory, then map matching algorithms can be used to obtain predicted location points (within a 200-m radius)[MOBILESENSORS].

4.1.2. Eavesdropping

Recovering speech from gyroscope readings is an example of eavesdropping attack. See [GYROSPEECHRECOGNITION].

4.1.3. Keystroke Monitoring

Many user inputs can be inferred from sensor readings, this includes a wide range of attacks on user PINs, passwords, and lock patterns (and even touch actions such as click, scroll, and zoom) using motion sensors. These attacks normally train a machine learning algorithm to discover such information about the users. See [STEALINGPINSVIASENSORS].

4.1.4. Device Fingerprinting

Sensors can provide information that can uniquely identify the device using those sensors. Every concrete sensor model has minor manufacturing imperfections and differences that will be unique for this model. These manufacturing variations and imperfections can be used to fingerprint the device [ACCELPRINT] [MOBILESENSORS].

4.1.5. User Identifying

Sensor readings can be used to identify the user, for example via inferring individual walking patterns from smartphone or wearable device motion sensors' data.

4.2. Mitigation Strategies

This section is non-normative.

This section gives a high-level presentation of some of the mitigation strategies specified in the normative sections of this specification.

4.2.1. Secure Context

Sensor readings are explicitly flagged by the Secure Contexts specification [POWERFUL-FEATURES] as a high-value target for network attackers. Thus all interfaces defined by this specification or extension specifications are only available within a secure context.

4.2.2. Permissions Policy

To avoid the privacy risk of sharing sensor readings with contexts unfamiliar to the user, sensor readings are only available for the documents which are allowed to use the policy-controlled features for the given sensor type. See [PERMISSIONS-POLICY] for more details.

4.2.3. Focused Area

Sensor readings are only available for an active document if the focus and origin check on it returns true.

This is done in order to mitigate the risk of a skimming attack against the navigable containing an element which has gained focus, for example when the user carries out an in-game purchase using a third party payment service from within an iframe.

4.2.4. Visibility State

Sensor readings are only available for the active documents whose visibility state is "visible".

4.2.5. Permissions API

Access to sensor readings are controlled by the Permissions API [PERMISSIONS].

4.3. Mitigation strategies applied on a case by case basis

Each sensor type will need to be assessed individually, taking into account the use cases it enables and its particular threat profile. While some of the below mitigation strategies are effective for certain sensors, they might also hinder or altogether prevent certain use cases.

Note: These mitigation strategies can be applied constantly or temporarily, for example when the user is carrying out specific actions, when other APIs which are known to amplify the level of the threat are in use, etc.

4.3.1. Limit maximum sampling frequency

User agents and extension specifications may mitigate certain threats by defining a sensor type's maximum sampling frequency.

What upper limit to choose depends on the sensor type, the kind of threats the user agent is trying to protect against, the expected resources of the attacker, etc.

Limiting the maximum sampling frequency prevents use cases which rely on low latency or high data density.

4.3.2. Stop the sensor altogether

This is obviously a last-resort solution, but it can be extremely effective if it’s temporal, for example to prevent password skimming attempts when the user is entering credentials on a different origin ([rfc6454]) or in a different application.

4.3.3. Limit number of delivered readings

An alternative to limiting the maximum sampling frequency is to limit the number of sensor readings delivered to Web application developer, regardless of the sampling frequency. This allows use cases which have low latency requirement to increase the sampling frequency without increasing the amount of data provided.

Discarding intermediary readings prevents certain use cases, such as those relying on certain kinds of filters.

4.3.4. Reduce accuracy

Reducing the accuracy of sensor readings or sensor reading timestamps might also help mitigate certain threats, thus user agents should not provide unnecessarily verbose readouts of sensors data.

Implementations of concrete sensors may define a threshold check algorithm so that new readings that do not differ enough from the latest readings are discarded.

Implementations of concrete sensors may define a reading quantization algorithm to reduce the accuracy of the sensor readings received from a device sensor.

Note: These two mitigation measures often complement each other. An implementation that only executes the threshold check algorithm might expose readings that are too precise, while an implementation that only rounds readings up may provide attackers with information about more precise readings when raw readings are rounded to different values.

Note: Inaccuracies will further increase for operations carried out on the sensor readings, or time deltas calculated from the timestamps. So, this mitigation strategy can affect certain use cases.

Note: While adding random bias to sensor readings has similar effects, it shouldn’t be used in practice as it is easy to filter out the added noise.

4.3.5. Keep the user informed about API use

User agents may choose to keep the user informed about current and past use of the API.

Note: This does not imply keeping a log of the actual sensor readings which would have issues of its own.

5. Concepts

5.1. Sensors

The term device sensor refers to a device’s underlying physical sensor instance.

A device sensor measures a physical quantities and provides a corresponding sensor reading which is a source of information about the environment.

Each sensor reading is composed of the values of the physical quantity measured by the device sensor at time tn which is called the reading timestamp.

If the device sensor performs a spatial measurement (e.g. acceleration, angular velocity), it must be resolved in a local coordinate system that represents a reference frame for the device sensor's sensor readings. A device sensor that provides such sensor readings is referred to as spatial sensor.

A spatial sensor can be uniaxial, biaxial, or triaxial, depending on the number of orthogonal axes in which it can perform simultaneous measurements.

Scalar physical quantities (e.g. temperature) do not require a local coordinate system for resolution.

The local coordinate system normally used in a mobile device is a Cartesian coordinate system, which is defined relative to the device’s screen, so that X and Y axes are parallel to the screen dimentions and Z axis is perpendicular to the screen surface.

The term platform sensor refers to platform interfaces, with which the user agent interacts to obtain sensor readings for a single sensor type originated from one or more device sensors.

Platform sensor can be defined by the underlying platform (e.g. in a native sensors framework) or by the user agent, if it has a direct access to device sensor.

From the implementation perspective platform sensor can be treated as a software proxy for the corresponding device sensor. It is possible to have multiple platform sensors simultaneously interacting with the same device sensor if the underlying platform suppports it.

In simple cases, a platform sensor corresponds to a single device sensor, but if the provided sensor readings are a product of sensor fusion performed in software, the platform sensor corresponds to a set of device sensors involved in the sensor fusion process.

Discrepancies between a sensor reading and the corresponding physical quantity being measured are corrected through calibration that can happen at manufacturing time. Some sensors can require dynamic calibration to compensate unknown discrepancies.

Note: Platform sensors created through sensor fusion are sometimes called virtual or synthetic sensors. However, the specification doesn’t make any practical distinction between them.

5.2. Sensor Types

Different sensor types measure different physical quantities such as temperature, air pressure, heart-rate, or luminosity.

For the purpose of this specification we distinguish between high-level and low-level sensor types.

Sensor types which are characterized by their implementation are referred to as low-level sensors. For example a Gyroscope is a low-level sensor type.

Sensors named after their readings, regardless of the implementation, are said to be high-level sensors. For instance, geolocation sensors provide information about the user’s location, but the precise means by which this data is obtained is purposefully left opaque (it could come from a GPS chip, network cell triangulation, wifi networks, etc. or any combination of the above) and depends on various, implementation-specific heuristics. High-level sensors are generally the fruits of applying algorithms to low-level sensors—for example, a pedometer can be built using only the output of a gyroscope—or of sensor fusion.

That said, the distinction between high-level and low-level sensor types is somewhat arbitrary and the line between the two is often blurred. For instance, a barometer, which measures air pressure, would be considered low-level for most common purposes, even though it is the product of the sensor fusion of resistive piezo-electric pressure and temperature sensors. Exposing the sensors that compose it would serve no practical purpose; who cares about the temperature of a piezo-electric sensor? A pressure-altimeter would probably fall in the same category, while a nondescript altimeter—which could get its data from either a barometer or a GPS signal—would clearly be categorized as a high-level sensor type.

Because the distinction is somewhat blurry, extensions to this specification (see § 10 Extensibility) are encouraged to provide domain-specific definitions of high-level and low-level sensors for the given sensor types they are targeting.

Sensor readings from different sensor types can be combined together through a process called sensor fusion. This process provides higher-level or more accurate data (often at the cost of increased latency). For example, the readings of a triaxial magnetometer needs to be combined with the readings of an accelerometer to provide a correct bearing.

Smart sensors and sensor hubs have built-in compute resources which allow them to carry out calibration and sensor fusion at the hardware level, freeing up CPU resources and lowering battery consumption in the process.

Sensor fusion can also be carried out in software if it cannot be performed at the hardware level or if an application-specific fusion algorithm is required.

5.3. Default sensor

The Generic Sensor API is designed to make the most common use cases straightforward while still enabling more complex use cases.

Most of devices deployed today do not carry more than one device sensor providing sensor readings of the same type. The use cases which require a set of similar device sensors are rare and generally limited to specific sensor types, such as multiple accelerometers in 2-in-1 laptops.

The API therefore makes it easy to interact with the device’s default (and often unique) sensor for each type simply by instantiating the corresponding Sensor subclass.

Indeed, without specific information identifying a particular sensor of a given type, the default sensor is chosen by the user agent.

If the underlying platform provides an interface to find the default sensor, the user agent must choose the sensor offered by the platform, otherwise the user agent itself defines which of the sensors present on the device is the default sensor.

Listening to the default accelerometer changes:
let sensor = new Accelerometer({ frequency: 30 });

sensor.onreading = () => { ... }
sensor.start();

Note: Extensions to this specification may choose not to define a default sensor when doing so wouldn’t make sense. For example, it does not make sense to explicitly define a default sensor for geolocation sensor type as the implementation of its interface can use multiple backends.

In cases where multiple device sensors corresponding to the same type may coexist on the same device, specification extension will have to define ways to uniquely identify each one.

For example checking the pressure of the left rear tire:
var sensor = new DirectTirePressureSensor({ position: "rear", side: "left" });
sensor.onreading = _ => console.log(sensor.pressure);
sensor.start();

5.4. Sampling Frequency and Reporting Frequency

For the purpose of this specification, a platform sensor's sampling frequency is defined as a frequency at which a platform sensor obtains sensor readings from the underlying device sensor. The way such sensor readings are obtained is implementation-defined.

The platform sensor's sampling frequency may not correspond to the device sensor's actual sampling rate, which, for the purpose of this specification, is opaque.

Note: System-level APIs for sensor readings and the underlying hardware interface to the sensors themselves may be built for polling or events. For a polling-based device sensor, the platform sensor's sampling frequency would be the rate at which a new reading is requested from the system or hardware. For an event-based device sensor, a platform sensor provides a requested sampling frequency to the system or hardware, and events are generated at that frequency or below. Events may not be generated if the sensor reading has not changed.

A device sensor may provide bounds for the sampling frequency value it can accept from a platform sensor in the form of a minimum sampling frequency and a maximum sampling frequency. A platform sensor's sampling frequency must not be less than the device sensor's minimum sampling frequency or greater than its maximum sampling frequency.

A platform sensor's sampling frequency is determined based on the provided [[frequency]] of the items in its set of activated sensor objects. The calculation is implementation-defined, but the outcome value must lie within the bounds set by the platform sensor's sensor type's minimum and maximum sampling frequencies and its device sensor's minimum and maximum sampling frequencies.

Note: For example, the user agent may estimate the sampling frequency as a Least Common Denominator (LCD) for a set of provided [[frequency]] capped by sampling frequency bounds defined by the underlying platform.

The reporting frequency for a concrete Sensor object is defined as a frequency at which the "reading" event is fired at this object.

A Sensor object cannot access new readings at a higher rate than the user agent obtains them from the underlying platform, therefore the reporting frequency can never exceed a platform sensor's sampling frequency, which in turn can never exceed a device sensor's maximum sampling frequency (when specified).

The reporting frequency differs from the Sensor's [[frequency]] in cases such as:

5.5. Conditions to expose sensor readings

The user agent can expose sensor readings to a Document document if and only if all of the following are true:

Note: In addition to the conditions above, it is important to note that Sensor subclasses invoke the check sensor policy-controlled features operation in their constructors, and § 7.1.7 Sensor.start() invokes request sensor access. Together, these checks correspond to the mitigation strategies described in § 4.2 Mitigation Strategies.

Note: In order to release hardware resources, the user agent can request underlying platform sensor to suspend notifications about newly available readings until it can expose sensor readings.

6. Model

6.1. Sensor Type

A sensor type must have the following associated data:

The minimum sampling frequency must not be greater than the maximum sampling frequency.

A sensor type may have the following associated data:

6.2. Sensor

The current browsing context's platform sensor must have:

Any time a new sensor reading for a platform sensor is obtained and if the user agent can expose sensor readings to the current navigable's active document, the user agent invokes update latest reading with the platform sensor and the sensor reading as arguments.

The latest reading map contains an entry whose key is "timestamp" and whose value is a high resolution timestamp that estimates the reading timestamp expressed in milliseconds as an unsafe current time.

Latest reading["timestamp"] is initially set to null, unless the latest reading map caches a previous reading.

The other entries of the latest reading map hold the values of the different quantities measured by the platform sensor. The keys of these entries must match the attribute identifier defined by the sensor type's associated extension sensor interface. The return value of the attribute getter is easily obtained by invoking get value from latest reading with the object implementing the extension sensor interface and the attribute identifier as arguments.

The value of all latest reading entries is initially set to null.

To get a platform sensor’s sampling bounds given a platform sensor platformSensor:
  1. Let minimumFrequency be platformSensor’s sensor type's minimum sampling frequency.

  2. If platformSensor’s connected device sensor has a minimum sampling frequency, set minimumFrequency to the maximum of minimumFrequency and this value.

  3. Let maximumFrequency be platformSensor’s sensor type's maximum sampling frequency.

  4. If platformSensor’s connected device sensor has a maximum sampling frequency, set maximumFrequency to the minimum of maximumFrequency and this value.

  5. Return a tuple (minimumFrequency, maximumFrequency).

This example illustrates a possible implementation of the described Model.

In the diagram below several activated Sensor objects from two different browsing contexts interact with a single device sensor.

Generic Sensor Model

The Sensor object in "idle" state is not among the platform sensor's activated sensor objects and thus it does not interact with the device sensor.

In this example there is a platform sensor instance per browsing context.

The latest reading map is shared between Sensor objects from the same context and is updated at a rate equal to the requested sampling frequency of the corresponding platform sensor.

7. API

7.1. The Sensor Interface

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface Sensor : EventTarget {
  readonly attribute boolean activated;
  readonly attribute boolean hasReading;
  readonly attribute DOMHighResTimeStamp? timestamp;
  undefined start();
  undefined stop();
  attribute EventHandler onreading;
  attribute EventHandler onactivate;
  attribute EventHandler onerror;
};

dictionary SensorOptions {
  double frequency;
};

A Sensor object has an associated platform sensor.

Concrete Sensor objects also have an associated sensor type, which is the sensor type that has their interface among its extension sensor interfaces.

The task source for the tasks mentioned in this specification is the sensor task source.

In the following example, firstly, we check whether the user agent has permission to access sensor readings, then we construct accelerometer sensor and add event listeners to get events for platform sensor activation, error conditions and notifications about newly available sensor readings. The example measures and logs maximum total acceleration of a device hosting the platform sensor.

The event handler event types for the corresponding Sensor Interface's event handler attributes are defined in Event handlers section.

navigator.permissions.query({ name: 'accelerometer' }).then(result => {
    if (result.state === 'denied') {
        console.log('Permission to use accelerometer sensor is denied.');
        return;
    }

    let acl = new Accelerometer({frequency: 30});
    let max_magnitude = 0;
    acl.addEventListener('activate', () => console.log('Ready to measure.'));
    acl.addEventListener('error', error => console.log(`Error: ${error.name}`));
    acl.addEventListener('reading', () => {
        let magnitude = Math.hypot(acl.x, acl.y, acl.z);
        if (magnitude > max_magnitude) {
            max_magnitude = magnitude;
            console.log(`Max magnitude: ${max_magnitude} m/s2`);
        }
    });
    acl.start();
});

7.1.1. Sensor lifecycle

Sensor lifecycle idle idle activating activating idle->activating start() start() activating->idle onerror activated activated activating->activated activated->idle stop() / onerror start start->idle construct

Note: The nodes in the diagram above represent the states of a Sensor object and they should not be confused with the possible states of the underlying platform sensor or device sensor.

7.1.2. Sensor garbage collection

A Sensor object whose [[state]] is "activating" must not be garbage collected if there are any event listeners registered for "activated" events, "reading" events, or "error" events.

A Sensor object whose [[state]] is "activated" must not be garbage collected if there are any event listeners registered for "reading" events, or "error" events.

When a Sensor object whose [[state]] is "activated" or "activating" is garbage collected, the user agent must invoke deactivate a sensor object with this object as argument.

7.1.3. Sensor internal slots

Instances of Sensor are created with the internal slots described in the following table:

Internal Slot Description (non-normative)
[[state]] The current state of the Sensor object which is one of "idle", "activating", or "activated". It is initially "idle".
[[frequency]] A double representing frequency in Hz that is used to calculate the sampling frequency for the associated platform sensor and to define the upper bound of the reporting frequency for this Sensor object. It is initially null.
[[lastEventFiredAt]] The high resolution timestamp of the latest sensor reading that was sent to observers of the Sensor object, expressed in milliseconds that passed since the time origin. It is initially null.
[[pendingReadingNotification]] A boolean which indicates whether the observers need to be notified after a new sensor reading was reported. It is initially false.

7.1.4. Sensor.activated

The activated getter steps are:

  1. If this.[[state]] is "activated", return true.

  2. Otherwise, return false.

7.1.5. Sensor.hasReading

The hasReading getter steps are:

  1. Let timestamp be the result of invoking get value from latest reading with this and "timestamp" as arguments.

  2. If timestamp is not null, return true.

  3. Otherwise, return false.

7.1.6. Sensor.timestamp

The timestamp getter steps are:
  1. Let global be this's relevant global object.

  2. Let unsafeTimestamp be the result of invoking get value from latest reading with this and "timestamp" as arguments.

  3. If unsafeTimestamp is null, return null.

  4. Otherwise, return relative high resolution time with unsafeTimestamp and global.

7.1.7. Sensor.start()

The start() method steps are:

  1. If this.[[state]] is either "activating" or "activated", then return.

  2. Set this.[[state]] to "activating".

  3. Run these sub-steps in parallel:

    1. Let permissionState be the result of invoking request sensor access with this as argument.

    2. If permissionState is "denied", then:

      1. Let e be the result of creating a "NotAllowedError" DOMException.

      2. Queue a task to run notify error with this and e as arguments.

      3. Return.

    3. Let connected be the result of invoking connect to sensor with this and this's relevant global object as argument.

    4. If connected is false, then

      1. Let e be the result of creating a "NotReadableError" DOMException.

      2. Queue a task to run notify error with this and e as arguments.

      3. Return.

    5. Invoke activate a sensor object with this as argument.

7.1.8. Sensor.stop()

The stop() method steps are:

  1. If this.[[state]] is "idle", then return.

  2. Set this.[[state]] to "idle".

  3. Run these sub-steps in parallel:

    1. Invoke deactivate a sensor object with this as argument.

7.1.9. Sensor.onreading

onreading is an EventHandler which is called to notify that new reading is available.

7.1.10. Sensor.onactivate

onactivate is an EventHandler which is called when this.[[state]] transitions from "activating" to "activated".

7.1.11. Sensor.onerror

onerror is an EventHandler which is called whenever an error in an abstract or IDL operation cannot be handled synchronously.

7.1.12. Event handlers

The following are the event handlers (and their corresponding event handler event types) that must be supported as attributes by the objects implementing the Sensor interface:

event handler event handler event type
onreading reading
onactivate activate
onerror error

7.2. The SensorErrorEvent Interface

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface SensorErrorEvent : Event {
  constructor(DOMString type, SensorErrorEventInit errorEventInitDict);
  readonly attribute DOMException error;
};

dictionary SensorErrorEventInit : EventInit {
  required DOMException error;
};

SensorErrorEvent instances are constructed by following the steps described in Event's constructor.

The error attribute must return the value it was initialized to. It represents the DOMException object passed to SensorErrorEventInit.

8. Abstract Operations

8.1. Initialize a sensor object

input

sensor_instance, a Sensor object.

options, a SensorOptions dictionary instance.

output

None

  1. For each keyvalue of options

    1. If the associated supported sensor options does not contain key

      1. Throw "NotSupportedError" DOMException.

  2. If options["frequency"] exists, then

    1. Set sensor_instance.[[frequency]] to options["frequency"].

    Note: There is no guarantee that the requested options["frequency"] can be respected. See § 5.4 Sampling Frequency and Reporting Frequency for constraints that may be applied.

8.2. Check sensor policy-controlled features

input

sensor_type, a sensor type.

output

True if all of the associated sensor feature names are allowed to use, false otherwise.

  1. Let feature_names be the sensor_type’s associated sensor feature names.

  2. For each feature_name of feature_names,

    1. If active document is not allowed to use the policy-controlled feature named feature_name, then:

      1. Return false.

  3. Return true.

8.3. Connect to sensor

input

sensor, a Sensor object.

global, a global object.

output

True if sensor was associated with a platform sensor, false otherwise.

  1. Let platformSensor be null.

  2. Let type be sensor’s associated sensor type.

  3. Let virtualSensorType be sensor’s associated virtual sensor type, or null if it is not set.

  4. Let topLevelTraversable be global’s navigable's top-level traversable.

  5. If virtualSensorType is not null and topLevelTraversable’s virtual sensor mapping contains virtualSensorType:

    1. Let virtualSensor be topLevelTraversable’s virtual sensor mapping[virtualSensorType].

    2. If virtualSensor’s can provide readings flag is true, set platformSensor to a platform sensor corresponding to virtualSensor.

      Note: If the can provide readings flag is false, platformSensor will remain null and this algorithm will return false.

  6. Otherwise:

    1. If the device has a single device sensor which can provide readings for type, then

      1. Set platformSensor to a platform sensor corresponding to this device sensor.

    2. If the device has multiple device sensors which can provide readings for type, then

      1. If type has an associated default sensor, then

        1. Set platformSensor to a platform sensor corresponding to this default device sensor.

  7. If platformSensor is null, return false.

  8. Let bounds be the result of invoking get a platform sensor’s sampling bounds with platformSensor.

  9. If sensor.[[frequency]] is null, set it to an implementation-defined value dependent on type.

  10. If sensor.[[frequency]] is less than bounds[0], set it to bounds[0].

  11. If sensor.[[frequency]] is greater than bounds[1], set it to bounds[1].

  12. Return true.

8.4. Activate a sensor object

input

sensor_instance, a Sensor object.

output

None

  1. Let sensor be the platform sensor associated with sensor_instance.

  2. Append sensor_instance to sensor’s set of activated sensor objects.

  3. Invoke set sensor settings with sensor as argument.

  4. Queue a task to run notify activated state with sensor_instance as an argument.

8.5. Deactivate a sensor object

input

sensor_instance, a Sensor object.

output

None

  1. Remove all tasks associated with sensor_instance from the task queue associated with sensor task source.

  2. Let sensor be the platform sensor associated with sensor_instance.

  3. If sensor’s set of activated sensor objects contains sensor_instance,

    1. Remove sensor_instance from sensor’s set of activated sensor objects.

    2. Invoke set sensor settings with sensor as argument.

    3. Set sensor_instance.[[pendingReadingNotification]] to false.

    4. Set sensor_instance.[[lastEventFiredAt]] to null.

8.6. Generic Sensor permission revocation algorithm

input

permissionName, a powerful feature name

output

None

  1. For each Sensor instance sensor in the current realm:

    1. If sensor.[[state]] is "idle", then continue.

    2. If sensor’s sensor type's sensor permission names contains permissionName:

      1. Invoke deactivate a sensor object with sensor.

      2. Let exception be the result of creating a "NotAllowedError" DOMException.

      3. Queue a task to run notify error with sensor and exception.

8.7. Set sensor settings

input

platformSensor, a platform sensor.

output

None

  1. If platformSensor’s set of activated sensor objects is empty,

    1. Set platformSensor’s sampling frequency to null.

    2. For each keyvalue of platformSensor’s latest reading.

      1. Set platformSensor’s latest reading[key] to null.

    3. Update the implementation-defined way in which sensor readings are obtained from platformSensor to no longer provide readings.

    4. Return.

  2. Set platformSensor’s sampling frequency to an implementation-defined value based on the [[frequency]] values of the items in its activated sensor objects set.

  3. Let bounds be the result of invoking get a platform sensor’s sampling bounds with platformSensor.

  4. Assert: platformSensor’s sampling frequency is greater than or equal to bounds[0] and less than or equal to bounds[1].

8.8. Update latest reading

input

sensor, a platform sensor.

reading, a sensor reading.

output

None

  1. Let type be sensor’s associated sensor type.

  2. If type’s threshold check algorithm is defined, then:

    1. Let result be the result of invoking type’s threshold check algorithm with reading and sensor’s latest reading.

    2. If result is false, then abort these steps.

  3. If reading["timestamp"] exists:

    1. Set reading["timestamp"] to the result of converting its current value in an implementation-defined way to an unsafe current time using the same monotonic clock that is shared by time origins.

      Note: The goal of this step is to ensure that a timestamp that may have been relative to a different time origin is converted to a value that can be used in computations with the same monotonic clock used by the operations described in [HR-TIME].

  4. Otherwise, set reading["timestamp"] to the unsafe shared current time.

    Note: In neither case is an unsafe current time ever exposed to script. Sensor.timestamp always returns a coarsened moment relative to a time origin.

  5. For each keyvalue of latest reading.

    1. Set latest reading[key] to the corresponding value of reading.

  6. Let activated_sensors be sensor’s associated set of activated sensor objects.

  7. Run these sub-steps in parallel:

    1. For each s in activated_sensors,

      1. Invoke report latest reading updated with s as an argument.

8.9. Report latest reading updated

input

sensor_instance, a Sensor object.

output

None

  1. If sensor_instance.[[pendingReadingNotification]] is true,

    1. Return.

  2. Set sensor_instance.[[pendingReadingNotification]] to true.

  3. Let lastReportedTimestamp be the value of sensor_instance.[[lastEventFiredAt]].

  4. If lastReportedTimestamp is not set

    1. Queue a task to run notify new reading with sensor_instance as an argument.

    2. Return.

  5. Assert: sensor_instance.[[frequency]] is not null.

  6. Assert: sensor_instance.[[frequency]] is greater than 0.

  7. Let reportingInterval be the result of 1 / sensor_instance.[[frequency]].

  8. Let timestampDelta be the result of latest reading["timestamp"] - lastReportedTimestamp.

  9. If timestampDelta is greater than or equal to reportingInterval

    1. Queue a task to run notify new reading with sensor_instance as an argument.

    2. Return.

  10. Let deferUpdateTime be the result of reportingInterval - timestampDelta.

  11. Spin the event loop for a period of time equal to deferUpdateTime.

  12. If sensor_instance.[[pendingReadingNotification]] is true,

    1. Queue a task to run notify new reading with sensor_instance as an argument.

8.10. Notify new reading

input

sensor_instance, a Sensor object.

output

None

  1. Set sensor_instance.[[pendingReadingNotification]] to false.

  2. Set sensor_instance.[[lastEventFiredAt]] to latest reading["timestamp"].

  3. Fire an event named "reading" at sensor_instance.

8.11. Notify activated state

input

sensor_instance, a Sensor object.

output

None

  1. Set sensor_instance.[[state]] to "activated".

  2. Fire an event named "activate" at sensor_instance.

  3. Let sensor be the platform sensor associated with sensor_instance.

  4. If sensor’s latest reading["timestamp"] is not null,

    1. Queue a task to run notify new reading with sensor_instance as an argument.

8.12. Notify error

input

sensor_instance, a Sensor object.

error, a DOMException.

output

None

  1. Set sensor_instance.[[state]] to "idle".

  2. Fire an event named "error" at sensor_instance using SensorErrorEvent with its error attribute initialized to error.

8.13. Get value from latest reading

input

sensor_instance, a Sensor object.

key, a string representing the name of the value.

output

A sensor reading value or null.

  1. If sensor_instance.[[state]] is "activated",

    1. Let readings be the latest reading of sensor_instance’s related platform sensor.

    2. Let type be sensor_instance’s associated sensor type.

    3. If type’s reading quantization algorithm is defined, then:

      1. Set readings to the result of invoking type’s reading quantization algorithm with readings.

    4. If the extension specification defines a local coordinate system for sensor_instance,

      1. Remap (see [COORDINATES-TRANSFORMATION]) readings values to the local coordinate system.

    5. Return readings[key].

  2. Otherwise, return null.

8.14. Request sensor access

input

sensor_instance, a Sensor object.

output

A permission state.

  1. Let sensor_type be sensor_instance’s associated sensor type.

  2. Let sensor_permissions be sensor_type’s associated sensor permission names.

  3. For each permission_name in sensor_permissions,

    1. Let state be the result of requesting permission to use permission_name.

    2. If state is "denied"

      1. Return "denied".

  4. Return "granted".

8.15. Focus and origin check

input

document, a Document.

output

A boolean.

  1. Let origin be document’s relevant settings object's origin.

  2. Let focusedDocument be document’s node navigable's top-level traversable's currently focused area's DOM anchor's node document.

  3. Let focusedOrigin be focusedDocument’s relevant settings object's origin.

  4. Return true if origin and focusedOrigin are same origin-domain, and false otherwise.

9. Automation

The Generic Sensor API and its extension specifications pose a challenge to test authors, as fully exercising those interfaces requires physical hardware devices that respond in predictable ways. To address this challenge this document defines a number of [WEBDRIVER2] extension commands that allow defining and controlling virtual sensors that behave like device sensors. These virtual sensors represent devices with particular properties and whose readings can be entirely defined by users.

9.1. Virtual Sensors

A virtual sensor simulates the behavior of a device sensor in controlled ways. It reports sensor readings to zero or more platform sensors connected to it.

A virtual sensor has the following associated data:

A virtual sensor type is a string that represents a sensor of a given type.

The per-type virtual sensor metadata is an ordered map of virtual sensor types to virtual sensor metadata. It is initially empty, and extension specifications should define one or more entries in the map corresponding to the sensor types they define.

A virtual sensor metadata is a struct whose items are:

Each top-level traversable has a virtual sensor mapping, which is an ordered map of virtual sensor types to virtual sensor.

Note: The virtual sensor mapping struct contains data that is common to all virtual sensors of a given type. A virtual sensor contains data that can vary on virtual sensor creation and utilization.

Note: Virtual sensor mappings are tied to top-level traversables rather than any navigable because platform sensors with a given sensor type in all navigables with the same top-level traversable are supposed to connect to the same virtual sensor. This better mimics real-world behavior, where the same hardware (or fusion) sensor provides readings to different navigables.

Note: This behavior additionally aids testing of this specification through web-platform-tests, as all WebDriver communication via testdriver.js goes through the frame containing the test harness.

9.2. Extension Commands

9.2.1. Create virtual sensor

HTTP Method URI Template
POST /session/{session id}/sensor

This extension command creates a new virtual sensor of a certain sensor type. Calls to Sensor.start() from Sensor instances of the same sensor type will cause this virtual sensor to be used as their backing device sensor until § 9.2.4 Delete virtual sensor is run.

Note: The way this extension command works allows Sensor instances of the same type to coexist and have different device sensors. A Sensor sensor may have been created and connected to a real, hardware sensor before this extension command is invoked. It continues to work and receive readings from it, and only receives readings from a virtual sensor if connect to sensor is invoked again.

Properties of the parameters argument used by this algorithm
Parameter name Value type Required
"type" String Yes
"connected" Boolean No
"maxSamplingFrequency" Number No
"minSamplingFrequency" Number No
The remote end steps are:
  1. Let virtualSensorType be the result of invoking get a property "type" from parameters.

  2. If virtualSensorType is not a String, return error with WebDriver error code invalid argument.

  3. If per-type virtual sensor metadata does not contain virtualSensorType, return error with WebDriver error code invalid argument.

  4. Let topLevelVirtualSensorMapping be the current browsing context's top-level traversable's virtual sensor mapping.

  5. If topLevelVirtualSensorMapping contains virtualSensorType, return error with WebDriver error code invalid argument.

  6. Let connected be the result of invoking get a property with default with "connected" and true from parameters.

  7. Let maxSamplingFrequency be the result of invoking get a property with default with "maxSamplingFrequency" and an implementation-defined value from parameters.

  8. If maxSamplingFrequency is not a Number, or its value is NaN, +∞, or −∞, return error with WebDriver error code invalid argument.

  9. Let minSamplingFrequency be the result of invoking get a property with default with "minSamplingFrequency" and an implementation-defined value from parameters.

  10. If minSamplingFrequency is not a Number, or its value is NaN, +∞, or −∞, return error with WebDriver error code invalid argument.

  11. If minSamplingFrequency is greater than maxSamplingFrequency, return error with WebDriver error code invalid argument.

  12. Let virtualSensor be a new virtual sensor.

  13. Set virtualSensor’s can provide readings flag to connected.

  14. Set virtualSensor’s minimum sampling frequency to minSamplingFrequency.

  15. Set virtualSensor’s maximum sampling frequency to maxSamplingFrequency.

  16. Set topLevelVirtualSensorMapping[virtualSensorType] to virtualSensor.

  17. Return success with data null.

To create an "ambient-light" virtual sensor in the current browsing context of the session with ID 23, the local end would POST to /session/23/sensor with the body:
{
  "type": "ambient-light",
  "maxSamplingFrequency": 60,
  "minSamplingFrequency": 5
}

Be aware that only one virtual sensor of a given sensor type can be created in a top-level traversable, otherwise an invalid argument WebDriver error code will be returned.

9.2.2. Get virtual sensor information

HTTP Method URI Template
GET /session/{session id}/sensor/{type}

This extension command retrieves information about a given virtual sensor created by § 9.2.1 Create virtual sensor.

When it returns success, success's associated data is an Object with the following properties:

Property name Value type Description (non-normative)
"requestedSamplingFrequency" Number The virtual sensor's requested sampling frequency

Note: See § 5.4 Sampling Frequency and Reporting Frequency for some constraints on sampling frequency as well as the explanation about why a requested sampling frequency is an implementation-defined value in § 9.1 Virtual Sensors. In general, it is only safe to assume that the value lies within the bounds set by the virtual sensor's minimum sampling frequency and maximum sampling frequency.

The remote end steps are:
  1. Let virtualSensorType be the value of the type url variable.

  2. Let topLevelVirtualSensorMapping be the current browsing context's top-level traversable's virtual sensor mapping.

  3. If topLevelVirtualSensorMapping does not contain virtualSensorType, return error with WebDriver error code invalid argument.

  4. Let virtualSensor be topLevelVirtualSensorMapping[virtualSensorType].

  5. Let info be a new Object.

  6. Invoke set a property on info with "requestedSamplingFrequency" and virtualSensor’s requested sampling frequency.

  7. Return success with data info.

9.2.3. Update virtual sensor reading

HTTP Method URI Template
POST /session/{session id}/sensor/{type}

This extension command makes a new sensor reading available to platform sensors.

Note: A virtual sensor acts like a device sensor, so the sensor reading produced here still has to be processed by a platform sensor, which might discard it due to, for example, a sensor type's threshold check algorithm or can expose sensor readings's result.

Properties of the parameters argument used by this algorithm
Parameter name Value type Required
"reading" Object Yes
The remote end steps are:
  1. Let reading be the result of invoking get a property "reading" from parameters.

  2. If reading is not an Object, return error with WebDriver error code invalid argument.

  3. Let virtualSensorType be the value of the type url variable.

  4. If per-type virtual sensor metadata does not contain virtualSensorType, return error with WebDriver error code invalid argument.

  5. Let metadata be per-type virtual sensor metadata[virtualSensorType].

  6. Let topLevelVirtualSensorMapping be the current browsing context's top-level traversable's virtual sensor mapping.

  7. If topLevelVirtualSensorMapping does not contain virtualSensorType, return error with WebDriver error code invalid argument.

  8. Let virtualSensor be topLevelVirtualSensorMapping[virtualSensorType].

  9. Let parsedReading be the result of invoking metadata’s reading parsing algorithm with reading.

  10. If parsedReading is undefined, return error with WebDriver error code invalid argument.

  11. In an implementation-defined way, make parsedReading available so that it can be obtained by platform sensors connected to virtualSensor.

  12. Return success with data null.

9.2.3.1. Algorithms for parsing readings

This specification defines some algorithms that extension specifications can use when defining a virtual sensor metadata for use in per-type virtual sensor metadata.

9.2.3.1.1. Parse single-value number reading
input

parameters, a JSON Object

valueName, a string

output

A sensor reading or undefined

  1. Let value be the result of invoking get a property from parameters with valueName.

  2. If value is not a Number, or its value is NaN, +∞, or −∞, return undefined.

  3. Let reading be a new sensor reading.

  4. Set reading[valueName] to value.

  5. Return reading.

9.2.3.1.2. Parse XYZ reading
input

parameters, a JSON Object

output

A sensor reading or undefined

  1. Let x be the result of invoking get a property from parameters with "x".

  2. If x is not a Number, or its value is NaN, +∞, or −∞, return undefined.

  3. Let y be the result of invoking get a property from parameters with "y".

  4. If y is not a Number, or its value is NaN, +∞, or −∞, return undefined.

  5. Let z be the result of invoking get a property from parameters with "z".

  6. If z is not a Number, or its value is NaN, +∞, or −∞, return undefined.

  7. Let reading be a new sensor reading.

  8. Set reading["x"] to x.

  9. Set reading["y"] to y.

  10. Set reading["z"] to z.

  11. Return reading.

9.2.4. Delete virtual sensor

HTTP Method URI Template
DELETE /session/{session id}/sensor/{type}

This extension command deletes a given type of virtual sensor.

The remote end steps are:
  1. Let virtualSensorType be the value of the type url variable.

  2. If per-type virtual sensor metadata does not contain virtualSensorType, return error with WebDriver error code invalid argument.

  3. Let topLevelVirtualSensorMapping be the current browsing context's top-level traversable's virtual sensor mapping.

  4. Remove topLevelVirtualSensorMapping[virtualSensorType].

  5. Return success with data null.

Note: The behavior of platform sensors and Sensor instances when a device sensor in use stops being available (e.g. it has been physically disconnected, or stopped due to a factor unrelated to the User Agent) is not specified. Implementations may, among other things, keep existing Sensor instances unchanged (they simply will not report new readings), deactivate a sensor object or cause the platform sensor to report an error that will ultimately result in notify error being invoked.

10. Extensibility

This section is non-normative.

Note: This section and its subsections provide guidance to extension specification authors using normative language. From implementers' point of view, this section and its subsections are considered non-normative.

This section describes how this specification can be extended to specify APIs for different sensor types.

Such extension specifications are encouraged to focus on a single sensor type, exposing both high and low level as appropriate.

Extension specifications are encouraged to define whether a calibration process applies to sensor readings.

Extension specifications may explicitly define the local coordinate system for the associated sensor type or make it configurable per Sensor object.

For an up-to-date list of extension specifications, please refer to [GENERIC-SENSOR-USECASES] and [MOTION-SENSORS] documents.

10.1. Security and Privacy

Extension specifications are expected to:

10.2. Naming

Sensor interfaces for low-level sensors should be named after their associated platform sensor. So for example, the interface associated with a gyroscope should be simply named Gyroscope. Sensor interfaces for high-level sensors should be named by combining the physical quantity the platform sensor measures with the "Sensor" suffix. For example, a platform sensor measuring the distance at which an object is from it may see its associated interface called ProximitySensor.

Attributes of the Sensor subclass that hold sensor readings values should be named after the full name of these values. For example, the Thermometer interface should hold the sensor reading's value in a temperature attribute (and not a value or temp attribute). A good starting point for naming are the Quantities, Units, Dimensions and Data Types Ontologies [QUDT].

10.3. Unit

Extension specifications must specify the unit of sensor readings.

As per the Technical Architecture Group’s (TAG) API Design Principles [API-DESIGN-PRINCIPLES], all time measurement should be in milliseconds. All other units should be specified using, in order of preference, and with the exception of temperature (for which Celsius should be favored over Kelvin), the International System of Units (SI), SI derived units, and Non-SI units accepted for use with the SI, as described in the SI Brochure [SI].

10.4. Exposing High-Level vs. Low-Level Sensors

So far, specifications exposing sensors to the Web platform have focused on high-level sensors APIs. [GEOLOCATION-API] [ORIENTATION-EVENT]

This was a reasonable approach for a number of reasons. Indeed, high-level sensors:

However, an increasing number of use cases such as virtual and augmented reality require low-level access to sensors, most notably for performance reasons.

Providing low-level access enables Web application developers to leverage domain-specific constraints and design more performant systems.

Following the precepts of the Extensible Web Manifesto [EXTENNNNSIBLE], extension specifications should focus primarily on exposing low-level sensor APIs, but should also expose high-level APIs when they are clear benefits in doing so.

10.5. When is Enabling Multiple Sensors of the Same Type Not the Right Choice?

It is not advisable to construct multiple Sensor instances of the same sensor type with equal construction parameters, as it can lead to unnecessary hardware resources consumption.

In cases when multiple observers are interested in notifications of a newly available sensor reading, an event listener can be added on a single Sensor instance instead of creating multiple instances of the same sensor type and using simple onreading event handler.

Conversely, multiple Sensors of the same sensor type can be created when they are intended to be used with different settings, such as: frequency, accuracy or other settings defined in extension specifications.

10.6. Definition Requirements

Extension specifications must define all the associated data listed in § 6.1 Sensor Type.

This section provides more information about some of the associated data that extension specifications must specify.

An extension specification may specify the following definitions for each sensor type:

10.7. Automation

In order to enable user-agent automation and application testing, extension specifications are encouraged to:

The extension specification for proximity sensors described in § 10.10 Example WebIDL could contain the following text:
The Proximity Sensor is a sensor type with one associated extension sensor interface, ProximitySensor. Its associated virtual sensor type is "proximity".

[...]

The proximity reading parsing algorithm, given a JSON Object parameters, must invoke parse single-value number reading with parameters and "distance".

The per-type virtual sensor metadata map must have an entry whose key is "proximity" and whose value is a virtual sensor metadata whose reading parsing algorithm is proximity reading parsing algorithm.

10.8. Extending the Permission API

An implementation of the Sensor interface for each sensor type must protect its reading by associated name or PermissionDescriptor. A low-level sensor may use its interface name as a name, for instance, "gyroscope" or "accelerometer". Fusion sensors must request permission to access each of the sensors that are used as a source of fusion.

Even though it might be difficult to reconstruct low-level sensor readings from fused data, some of the original information might be inferred. For example, it is easy to deduce user’s orientation in space if absolute or geomagnetic orientation sensors are used, therefore, these sensors must request permission to use magnetometer as it provides information about orientation of device in relation to Earth’s magnetic field. In contrast, relative orientation sensor does not expose such information, thus, it does not need to request permission to use magnetometer.

Permission descriptors can also be used to set maximum allowed limits for accuracy or sampling frequency. An example for a possible extension of the Permission API for accelerometer sensor is given below.

dictionary AccelerometerPermissionDescriptor : PermissionDescriptor {
    boolean highAccuracy = false;
    boolean highFrequency = false;
};

10.9. Extending the Permissions Policy API

An implementation of the Sensor interface for each sensor type has one (if sensor fusion is not performed) or several policy-controlled features that control whether or not this implementation can be used in a document.

The features' default allowlist is 'self'.

Note: The default allowlist of 'self' allows Sensor interface implementation usage in same-origin nested frames but prevents third-party content from sensor readings access.

The sensor feature names set must contain policy-controlled feature tokens of every associated feature.

A low-level sensor may use its interface name as a policy-controlled feature token, for instance, "gyroscope" or "accelerometer". Unless the extension specification defines otherwise, the sensor feature names matches the same type-associated sensor permission names.

The accelerometer feature is selectively enabled for third-party origin by adding an allow attribute to the frame container element:
<iframe src="https://third-party.com" allow="accelerometer"/></iframe>
A sensor usage is disabled completely by specifying the permissions policy in an HTTP response header:
Permissions-Policy: accelerometer=()

Fusion sensors must use sensor feature names of the sensors that are used as a source of fusion.

Allow third-party origin to use accelerometer, magnetometer and gyroscope features that are required by the absolute orientation sensor.
<iframe src="https://third-party.com" allow="accelerometer; magnetometer; gyroscope"/>

10.10. Example WebIDL

Here’s an example WebIDL for a possible extension of this specification for proximity sensors.

[SecureContext, Exposed=Window]
interface ProximitySensor : Sensor {
    constructor(optional ProximitySensorOptions proximitySensorOptions = {});
    readonly attribute double? distance;
};

dictionary ProximitySensorOptions : SensorOptions {
    double min;
    double max;
    ProximitySensorPosition position;
    ProximitySensorDirection direction;
};

enum ProximitySensorPosition {
    "top-left",
    "top",
    "top-right",
    "middle-left",
    "middle",
    "middle-right",
    "bottom-left",
    "bottom",
    "bottom-right"
};

enum ProximitySensorDirection {
    "front",
    "rear",
    "left",
    "right",
    "top",
    "bottom"
};

11. Acknowledgements

First and foremost, I would like to thank Anssi Kostiainen for his continuous and dedicated support and input throughout the development of this specification, as well as Mikhail Pozdnyakov, Alexander Shalamov, Rijubrata Bhaumik, and Kenneth Rohde Christiansen for their invaluable implementation feedback, suggestions, and research that have helped inform the specification work.

Special thanks to Rick Waldron for driving the discussion around a generic sensor API design for the Web, sketching the original API on which this is based, providing implementation feedback from his work on Johnny-Five, and continuous input during the development of this specification.

Special thanks to Boris Smus, Tim Volodine, and Rich Tibbett for their initial work on exposing sensors to the web with consistency.

Thanks to Anne van Kesteren for his tireless help both in person and through IRC.

Thanks to Domenic Denicola and Jake Archibald for their help.

Thanks also to Frederick Hirsch and Dominique Hazaël-Massieux (via the HTML5Apps project) for both their administrative help and technical input.

Thanks to Tab Atkins for making Bikeshed and taking the time to explain its subtleties.

Thanks to Lukasz Olejnik and Maryam Mehrnezhad for their contributions around privacy and security.

The following people have greatly contributed to this specification through extensive discussions on GitHub: Anssi Kostiainen, Boris Smus, chaals, Claes Nilsson, Dave Raggett, David Mark Clements, Domenic Denicola, Dominique Hazaël-Massieux (via the HTML5Apps project), Francesco Iovine, Frederick Hirsch, gmandyam, Jafar Husain, Johannes Hund, Kris Kowal, Lukasz Olejnik, Marcos Caceres, Marijn Kruisselbrink, Mark Foltz, Mats Wichmann, Matthew Podwysocki, Olli Pettay, pablochacin, Remy Sharp, Rich Tibbett, Rick Waldron, Rijubrata Bhaumik, robman, Sean T. McBeth, Tab Atkins Jr., Virginie Galindo, zenparsing, and Zoltan Kis.

We’d also like to thank Anssi Kostiainen, Dominique Hazaël-Massieux, Erik Wilde, and Michael[tm] Smith for their editorial input.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HR-TIME]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PERMISSIONS-POLICY]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[PERMISSIONS-REQUEST]
Requesting Permissions. cg-draft. URL: https://wicg.github.io/permissions-request/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBDRIVER2]
Simon Stewart; David Burns. WebDriver. URL: https://w3c.github.io/webdriver/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[ACCELPRINT]
Dey, Sanorita, et al.. AccelPrint: Imperfections of Accelerometers Make Smartphones Trackable. 2014. Informational. URL: http://synrg.csl.illinois.edu/papers/AccelPrint_NDSS14.pdf
[API-DESIGN-PRINCIPLES]
Sangwhan Moon; Lea Verou. Web Platform Design Principles. URL: https://w3ctag.github.io/design-principles/
[COORDINATES-TRANSFORMATION]
George W. Collins, II. The Foundations of Celestial Mechanics. 2004. Informational. URL: http://ads.harvard.edu/books/1989fcm..book/Chapter2.pdf
[EXTENNNNSIBLE]
The Extensible Web Manifesto. 10 June 2013. URL: https://extensiblewebmanifesto.org/
[GENERIC-SENSOR-USECASES]
Rick Waldron, Mikhail Pozdnyakov, Alexander Shalamov. Sensor Use Cases. 2017. Note. URL: https://w3c.github.io/sensors/usecases
[GEOLOCATION-API]
Andrei Popescu. Geolocation API Specification 2nd Edition. URL: https://w3c.github.io/geolocation-api/
[GYROSPEECHRECOGNITION]
Michalevsky, Y., Boneh, D. and Nakibly, G.. Gyrophone: Recognizing Speech from Gyroscope Signals. 2014. Informational. URL: https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-michalevsky.pdf
[MOBILESENSORS]
Manish J. Gajjar. Mobile Sensors and Context-Aware Computing. 2017. Informational.
[MOTION-SENSORS]
Kenneth Christiansen; Alexander Shalamov. Motion Sensors Explainer. URL: https://w3c.github.io/motion-sensors/
[ORIENTATION-EVENT]
Reilly Grant; Raphael Kubo da Costa. DeviceOrientation Event Specification. URL: https://w3c.github.io/deviceorientation/
[POWERFUL-FEATURES]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[QUDT]
Ralph Hodgson; et al. QUDT - Quantities, Units, Dimensions and Data Types Ontologies. 18 March 2014. URL: http://www.qudt.org/
[RFC6454]
A. Barth. The Web Origin Concept. December 2011. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6454
[SECURITY-PRIVACY-QUESTIONNAIRE]
Theresa O'Connor; Peter Snyder. Self-Review Questionnaire: Security and Privacy. URL: https://w3ctag.github.io/security-questionnaire/
[SI]
SI Brochure: The International System of Units (SI), 8th edition. 2014. URL: http://www.bipm.org/en/publications/si-brochure/
[STEALINGPINSVIASENSORS]
Maryam Mehrnezhad, Ehsan Toreini, Siamak F. Shahandashti, Feng Hao. Stealing PINs via mobile sensors: actual risk versus user perception. 2017. Informational. URL: https://rd.springer.com/article/10.1007/s10207-017-0369-x?wt_mc=Internal.Event.1.SEM.ArticleAuthorOnlineFirst

IDL Index

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface Sensor : EventTarget {
  readonly attribute boolean activated;
  readonly attribute boolean hasReading;
  readonly attribute DOMHighResTimeStamp? timestamp;
  undefined start();
  undefined stop();
  attribute EventHandler onreading;
  attribute EventHandler onactivate;
  attribute EventHandler onerror;
};

dictionary SensorOptions {
  double frequency;
};

[SecureContext, Exposed=(DedicatedWorker, Window)]
interface SensorErrorEvent : Event {
  constructor(DOMString type, SensorErrorEventInit errorEventInitDict);
  readonly attribute DOMException error;
};

dictionary SensorErrorEventInit : EventInit {
  required DOMException error;
};

MDN

Sensor/activate_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/activated

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/error_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/hasReading

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/reading_event

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/start

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/stop

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor/timestamp

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Sensor

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent/SensorErrorEvent

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent/error

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

SensorErrorEvent

In only one current engine.

FirefoxNoneSafariNoneChrome67+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Headers/Permissions-Policy/accelerometer

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/ambient-light-sensor

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/gyroscope

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

Headers/Permissions-Policy/magnetometer

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?