loading
Note:
A phone or tablet, please These events only fire on hardware with the matching sensors. A desktop browser reports the events as supported and then never sends one, so IsOrientationSupported tells you the API exists, not that readings are coming. Open this page on a phone to see it work.

Support and permission

IsOrientationSupported / IsMotionSupported / NeedsPermission / RequestPermission

iOS gates both streams behind an explicit grant that must be asked for from a click handler and is not remembered across page loads. Everywhere else there is no gate, and RequestPermission returns Granted immediately - so it is always safe to call, and NeedsPermission tells you whether a button is worth showing.

C#
@inject Bit.Butil.DeviceOrientation deviceOrientation

var hasOrientation = await deviceOrientation.IsOrientationSupported();
var hasMotion = await deviceOrientation.IsMotionSupported();
var gated = await deviceOrientation.NeedsPermission();   // true on iOS

// must run inside a click handler:
var permission = await deviceOrientation.RequestPermission();
if (permission == DeviceSensorPermission.Granted)
{
    // safe to subscribe
}
Live sample
Permission Not requested.
support check output
Results will appear here when you interact with the samples.

Tilt

SubscribeOrientation

Alpha is rotation about the vertical axis, beta the front-to-back tilt (0 flat, 90 upright) and gamma the left-to-right tilt. With absolute: true it prefers the deviceorientationabsolute event, whose alpha is a real compass heading rather than being relative to wherever the device happened to be pointing when the listener attached - the Absolute flag on each reading tells you which one you actually got.

C#
private ButilSubscription? _tilt;

_tilt = await deviceOrientation.SubscribeOrientation(
    reading => InvokeAsync(() =>
    {
        _reading = reading;   // Alpha / Beta / Gamma in degrees, Absolute flag
        StateHasChanged();
    }),
    absolute: true,
    minIntervalMs: 100);   // these events fire tens of times a second

// later:
await _tilt.DisposeAsync();
Live sample
orientation output
Results will appear here when you interact with the samples.

Motion

SubscribeMotion

Acceleration in m/s², with and without gravity, plus the rotation rate in degrees per second. A device lying flat reports about 9.8 on AccelerationIncludingGravityZ and roughly nothing on the gravity-free members - the difference between the two is what a shake detector keys off. Interval is the sensor's own refresh rate, not the rate readings reach your handler.

C#
_motion = await deviceOrientation.SubscribeMotion(
    reading => InvokeAsync(() =>
    {
        // a crude shake detector:
        var magnitude = Math.Sqrt(
            reading.AccelerationX * reading.AccelerationX +
            reading.AccelerationY * reading.AccelerationY +
            reading.AccelerationZ * reading.AccelerationZ);

        if (magnitude > 15) OnShake();
        StateHasChanged();
    }),
    minIntervalMs: 100);
Live sample
motion output
Results will appear here when you interact with the samples.
Warning:
Throttle, or drown Both streams fire tens of times a second. Every subscription is throttled in JavaScript before anything crosses into .NET - minIntervalMs defaults to 100, which is plenty for a UI. Passing 0 forwards every event and will keep a Blazor render loop permanently busy.

API reference

Member
Signature
Description
IsOrientationSupported
ValueTask<bool> IsOrientationSupported()
True when the runtime exposes DeviceOrientationEvent. The class existing does not mean the hardware does.
IsMotionSupported
ValueTask<bool> IsMotionSupported()
True when the runtime exposes DeviceMotionEvent. Same caveat as above.
NeedsPermission
ValueTask<bool> NeedsPermission()
True on engines that gate these events behind an explicit grant - in practice Safari on iOS.
RequestPermission
ValueTask<DeviceSensorPermission> RequestPermission()
Asks for access. Granted immediately where there is no gate. Must be called from a user gesture; a call outside one is reported as Denied rather than throwing.
SubscribeOrientation
ValueTask<ButilSubscription> SubscribeOrientation(Action<DeviceOrientationReading> handler, bool absolute = false, int minIntervalMs = 100)
Subscribes to tilt readings. absolute prefers the compass-relative event where it exists.
SubscribeMotion
ValueTask<ButilSubscription> SubscribeMotion(Action<DeviceMotionReading> handler, int minIntervalMs = 100)
Subscribes to acceleration and rotation-rate readings.
DisposeAsync
ValueTask DisposeAsync()
On scope/circuit teardown, detaches any listener whose subscription was never disposed.
DeviceOrientationReading
Alpha, Beta, Gamma, Absolute
Tilt in degrees. Absolute tells you whether Alpha is a compass heading.
DeviceMotionReading
Acceleration{X,Y,Z}, AccelerationIncludingGravity{X,Y,Z}, Rotation{Alpha,Beta,Gamma}, Interval
Acceleration in m/s² and rotation rate in °/s. Missing values arrive as 0.
An unhandled error has occurred. Reload 🗙