loading
Warning:
Chromium only, over HTTPS The Generic Sensor API ships in Chromium-based browsers, mostly on mobile hardware. Support is per sensor, not per API - a browser can ship Accelerometer and not Magnetometer, and a device can ship neither. A Permissions-Policy on the document can also block a sensor, which arrives on the error callback rather than as an exception.
Note:
How this differs from DeviceOrientation The DeviceOrientation service wraps the legacy deviceorientation / devicemotion events. What these sensors add is an explicit frequency, a per-sensor permission model, an error channel, and hardware the legacy events never exposed: magnetometer, gravity and ambient light.

Per-sensor support and permission

IsSupported / QueryPermission

IsSupported reports whether the constructor exists. QueryPermission reports the permission state - it is a query only, since sensor permissions cannot be requested on their own; the prompt, where an engine has one, happens on the first Subscribe.

C#
@inject Bit.Butil.Sensors sensors

var supported = await sensors.IsSupported(SensorType.Accelerometer);
PermissionState state = await sensors.QueryPermission(SensorType.Accelerometer);
Live sample
support output
Results will appear here when you interact with the samples.

Read a sensor

Subscribe

Starts a sensor and calls back on every reading. The eight sensors report different quantities, so a reading carries all of them and leaves the ones this sensor does not produce as null. Frequency is a hint the platform is free to ignore; MinIntervalMs is the rate limit that actually holds, applied in JS so a 60 Hz sensor does not spend 60 interop round-trips a second. Dispose the subscription to stop - a running sensor keeps hardware awake.

C#
await using var subscription = await sensors.Subscribe(
    SensorType.Accelerometer,
    reading => Console.WriteLine($"{reading.X}, {reading.Y}, {reading.Z}"),
    new SensorOptions { Frequency = 10, MinIntervalMs = 100 },
    onError: message => Console.WriteLine(message));
Live sample
Sensor
Frequency (Hz, 0 = platform default)
Min interval (ms, 0 = every reading)
Reference frame (every sensor but ambient light)
reading output
Results will appear here when you interact with the samples.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported(SensorType type)
True when the runtime exposes the constructor behind the given sensor. Returns default (false) during prerender/SSR instead of throwing.
QueryPermission
ValueTask<PermissionState> QueryPermission(SensorType type)
The permission state - 'granted', 'denied' or 'prompt'. A query only; sensors that fuse several physical ones report the least-granted.
Subscribe
ValueTask<ButilSubscription> Subscribe(SensorType type, Action<SensorReading> onReading, SensorOptions? options = null, Action<string>? onError = null)
Starts a sensor and calls back on each reading. Dispose the subscription to stop it.
DisposeAsync
ValueTask DisposeAsync()
Stops every sensor this instance started and releases the JS callback reference.
InvokeSensorReading
void InvokeSensorReading(Guid id, SensorReading reading)
JSInvokable interop plumbing for readings - not intended for app code.
InvokeSensorError
void InvokeSensorError(Guid id, string message)
JSInvokable interop plumbing for sensor errors - not intended for app code.
SensorType
enum SensorType { Accelerometer, Gyroscope, Magnetometer, AbsoluteOrientation, RelativeOrientation, Gravity, LinearAcceleration, AmbientLight }
Which of the eight Generic Sensor classes to use.
SensorOptions
class SensorOptions { double? Frequency; SensorReferenceFrame? ReferenceFrame; int MinIntervalMs = 100; }
Sample rate, the axes a spatial sensor reports against (every type but AmbientLight), and the shortest gap between two readings reaching .NET. Frequency is only a hint the platform may ignore; MinIntervalMs is the cap that holds, applied in JS before the interop round-trip is paid for. 0 forwards every reading.
SensorReferenceFrame
enum SensorReferenceFrame { Device, Screen }
Device axes ignore screen rotation; screen axes follow it. Taken by every sensor but AmbientLight.
SensorReading
class SensorReading { string Type; double Timestamp; double? X; double? Y; double? Z; double[]? Quaternion; double? Illuminance; }
One reading. Vector sensors fill X/Y/Z, orientation sensors fill Quaternion, the light sensor fills Illuminance.
An unhandled error has occurred. Reload 🗙