{"id":14491,"date":"2018-08-01T12:20:39","date_gmt":"2018-08-01T12:20:39","guid":{"rendered":"https:\/\/mobiforge.com\/?p=14491"},"modified":"2018-07-25T08:30:23","modified_gmt":"2018-07-25T08:30:23","slug":"the-generic-sensor-api","status":"publish","type":"post","link":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api","title":{"rendered":"The Generic Sensor API"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass-300x223.jpg\" alt=\"iphone compass\" width=\"300\" class=\"responsive alignleft size-medium wp-image-14497\" srcset=\"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass-300x223.jpg 300w, https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass-768x570.jpg 768w, https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass-1024x759.jpg 1024w, https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass-510x378.jpg 510w, https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg 1200w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>Today&rsquo;s devices pack in a vast array of sensors that gather data about the device and the world around it. For web applications, access to these sensors has grown over time through the addition to the browser of various sensor APIs such as the Geolocation API, and the DeviceOrientation Events API.<\/p>\n<p>Such APIs have been instrumental in evolving the web platform to feature-parity with native apps, while at the same time still offering all the advantages the web has over native.<\/p>\n<p>In this article we take a look at a new API for device sensors: the Generic Sensor API.<\/p>\n<h2>Why do we need a new sensor API?<\/h2>\n<p>Since we already have APIs to access sensor data in the browser, you might be wondering why we need a new API. <\/p>\n<p>The existing sensor APIs have little in common, especially considering that, broadly, they offer similar types of functionality: reading sensor data, and doing something with that data.<\/p>\n<p>The Generic Sensor API aims to address this by providing a set of consistent APIs to access sensors. The goals of the API are outlined in its <a href=\"https:\/\/www.w3.org\/TR\/generic-sensor\/\">spec (recommendation status)<\/a>:<\/p>\n<blockquote>\n<p>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.<\/p>\n<\/blockquote>\n<p>The Generic Sensor API achieves this by defining a set of interfaces that expose sensors, consisting of the base <code>Sensor<\/code> interface, and concrete sensor classes that extend this, making it easy to add new sensors, and providing a consistent way to use them.<\/p>\n<h3>Fusion sensors<\/h3>\n<p>An interesting aspect of the API is <em>Fusion sensors<\/em>. You can think of these as virtual sensors that combine or fuse the data of multiple <em>real<\/em> sensors into a new sensor that is reliable and robust, or that has filtered and combined the data in such as way that is appropriate for a certain goal.<\/p>\n<p>We&rsquo;ll see the benefit of this later when we first build a simple compass app using the <code>Magnetometer<\/code> sensor directly, and then see how we can improve this based on the <code>AbsoluteOrientationSensor<\/code>, a fusion sensor which combines data from real magnetometer, accelerometer, and gyroscope sensors.<\/p>\n<h2>Sensors supported by the Generic Sensor API<\/h2>\n<p>The API has specifications for a number of sensors, such as <a href=\"https:\/\/www.w3.org\/TR\/accelerometer\">Accelerometer<\/a>, <a href=\"https:\/\/www.w3.org\/TR\/ambient-light\">Ambient Light Sensor<\/a>, <a href=\"https:\/\/www.w3.org\/TR\/magnetometer\">Magnetometer<\/a>, <a href=\"https:\/\/www.w3.org\/TR\/gyroscope\/\">Gyroscope<\/a>, <a href=\"https:\/\/www.w3.org\/TR\/orientation-sensor\">OrientationSensor<\/a> as well as drafts for future sensors, such as <a href=\"https:\/\/wicg.github.io\/geolocation-sensor\/\">Geolocation Sensor<\/a> and <a href=\"https:\/\/www.w3.org\/TR\/proximity\/\">Proximity Sensor<\/a>.<\/p>\n<p>Chrome is leading the way with respect to implementation. The following <em>motion sensors<\/em> are enabled by default since Chrome 67:<\/p>\n<ul>\n<li><code>Accelerometer<\/code><\/li>\n<li><code>Gyroscope<\/code><\/li>\n<li><code>LinearAccelerationSensor<\/code><\/li>\n<li><code>AbsoluteOrientationSensor<\/code><\/li>\n<li><code>RelativeOrientationSensor<\/code><\/li>\n<\/ul>\n<p>And the following <em>environmental sensors<\/em> are not enabled by default, but can be enabled in Chrome flag settings (visit <code>chrome:\/\/flags<\/code> and enable <em>Generic Sensor Extra Classes<\/em>)<\/p>\n<ul>\n<li><code>AmbientLightSensor<\/code><\/li>\n<li><code>Magnetometer<\/code><\/li>\n<\/ul>\n<p>Edge and Firefox have partial implementations of the <code>AmbientLightSensor<\/code>, and <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1432631\">Firefox has an open issue<\/a> to implement the Generic Sensor API, but there is no ETA on this yet. <\/p>\n<p>Support for individual sensor implementations are listed separately on <a href=\"https:\/\/caniuse.com\/#search=sensor\">caniuse.com<\/a>.<\/p>\n<p>W3C invites implementations of the Sensor APIs <a href=\"https:\/\/www.w3.org\/blog\/news\/archives\/6912\">here<\/a>.<\/p>\n<h2>How to use the Generic Sensor API<\/h2>\n<p>Let&rsquo;s run through a couple of examples. Before we begin, note that this API can only be used on <em>secure (HTTPS) contexts<\/em>. <\/p>\n<p>The general pattern of use is:<\/p>\n<ol>\n<li>Create sensor<\/li>\n<li>Define callback function<\/li>\n<li>Start sensor<\/li>\n<li>Stop sensor<\/li>\n<\/ol>\n<h3>1. Create sensor<\/h3>\n<p>We create a sensor like this: <\/p>\n<pre>\r\n  sensor = new &lt;SensorName&gt;();\r\n<\/pre>\n<p>replacing <SensorName> with the name of the desired sensor. For example,<\/p>\n<pre>\r\n  sensor = new Accelerometer();\r\n<\/pre>\n<p>or<\/p>\n<pre>\r\n  sensor = new AbsoluteOrientationSensor();\r\n<\/pre>\n<\/p>\n<p>You get the idea. We could do the same for any of the sensors listed earlier.<\/p>\n<h4>Checking for sensor support<\/h4>\n<p>Don&rsquo;t forget to check for sensor support. There are a couple of ways to check for support for a sensor. First, you can check for support with something like this:<\/p>\n<pre>\r\n  if(AmbientLightSensor in window) {\r\n    \/\/ Create Sensor\r\n  }\r\n<\/pre>\n<p>Alternatively, you can wrap your code within a try-catch block:<\/p>\n<pre>\r\n  try {\r\n    \/\/ Create Sensor\r\n  } catch(error) {\r\n    console.log('Error creating sensor')\r\n    \/\/Fallback, do something else etc.\r\n  }\r\n<\/pre>\n<h4>Setting sampling frequency<\/h4>\n<p>We can also set the data sampling frequency, in Hz:<\/p>\n<pre>\r\n  \/\/Read once per second\r\n  sensor = new AmbientLightSensor({frequency: 1});\r\n<\/pre>\n<p>Note that, for security reasons, the browser implementation of any particular sensor may impose limits on various sensor properties. For example, if you try to set the frequency of the <code>AmbientLightSensor<\/code> in Chrome to a value greater than 10, you will see a console message:<\/p>\n<p><code>Maximum allowed frequency value for this sensor type is 10 Hz.<br \/>\n<\/code><\/p>\n<p>What kind of security concerns might there be around the ambient light sensor? Reflected ambient light levels from a device&rsquo;s screen can be exploited to determine pixel values of the screen. Limiting the sampling frequency can mitigate threats like this.<\/p>\n<h3>2. Define callback function<\/h3>\n<p>Defining a sensor callback is straightforward:<\/p>\n<pre>\r\n  sensor.addEventListener('reading', listener);\r\n\r\n  function listener() {\r\n    \/\/ Do something with the sensor data\r\n  }\r\n<\/pre>\n<h3>3. Start the sensor<\/h3>\n<p>This needs no explanation:<\/p>\n<pre>\r\n  \/\/Start the sensor\r\n  sensor.start()\r\n<\/pre>\n<h3>4. Stop the sensor<\/h3>\n<p>When you&rsquo;re done with the sensor, you can stop using it with:<\/p>\n<pre>\r\n  \/\/Stop the sensor\r\n  sensor.stop()\r\n<\/pre>\n<\/p>\n<p>This pattern will be repeated for all the sensors we use in the following sections.<\/p>\n<h2>Gyroscope<\/h2>\n<p>The Gyroscope sensor measures angular velocity around device&rsquo;s local X, Y, Z axes, in radians per second. The sensor&rsquo;s readings are stored in the <code>x<\/code>, <code>y<\/code>, <code>z<\/code> properties, so we can output the data to the browser with the following code:<\/p>\n<pre>\r\n  &lt;div id='status'&gt;&lt;\/div&gt;\r\n  &lt;script&gt;\r\n    if ( 'Gyroscope' in window ) {\r\n      let sensor = new Gyroscope();\r\n      sensor.addEventListener('reading', function(e) {\r\n        document.getElementById('status').innerHTML = 'x: ' + e.target.x + ' y: ' + e.target.y + ' z: ' + e.target.z;\r\n      });\r\n      sensor.start();\r\n    }\r\n  &lt;\/script&gt;\r\n<\/pre>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/gyroscope.html\" target=\"_blank\">View example<\/a>.<\/p>\n<h2>Accelerometer<\/h2>\n<p>There are actually three sensors defined in the <a href=\"https:\/\/www.w3.org\/TR\/accelerometer\/\">Accelerometer spec<\/a>: <code>Accelerometer<\/code>, <code>LinearAccelerationSensor<\/code>, and <code>GravitySensor<\/code>. Each of them provides some information about the accelerations applied to the device&rsquo;s X, Y, and Z axes. <\/p>\n<h3>Accelerometer Sensor<\/h3>\n<p>The accelerometer provides acceleration information on the device&rsquo;s X, Y, Z axes. Similar to the Gyroscope, we can output this data to the browser with:<\/p>\n<pre>\r\n  let sensor = new Accelerometer();\r\n  sensor.addEventListener('reading', function(e) {\r\n    document.getElementById('status').innerHTML = 'x: ' + e.target.x + ' y: ' + e.target.y + ' z: ' + e.target.z;\r\n    console.log(e.target);\r\n  });\r\n  sensor.start();\r\n<\/pre>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/accelerometer.html\" target=\"_blank\">View example<\/a>.<\/p>\n<h3>LinearAcceleration Sensor<\/h3>\n<p>This is a subclass of <code>Accelerometer<\/code>, and measures acceleration applied to the device <em>excluding<\/em> the acceleration contributed by gravity. It uses <code>x<\/code>, <code>y<\/code>, and <code>z<\/code> properties as above.<\/p>\n<h3>Gravity Sensor<\/h3>\n<p>Another subclass of <code>Accelerometer<\/code>. This time the data represents acceleration due to the effect of gravity on each of the devices axes. (Note that this sensor is not currently available in Chrome).<\/p>\n<h2>Ambient Light Sensor<\/h2>\n<p>Next, let&rsquo;s look at the Ambient Light Sensor. This sensor reports the environmental light level as detected by the device&rsquo;s light sensor. Use cases could include accessibility, smart lighting, and camera settings based on ambient light.<\/p>\n<p>Note that at the time of writing, environmental sensors (the Ambient Light Sensor, and Magnetometer) must be manually enabled in Chrome browser by visiting <code>chrome:\/\/flags<\/code> and enabling the <em>Generic Sensor Extra Classes<\/em> option, so do this first before trying to run any demo code.<\/p>\n<p>The Ambient light sensor reports the detected light level in lux units, between 0 and around 30000 (the upper limit appears to be device-dependent: some devices tested maxed out at 32768, while others at 30000).<\/p>\n<p>First we create the sensor:<\/p>\n<pre>\r\n  sensor = new AmbientLightSensor();\r\n<\/pre>\n<p>Next let&rsquo;s consider the callback function. To read the value from this sensor, we check the <code>illuminance<\/code> property. So we could output the raw data to the browser with:<\/p>\n<pre>\r\n  &lt;div id='status'&gt;&lt;\/div&gt;\r\n\r\n  &lt;script&gt;\r\n    sensor.addEventListener('reading', function(e) {\r\n      document.getElementById('status').innerHTML = e.target.illuminance;\r\n    }\r\n  &lt;\/script&gt;\r\n<\/pre>\n<p>Now let&rsquo;s do something else with the data, apart from just outputting the raw values to the browser.<\/p>\n<p>A use-case for the ambient light sensor could be to automatically adjust screen brightness based on the ambient light. When the environment is bright, we crank up the screen brightness; when it&rsquo;s dark, then we crank it down. However, the web browser can&rsquo;t control the actual raw screen levels. One thing we can do however is adjust the colours on the page to show darker or lighter colours based on the ambient light readings, so we could have a dark theme and a light theme.<\/p>\n<p>The example below shows how to set the background colour of the page based on the ambient light level. We just map the lux value to a value in range 0 to 255, and then set the background colour of the page body to the corresponding grey level:<\/p>\n<pre>\r\n  sensor.addEventListener('reading', function(e) {\r\n    document.getElementById('status').innerHTML = e.target.illuminance;\r\n    console.log(e.target.illuminance);\r\n\r\n    var lux = e.target.illuminance;\r\n    console.log('L:', lux.map(0,500,0,255));\r\n    var val = lux.map(0,500,0,255);\r\n    document.body.style.backgroundColor = 'rgb('+val+','+val+','+val+')';\r\n\r\n  });\r\n<\/pre>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/ambientlight.html\" target=\"_blank\">View example<\/a>.<\/p>\n<p>An alternative example, and perhaps more useful, could be to toggle a CSS class, <code>light<\/code>, or <code>dark<\/code> whenever the light level crosses some threshold.<\/p>\n<p>On the Intel github pages there is a pretty nice map demonstration that takes this idea a bit further: when the ambient light level drops below a threshold a Google Map is switched to a dark theme. <a href=\"https:\/\/intel.github.io\/generic-sensor-demos\/ambient-map\/build\/bundled\/\">https:\/\/intel.github.io\/generic-sensor-demos\/ambient-map\/build\/bundled\/<\/a><\/p>\n<h2>Magnetometer<\/h2>\n<p>Next let&rsquo;s take a look at the <a href=\"https:\/\/www.w3.org\/TR\/magnetometer\/\">Magnetometer sensor<\/a>. As it&rsquo;s name suggests, it measures magnetic fields, in three axes (<code>x, y, z<\/code>) around the device. We can get the raw data by querying the <code>x<\/code>, <code>y<\/code>, and <code>z<\/code> properties like this:<\/p>\n<pre>\r\n  var sensor = new Magnetometer();\r\n  sensor.addEventListener('reading', function(e) {\r\n    document.getElementById('status').innerHTML = 'x: ' + e.target.x + ' y: ' + e.target.y + ' z: ' + e.target.z;\r\n  });\r\n  sensor.start();\r\n<\/pre>\n<p>With a little bit of trigonometry on the <code>x<\/code> and <code>y<\/code> axes (courtesy of the <a href=\"https:\/\/www.w3.org\/TR\/magnetometer\/#compass\">MagnetomerSensor spec<\/a>), we can compute a compass heading, and display that instead:<\/p>\n<pre>\r\n  var sensor = new Magnetometer();\r\n  sensor.addEventListener('reading', function(e) {\r\n    let heading = Math.atan2(e.target.y, e.target.x) * (180 \/ Math.PI);\r\n    document.getElementById('status').innerHTML = 'Heading in degrees: ' + heading;\r\n  });\r\n  sensor.start();\r\n<\/pre>\n<p>(Multiplying by <code>180\/Math.PI<\/code> converts from radians to degrees)<\/p>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/magnetometer.html\" target=\"_blank\">View example<\/a>.<\/p>\n<p>Based on the heading, a simple compass image or a map could be rotated to that heading (we did something similar to this using the <a href=\"https:\/\/mobiforge.com\/design-development\/html5-mobile-web-device-orientation-events\">DeviceOrientation Events API<\/a> previously):<\/p>\n<pre>\r\n sensor.addEventListener('reading', function(e) {\r\n    let heading = Math.atan2(e.target.y, e.target.x) * (180 \/ Math.PI);\r\n    \/\/Adjust heading\r\n    heading = heading-180;\r\n    if(heading &lt; 0) heading = 360 + heading;\r\n    document.getElementById('status').innerHTML = 'Heading in degrees: ' + heading;\r\n      compass.style.Transform = 'rotate(-' + heading + 'deg)';\r\n  });\r\n  sensor.start();\r\n<\/pre>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/compass.html\" target=\"_blank\">View example<\/a>.<\/p>\n<p>There are limitations to this approach though, as outlined in the <a href=\"https:\/\/www.w3.org\/TR\/magnetometer\/#compass\">Magnetometer interface description<\/a>. The main problem is that the device needs to be level with the Earth&rsquo;s surface, or else tilt compensation should be applied. We can improve on this using the Orientation sensor, which is a fusion sensor combining accelerometer, magnetometer, and gyroscope data.<\/p>\n<h2>Fusion sensors: combining real sensors<\/h2>\n<p>We briefly mentioned <em>fusion sensors<\/em> earlier, and how they are a benefit of the Generic Sensor API. By combining data from multiple real sensors, new <em>virtual<\/em> sensors can be implemented that combine and filter the data so that it&rsquo;s easier to use, or so that it can be more useful in a particular problem domain.<\/p>\n<p>We can see this by improving on our initial <code>Magnetometer<\/code> compass example with the <code>OrientationSensor<\/code>.<\/p>\n<h2>The OrientationSensor<\/h2>\n<p>The <code>OrientationSensor<\/code> has two subclasses:<\/p>\n<ol>\n<li><code>AbsoluteOrientationSensor<\/code>: reports rotation of device in <em>world coordinates<\/em>, based on real accelerometer, gyroscope, and magnetometer sensors<\/li>\n<li><code>RelativeOrientationSensor<\/code>: reports rotation of device relative to a stationary coordinate system, based on real accelerometer and gyroscope sensors<\/li>\n<\/ol>\n<h3>Screen coordinates syncing<\/h3>\n<p>A nice feature of the <code>OrientationSensor<\/code> is the automatic mapping of sensor readings to screen coordinates, taking screen orientation into account. Previously this mapping would have to be implemented in the application logic, requiring the developer to write JavaScript code to watch out for orientation change events.<\/p>\n<p>The <code>referenceFrame<\/code> parameter is used to specify whether readings should be relative to the device or the screen orientation:<\/p>\n<pre>\r\n  \/\/ This is the default (i.e. referenceFrame could be omitted here)\r\n  let sensor = new RelativeOrientationSensor({referenceFrame: \"device\"});\r\n\r\n  \/\/ Use screen coordinate system\r\n  let sensor = new RelativeOrientationSensor({referenceFrame: \"screen\"});\r\n<\/pre>\n<h2>Using the OrientationSensor<\/h2>\n<p>Using the these sensors follows the same Generic Sensor API pattern as before:<\/p>\n<pre>\r\n  let sensor = new AbsoluteOrientationSensor({frequency: 60});\r\n  ...\r\n  sensor.start();\r\n<\/pre>\n<pre>\r\n  let sensor = new RelativeOrientationSensor({frequency: 60});\r\n  ...\r\n  sensor.start();\r\n<\/pre>\n<p>We can get the quaternion representing the device motion by reading the <code>quaternion<\/code> property of the sensor event:<\/p>\n<pre>\r\n  e.target.quaternion\r\n<\/pre>\n<h2>Quaternion sensor data<\/h2>\n<p>Each rotation data event from these sensors is returned as a quaternion <code>[x,y,z,w]<\/code> that represents a rotation in 3D space relative to the coordinate system. The components representing the vector part of the quaternion go first, and the scalar component comes after. Returning data in this format makes more compatible with graphics environments such as WebGL.<\/p>\n<p>A real benefit of using quaternion data can be seen through 3D examples with WebGL, or third party 3D libraries such as Three.js. We won&rsquo;t go into these here, but there are several excellent examples on <a href=\"https:\/\/intel.github.io\/generic-sensor-demos\/\">Intel&rsquo;s Generic Sensor API Demos github project<\/a> that demonstrate some of the possibilities, including 360 degree panorama and VR demos. I recommend checking these out.<\/p>\n<p>We&rsquo;ll update our previous compass example to make use of the <code>OrientationSensor<\/code>. Since we&rsquo;re concerned with the orientation of the device <em>with respect to the real world<\/em>, this sounds like a job for <code>AbsoluteOrientationSensor<\/code>.<\/p>\n<h2>A compass with AbsoluteOrientationSensor<\/h2>\n<p>We can start by modifying our previous <code>MagnetometerSensor<\/code> compass example to retrieve the <code>quaternion<\/code> property, instead of the magnetometer axial rotation values:<\/p>\n<pre>\r\n  let compass = document.getElementById('compass');\r\n  let sensor = new AbsoluteOrientationSensor();\r\n  sensor.addEventListener('reading', function(e) {\r\n    var q = e.target.quaternion;\r\n    ...\r\n  }\r\n<\/pre>\n<p>This time to compute the heading we need to calculate the <em>Euler angle<\/em> for the <em>x<\/em> axis. After a quick <a href=\"https:\/\/en.wikipedia.org\/wiki\/Conversion_between_quaternions_and_Euler_angles\">quaternion-to-Euler-angles crash course on Wikipedia<\/a>, and a bit of experimentation to figure out which sensor properties represented the real world axes we are interested in, we can separate out the compass rotation from the rotations represented in the quaternion with the following:<\/p>\n<pre>\r\n  Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2])\r\n<\/pre>\n<p>As before, we&rsquo;ll multiply by <code>180\/Math.PI<\/code> to convert from radians to degrees, and now we have a heading.<\/p>\n<p>The rest of the example code is the same as before.<\/p>\n<pre>\r\n  let compass = document.getElementById('compass');\r\n  let sensor = new AbsoluteOrientationSensor();\r\n  sensor.addEventListener('reading', function(e) {\r\n    var q = e.target.quaternion;\r\n    heading = Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2])*(180\/Math.PI);\r\n    if(heading &lt; 0) heading = 360+heading;\r\n    compass.style.Transform = 'rotate(' + heading + 'deg)';\r\n  }\r\n  sensor.start();\r\n<\/pre>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/fusioncompass.html\" target=\"_blank\"><img decoding=\"async\" src=\"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/fusion-compass.jpg\" alt=\"\" width=\"330\" class=\"responsive aligncenter size-full wp-image-14521\" style=\"border:1px solid #ccc\" srcset=\"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/fusion-compass.jpg 330w, https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/fusion-compass-169x300.jpg 169w\" sizes=\"(max-width: 330px) 100vw, 330px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/fusioncompass.html\" target=\"_blank\">View example<\/a>.<\/p>\n<p>The advantage of the fusion sensor can be seen in this example: the compass is more stable, and not affected by tilting.<\/p>\n<p>(Note, this has only been tested on a couple of Android devices, so your mileage may vary)<\/p>\n<h3>Geolocation Sensor<\/h3>\n<p>There is a request for implementation of a <a href=\"https:\/\/wicg.github.io\/geolocation-sensor\/\">Geolocation Sensor<\/a> for the Generic Sensor API, but at time of writing no such sensor has been implemented in any of the main browsers. Notably, there has been a discussion on the merits of <a href=\"https:\/\/www.w3.org\/TR\/generic-sensor\/#high-vs-low-level\">high-level vs low-level access to underlying sensors<\/a>.<\/p>\n<p>Geolocation Sensor Issues tracker: <a href=\"https:\/\/github.com\/wicg\/geolocation-sensor\/issues\">https:\/\/github.com\/wicg\/geolocation-sensor\/issues<\/a><\/p>\n<h2>Privacy and permissions<\/h2>\n<p>The Generic Sensor API is integrated with the <a href=\"https:\/\/www.w3.org\/TR\/permissions\/\">Permissions API<\/a>. However, the <a href=\"https:\/\/www.w3.org\/TR\/generic-sensor\/#permissions\">spec states<\/a> that <em>access can be granted without prompting the user<\/em>, and it depends on the browser and the sensor in question.<\/p>\n<p>While access to accelerometer and gyro sensors might not seem as important to require permission for, as, say a Geolocation sensor, there can be unforeseen and undesirable uses of seemingly innocuous sensors. <a href=\"https:\/\/twitter.com\/KrauseFx\">Felix Krause<\/a>, a developer at Google, demonstrated a <a href=\"https:\/\/github.com\/KrauseFx\/user.activity\">proof of concept of how a user&rsquo;s activity can be determined<\/a> based on the data coming from acceleration and gyro sensors, using the older APIs.<\/p>\n<p>Some of the activities that can be determined are:<\/p>\n<ul>\n<li>sitting<\/li>\n<li>standing<\/li>\n<li>walking<\/li>\n<li>running<\/li>\n<li>driving<\/li>\n<li>taking a picture<\/li>\n<li>lying in bed<\/li>\n<li>laying the phone on a table<\/li>\n<\/ul>\n<p>\nAccording to Felix, it would likely be possible to determine if a user was impaired due to alcohol too!\n<\/p>\n<\/p>\n<p>The Generic Sensor API team were quick to point out that security and privacy were central considerations in the API, intimating that this <em>should<\/em> not be possible with the Generic Sensor API. <\/p>\n<blockquote class=\"twitter-tweet tw-align-center\" data-lang=\"en\">\n<p lang=\"en\" dir=\"ltr\">Within 2 hours <a href=\"https:\/\/twitter.com\/kennethrohde?ref_src=twsrc%5Etfw\">@kennethrohde<\/a> and <a href=\"https:\/\/twitter.com\/darktears?ref_src=twsrc%5Etfw\">@darktears<\/a>, who both work on Chromium &amp; W3C specs, send me links to where this is being discussed \u2764\ufe0f <a href=\"https:\/\/t.co\/cWfuvZeyTy\">pic.twitter.com\/cWfuvZeyTy<\/a><\/p>\n<p>&mdash; Felix Krause (@KrauseFx) <a href=\"https:\/\/twitter.com\/KrauseFx\/status\/905857541590065152?ref_src=twsrc%5Etfw\">September 7, 2017<\/a><\/p><\/blockquote>\n<p><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n<p>In the current Chrome implementation anyway, user permission <strong>was not required<\/strong> to access any of the sensors used in this article, so it looks like it might still be possible to reproduce this tracking behaviour in the new API. On the other hand, it&rsquo;s worth noting that <a href=\"https:\/\/w3c.github.io\/sensors\/#visibility-state\">sensor readings will only be available for active and visible pages<\/a>. This should reduce somewhat the tracking risks described above in some circumstances, for example, when the device is in a pocket, but not while the user is actively using the device.<\/p>\n<p>A discussion about sensor permissions can be found <a href=\"https:\/\/github.com\/w3c\/sensors\/issues\/174\">here<\/a>.<\/p>\n<h2>Generic Sensor API: a grand-unifying sensor API?<\/h2>\n<p>At the outset of this article, we mentioned that the goal of the API was to provide a consistent interface to all device sensors.<\/p>\n<p>It&rsquo;s early days still. Browser support is minimal. Apart from the <code>AmbientLightSensor<\/code>, Chrome is the only browser offering support right now.<\/p>\n<p>Of the sensors supported in Chrome 67, the API delivers a good developer experience, delivering on its promise to provide a consistent API across different sensors.<\/p>\n<p>A notable sensor absent right now is the Geolocation sensor. Although the <a href=\"https:\/\/mobiforge.com\/design-development\/html5-mobile-web-a-guide-geolocation-api\">HTML5 Geolocation API<\/a> already provides location data (derived from GPS, A-GPS, Wifi data) to the browser, it seems like a likely candidate for the Generic Sensor API.<\/p>\n<p>Geolocation is a key part of the modern mobile experience, covering everything from navigation to local search, and was the first sensor I thought of when I first heard about the Generic Sensor API. While work is underway, <a href=\"https:\/\/wicg.github.io\/geolocation-sensor\/\">its specification<\/a> is only at Editor&rsquo;s draft status, and not an actual recommendation like the other sensors discussed in this article. The Generic Sensor API provides a nice, consistent interface to device sensors but needs to include Geolocation before it can be considered complete.<\/p>\n<h2>Examples and code<\/h2>\n<ul>\n<li>Examples: <a href=\"https:\/\/mobiforge.gitlab.io\/sensors\/\">https:\/\/mobiforge.gitlab.io\/sensors\/<\/a><\/li>\n<li>Example code: <a href=\"https:\/\/gitlab.com\/mobiforge\/sensors\/\">https:\/\/gitlab.com\/mobiforge\/sensors\/<\/a><\/li>\n<\/ul>\n<h2>Links and specs<\/h2>\n<ul>\n<li>Generic Sensor API <a href=\"https:\/\/www.w3.org\/TR\/generic-sensor\/\">https:\/\/www.w3.org\/TR\/generic-sensor\/<\/a><\/li>\n<li>Gyroscope <a href=\"https:\/\/www.w3.org\/TR\/gyroscope\/\">https:\/\/www.w3.org\/TR\/gyroscope\/<\/a><\/li>\n<li>Magnetometer <a href=\"https:\/\/www.w3.org\/TR\/magnetometer\">https:\/\/www.w3.org\/TR\/magnetometer<\/a><\/li>\n<li>Ambient Light Sensor <a href=\"https:\/\/www.w3.org\/TR\/ambient-light\">https:\/\/www.w3.org\/TR\/ambient-light<\/a><\/li>\n<li>Accelerometer <a href=\"https:\/\/www.w3.org\/TR\/accelerometer\">https:\/\/www.w3.org\/TR\/accelerometer<\/a><\/li>\n<li>Orientation sensor <a href=\"https:\/\/www.w3.org\/TR\/orientation-sensor\">https:\/\/www.w3.org\/TR\/orientation-sensor<\/a><\/li>\n<li>Geolocation sensor <a href=\"https:\/\/wicg.github.io\/geolocation-sensor\/\">https:\/\/wicg.github.io\/geolocation-sensor\/<\/a><\/li>\n<li>Proximity sensor <a href=\"https:\/\/www.w3.org\/TR\/proximity\/\">https:\/\/www.w3.org\/TR\/proximity\/<\/a><\/li>\n<li>Sensors for the web <a href=\"https:\/\/developers.google.com\/web\/updates\/2017\/09\/sensors-for-the-web\">https:\/\/developers.google.com\/web\/updates\/2017\/09\/sensors-for-the-web<\/a><\/li>\n<\/ul>\n<p><strong>Main image:<em><a href=\"https:\/\/unsplash.com\/photos\/P6Kx-RVpDe0\">Robert Penaloza \/ Unsplash<\/a><\/em><\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"Today&rsquo;s devices pack in a vast array of sensors that gather data about the device and the world around it. For web applications, access to these sensors has grown over time through the addition to the browser of various sensor APIs such as the Geolocation API, and the DeviceOrientation Events API. Such APIs have been","protected":false},"author":6,"featured_media":14497,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3667,9997],"tags":[10072,3663,10071],"class_list":["post-14491","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-design-development","category-large-featured","tag-generic-sensor-api","tag-html5","tag-sensors"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>The Generic Sensor API - mobiForge<\/title>\n<meta name=\"description\" content=\"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"The Generic Sensor API - mobiForge\" \/>\n<meta property=\"og:description\" content=\"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api\" \/>\n<meta property=\"og:site_name\" content=\"mobiForge\" \/>\n<meta property=\"article:published_time\" content=\"2018-08-01T12:20:39+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"890\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ruadh\u00e1n O&#039;Donoghue\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@rodono\" \/>\n<meta name=\"twitter:site\" content=\"@mobiForge\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ruadh\u00e1n O&#039;Donoghue\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api\"},\"author\":{\"name\":\"Ruadh\u00e1n O'Donoghue\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/#\\\/schema\\\/person\\\/29eef51116f07b88e49e5cd6bd762522\"},\"headline\":\"The Generic Sensor API\",\"datePublished\":\"2018-08-01T12:20:39+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api\"},\"wordCount\":2692,\"image\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/mobiforge.com\\\/wp-content\\\/uploads\\\/2018\\\/07\\\/iphone-compass.jpg\",\"keywords\":[\"generic sensor api\",\"HTML5\",\"sensors\"],\"articleSection\":[\"Design & Development\",\"large-featured\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api\",\"url\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api\",\"name\":\"The Generic Sensor API - mobiForge\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/mobiforge.com\\\/wp-content\\\/uploads\\\/2018\\\/07\\\/iphone-compass.jpg\",\"datePublished\":\"2018-08-01T12:20:39+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/#\\\/schema\\\/person\\\/29eef51116f07b88e49e5cd6bd762522\"},\"description\":\"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#primaryimage\",\"url\":\"https:\\\/\\\/mobiforge.com\\\/wp-content\\\/uploads\\\/2018\\\/07\\\/iphone-compass.jpg\",\"contentUrl\":\"https:\\\/\\\/mobiforge.com\\\/wp-content\\\/uploads\\\/2018\\\/07\\\/iphone-compass.jpg\",\"width\":1200,\"height\":890,\"caption\":\"iphone compass\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/design-development\\\/the-generic-sensor-api#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mobiforge.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"The Generic Sensor API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/#website\",\"url\":\"https:\\\/\\\/mobiforge.com\\\/\",\"name\":\"mobiForge\",\"description\":\"Mobile Web Development\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/mobiforge.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mobiforge.com\\\/#\\\/schema\\\/person\\\/29eef51116f07b88e49e5cd6bd762522\",\"name\":\"Ruadh\u00e1n O'Donoghue\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g\",\"caption\":\"Ruadh\u00e1n O'Donoghue\"},\"description\":\"Web & mobile developer & consultant Editor & contributor at mobiForge Author of \\\"AMP: Building Accelerated Mobile Pages\\\" Runs westerntechnological.ie\",\"sameAs\":[\"http:\\\/\\\/ruadhan.com\",\"https:\\\/\\\/x.com\\\/rodono\"],\"url\":\"https:\\\/\\\/mobiforge.com\\\/author\\\/ruadhan\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"The Generic Sensor API - mobiForge","description":"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api","og_locale":"en_US","og_type":"article","og_title":"The Generic Sensor API - mobiForge","og_description":"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.","og_url":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api","og_site_name":"mobiForge","article_published_time":"2018-08-01T12:20:39+00:00","og_image":[{"width":1200,"height":890,"url":"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg","type":"image\/jpeg"}],"author":"Ruadh\u00e1n O'Donoghue","twitter_card":"summary_large_image","twitter_creator":"@rodono","twitter_site":"@mobiForge","twitter_misc":{"Written by":"Ruadh\u00e1n O'Donoghue","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#article","isPartOf":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api"},"author":{"name":"Ruadh\u00e1n O'Donoghue","@id":"https:\/\/mobiforge.com\/#\/schema\/person\/29eef51116f07b88e49e5cd6bd762522"},"headline":"The Generic Sensor API","datePublished":"2018-08-01T12:20:39+00:00","mainEntityOfPage":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api"},"wordCount":2692,"image":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#primaryimage"},"thumbnailUrl":"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg","keywords":["generic sensor api","HTML5","sensors"],"articleSection":["Design & Development","large-featured"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api","url":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api","name":"The Generic Sensor API - mobiForge","isPartOf":{"@id":"https:\/\/mobiforge.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#primaryimage"},"image":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#primaryimage"},"thumbnailUrl":"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg","datePublished":"2018-08-01T12:20:39+00:00","author":{"@id":"https:\/\/mobiforge.com\/#\/schema\/person\/29eef51116f07b88e49e5cd6bd762522"},"description":"The Generic Sensor API aims to make device sensors easier to use and extend by providing a set of consistent APIs to access sensors.","breadcrumb":{"@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#primaryimage","url":"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg","contentUrl":"https:\/\/mobiforge.com\/wp-content\/uploads\/2018\/07\/iphone-compass.jpg","width":1200,"height":890,"caption":"iphone compass"},{"@type":"BreadcrumbList","@id":"https:\/\/mobiforge.com\/design-development\/the-generic-sensor-api#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/mobiforge.com\/"},{"@type":"ListItem","position":2,"name":"The Generic Sensor API"}]},{"@type":"WebSite","@id":"https:\/\/mobiforge.com\/#website","url":"https:\/\/mobiforge.com\/","name":"mobiForge","description":"Mobile Web Development","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/mobiforge.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/mobiforge.com\/#\/schema\/person\/29eef51116f07b88e49e5cd6bd762522","name":"Ruadh\u00e1n O'Donoghue","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9310cc7f204a0d321336b563b79f7da00fb811ead577cbd50e22035899661faf?s=96&d=mm&r=g","caption":"Ruadh\u00e1n O'Donoghue"},"description":"Web & mobile developer & consultant Editor & contributor at mobiForge Author of \"AMP: Building Accelerated Mobile Pages\" Runs westerntechnological.ie","sameAs":["http:\/\/ruadhan.com","https:\/\/x.com\/rodono"],"url":"https:\/\/mobiforge.com\/author\/ruadhan"}]}},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/posts\/14491","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/comments?post=14491"}],"version-history":[{"count":24,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/posts\/14491\/revisions"}],"predecessor-version":[{"id":14615,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/posts\/14491\/revisions\/14615"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/media\/14497"}],"wp:attachment":[{"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/media?parent=14491"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/categories?post=14491"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mobiforge.com\/wp-json\/wp\/v2\/tags?post=14491"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}