Primitives for device sensors: accelerometer, gyroscope, compass, battery, and generic Sensor API.
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Sensors | 1.0.0-next.3 (next) | Aug 17, 2026 | Demo → |
npm i @solid-primitives/sensors@nextReactive primitives for device motion, orientation, and hardware sensors using standard browser APIs.
Accelerometer
Uses the DeviceMotionEvent API.
makeAccelerometer
Attaches a devicemotion event listener and calls onChange with the latest acceleration reading, throttled to at most once per interval milliseconds.
import { makeAccelerometer } from "@solid-primitives/sensors";
const cleanup = makeAccelerometer( acceleration => { console.log(acceleration?.x, acceleration?.y, acceleration?.z); }, { includeGravity: false, interval: 100 },);
// Later, stop listening:cleanup();Options:
| Option | Type | Default | Description |
|---|---|---|---|
includeGravity | boolean | false | When true, uses accelerationIncludingGravity instead of acceleration |
interval | number | 100 | Minimum milliseconds between onChange calls |
Returns: VoidFunction — call to remove the event listener.
createAccelerometer
Reactive wrapper around makeAccelerometer. Returns a signal accessor that starts as undefined and updates to the latest DeviceMotionEventAcceleration reading on each throttled event.
import { createAccelerometer } from "@solid-primitives/sensors";
function MyComponent() { const acceleration = createAccelerometer(); // acceleration() is AccelerometerReading | undefined
return ( <div> X: {acceleration()?.x ?? 0}, Y: {acceleration()?.y ?? 0}, Z: {acceleration()?.z ?? 0} </div> );}Signature:
function createAccelerometer( includeGravity?: boolean, // default: false interval?: number, // default: 100ms): Accessor<AccelerometerReading | undefined>;
type AccelerometerReading = DeviceMotionEventAcceleration | null;SSR: Returns () => ({ x: 0, y: 0, z: 0 }) on the server — no event listeners are attached.
Gyroscope
Uses the DeviceOrientationEvent API.
makeGyroscope
Attaches a deviceorientation event listener and calls onChange with the latest orientation reading, throttled to at most once per interval milliseconds. null orientation values (common on some platforms) are coerced to 0.
import { makeGyroscope } from "@solid-primitives/sensors";
const cleanup = makeGyroscope( orientation => { console.log(orientation.alpha, orientation.beta, orientation.gamma); }, { interval: 100 },);
// Later, stop listening:cleanup();Options:
| Option | Type | Default | Description |
|---|---|---|---|
interval | number | 100 | Minimum milliseconds between onChange calls |
Returns: VoidFunction — call to remove the event listener.
createGyroscope
Reactive wrapper around makeGyroscope. Returns an object with reactive alpha, beta, and gamma getters that start at 0 and update on each throttled orientation event.
import { createGyroscope } from "@solid-primitives/sensors";
function MyComponent() { const orientation = createGyroscope();
return ( <div> α: {orientation.alpha}°, β: {orientation.beta}°, γ: {orientation.gamma}° </div> );}Signature:
function createGyroscope( interval?: number, // default: 100ms): GyroscopeReading;
type GyroscopeReading = { alpha: number; beta: number; gamma: number };SSR: Returns { alpha: 0, beta: 0, gamma: 0 } — a plain non-reactive object.
Generic Sensor API
A factory pair for any Generic Sensor API sensor (Chromium-based browsers). Covers LinearAccelerationSensor, GravitySensor, AbsoluteOrientationSensor, RelativeOrientationSensor, and others.
makeSensor
Sets up any Generic Sensor API sensor and calls onChange with the live sensor object on each reading. Returns null if the sensor constructor throws (API unsupported or permission denied).
import { makeSensor } from "@solid-primitives/sensors";
const cleanup = makeSensor( LinearAccelerationSensor, sensor => console.log(sensor.x, sensor.y, sensor.z), { frequency: 60 },);
if (cleanup) { // Later, stop: cleanup();}Signature:
function makeSensor<T extends GenericSensor>( SensorClass: { new (options?: any): T }, onChange: (sensor: T) => void, options?: SensorOptions,): VoidFunction | null;
type SensorOptions = { frequency?: number };Returns: VoidFunction (cleanup) or null if unsupported.
createSensor
Reactive wrapper around makeSensor. Returns an accessor that updates on every reading event — even if the sensor object reference is the same — because the underlying signal uses equals: false. Returns undefined until the first reading or if the sensor is unavailable.
import { createSensor } from "@solid-primitives/sensors";
function MyComponent() { const sensor = createSensor(LinearAccelerationSensor, { frequency: 60 });
return ( <Show when={sensor()}> {s => <div>X: {s().x ?? 0}</div>} </Show> );}Signature:
function createSensor<T extends GenericSensor>( SensorClass: { new (options?: any): T }, options?: SensorOptions,): Accessor<T | undefined>;SSR: Returns () => undefined.
Compass
Uses window.Magnetometer from the Generic Sensor API. Chromium-based browsers only. Reports raw magnetic field strength in microteslas (µT) as { x, y, z } components.
makeCompass
import { makeCompass } from "@solid-primitives/sensors";
const cleanup = makeCompass(({ x, y, z }) => console.log(`Field: ${x}µT, ${y}µT, ${z}µT`), { frequency: 10, referenceFrame: "device",});
if (cleanup) cleanup();Options:
| Option | Type | Default | Description |
|---|---|---|---|
frequency | number | — | Readings per second |
referenceFrame | "device" | "screen" | "device" | Coordinate reference frame |
Returns: VoidFunction or null if window.Magnetometer is unavailable.
createCompass
Returns an object with reactive x, y, z getters (in µT), all starting at 0.
import { createCompass } from "@solid-primitives/sensors";
function Compass() { const mag = createCompass({ frequency: 10 }); const heading = () => Math.atan2(mag.y, mag.x) * (180 / Math.PI); return <div>Heading: {heading()}°</div>;}Signature:
function createCompass(options?: CompassOptions): CompassReading;
type CompassOptions = { frequency?: number; referenceFrame?: "device" | "screen" };type CompassReading = { x: number; y: number; z: number };SSR: Returns { x: 0, y: 0, z: 0 } — a plain non-reactive object.
Battery
Uses the Battery Status API (navigator.getBattery()). Supported in Chrome/Edge; not available in Firefox or Safari.
makeBattery
Subscribes to the Battery API and calls onChange immediately with the current reading, then again on every battery change event. Returns a synchronous cleanup function — safe to use with onCleanup even though the API initializes asynchronously.
import { makeBattery } from "@solid-primitives/sensors";
const cleanup = makeBattery(({ level, charging }) => { console.log(`${Math.round(level * 100)}% ${charging ? "charging" : "discharging"}`);});
// Later:cleanup();Signature:
function makeBattery(onChange: (reading: BatteryReading) => void): VoidFunction;
type BatteryReading = { charging: boolean; chargingTime: number; // seconds until full; Infinity if not charging dischargingTime: number; // seconds until empty; Infinity if charging level: number; // 0.0–1.0};Returns: VoidFunction — always (no-ops if the API is unavailable).
createBattery
Returns a reactive accessor for battery status. Starts as undefined until the Battery API resolves. Subscribe to any of the four properties to track specific changes.
import { createBattery } from "@solid-primitives/sensors";
function BatteryIndicator() { const battery = createBattery();
return ( <Show when={battery()} fallback={<span>Loading battery…</span>}> {b => ( <span> {Math.round(b().level * 100)}%{b().charging ? " ⚡" : ""} </span> )} </Show> );}Signature:
function createBattery(): Accessor<BatteryReading | undefined>;SSR: Returns () => ({ charging: false, chargingTime: 0, dischargingTime: 0, level: 1 }).
Throttling
Both makeAccelerometer and makeGyroscope throttle events using a leading-edge strategy: the first event in a burst fires onChange immediately; subsequent events within interval ms are dropped. The next event after the interval elapses fires again.
Set interval: 0 to disable throttling (useful in tests).
Generic Sensor API primitives (makeSensor, makeCompass) use the sensor's built-in frequency option for rate control — no additional throttling is applied.
Types
type AccelerometerReading = DeviceMotionEventAcceleration | null;type GyroscopeReading = { alpha: number; beta: number; gamma: number };type SensorOptions = { frequency?: number };type CompassOptions = { frequency?: number; referenceFrame?: "device" | "screen" };type CompassReading = { x: number; y: number; z: number };type BatteryReading = { charging: boolean; chargingTime: number; dischargingTime: number; level: number;};Changelog
See CHANGELOG.md