The @capgo/capacitor-accelerometer package provides access to device accelerometer sensors, allowing you to read acceleration forces and detect device motion in your Capacitor applications. This tutorial will guide you through installation, configuration, and practical usage including shake detection, tilt sensing, and step counting.
Install the package using your preferred package manager:
npm install @capgo/capacitor-accelerometer
npx cap sync
No additional configuration required for iOS and Android.
import { Accelerometer } from '@capgo/capacitor-accelerometer';
async function startAccelerometer() {
try {
await Accelerometer.start({
interval: 100 // Update interval in milliseconds
});
console.log('Accelerometer started');
} catch (error) {
console.error('Failed to start accelerometer:', error);
}
}
function setupAccelerationListener() {
Accelerometer.addListener('accelerationChange', (data) => {
console.log('X-axis:', data.x, 'm/s²');
console.log('Y-axis:', data.y, 'm/s²');
console.log('Z-axis:', data.z, 'm/s²');
console.log('Timestamp:', data.timestamp);
updateDisplay(data);
});
}
function updateDisplay(data: any) {
document.getElementById('x-axis').textContent = data.x.toFixed(2);
document.getElementById('y-axis').textContent = data.y.toFixed(2);
document.getElementById('z-axis').textContent = data.z.toFixed(2);
}
async function getCurrentAcceleration() {
try {
const reading = await Accelerometer.getCurrentAcceleration();
console.log('Current acceleration:');
console.log('X:', reading.x);
console.log('Y:', reading.y);
console.log('Z:', reading.z);
return reading;
} catch (error) {
console.error('Failed to get acceleration:', error);
return null;
}
}
async function stopAccelerometer() {
try {
await Accelerometer.stop();
console.log('Accelerometer stopped');
} catch (error) {
console.error('Failed to stop accelerometer:', error);
}
}
When device is at rest:
Here's a comprehensive service for managing accelerometer functionality:
import { Accelerometer } from '@capgo/capacitor-accelerometer';
export interface AccelerationData {
x: number;
y: number;
z: number;
timestamp: number;
}
export class AccelerometerService {
private listener: any = null;
private isMonitoring = false;
private callbacks: Map<string, Function> = new Map();
async startMonitoring(interval: number = 100): Promise<void> {
if (this.isMonitoring) {
console.log('Already monitoring');
return;
}
await Accelerometer.start({ interval });
this.listener = Accelerometer.addListener('accelerationChange', (data) => {
this.handleAcceleration(data);
});
this.isMonitoring = true;
console.log('Accelerometer monitoring started');
}
async stopMonitoring(): Promise<void> {
if (!this.isMonitoring) {
return;
}
if (this.listener) {
this.listener.remove();
this.listener = null;
}
await Accelerometer.stop();
this.isMonitoring = false;
console.log('Accelerometer monitoring stopped');
}
private handleAcceleration(data: AccelerationData): void {
this.callbacks.forEach((callback) => {
callback(data);
});
}
subscribe(key: string, callback: Function): void {
this.callbacks.set(key, callback);
}
unsubscribe(key: string): void {
this.callbacks.delete(key);
}
calculateMagnitude(data: AccelerationData): number {
return Math.sqrt(
data.x * data.x +
data.y * data.y +
data.z * data.z
);
}
isMonitoringActive(): boolean {
return this.isMonitoring;
}
}
Implement shake gesture detection:
class ShakeDetector {
private accelerometer: AccelerometerService;
private lastUpdate = 0;
private lastX = 0;
private lastY = 0;
private lastZ = 0;
private shakeThreshold = 15;
private shakeInterval = 100; // ms
private onShakeCallback: Function | null = null;
constructor() {
this.accelerometer = new AccelerometerService();
}
async start(onShake: Function) {
this.onShakeCallback = onShake;
await this.accelerometer.startMonitoring(this.shakeInterval);
this.accelerometer.subscribe('shake', (data: AccelerationData) => {
this.detectShake(data);
});
}
async stop() {
this.accelerometer.unsubscribe('shake');
await this.accelerometer.stopMonitoring();
}
private detectShake(data: AccelerationData): void {
const currentTime = Date.now();
if (currentTime - this.lastUpdate > this.shakeInterval) {
const deltaX = Math.abs(data.x - this.lastX);
const deltaY = Math.abs(data.y - this.lastY);
const deltaZ = Math.abs(data.z - this.lastZ);
if (deltaX > this.shakeThreshold ||
deltaY > this.shakeThreshold ||
deltaZ > this.shakeThreshold) {
console.log('Shake detected!');
if (this.onShakeCallback) {
this.onShakeCallback();
}
}
this.lastUpdate = currentTime;
this.lastX = data.x;
this.lastY = data.y;
this.lastZ = data.z;
}
}
setThreshold(threshold: number) {
this.shakeThreshold = threshold;
}
}
// Usage
const shakeDetector = new ShakeDetector();
shakeDetector.start(() => {
console.log('User shook the device!');
// Trigger shake action
});
Detect device orientation and tilt angles:
class TiltDetector {
private accelerometer: AccelerometerService;
constructor() {
this.accelerometer = new AccelerometerService();
}
async start(onTilt: Function) {
await this.accelerometer.startMonitoring(200);
this.accelerometer.subscribe('tilt', (data: AccelerationData) => {
const tiltInfo = this.analyzeTilt(data);
onTilt(tiltInfo);
});
}
async stop() {
this.accelerometer.unsubscribe('tilt');
await this.accelerometer.stopMonitoring();
}
private analyzeTilt(data: AccelerationData) {
// Calculate tilt angles
const roll = Math.atan2(data.y, data.z) * (180 / Math.PI);
const pitch = Math.atan2(-data.x, Math.sqrt(data.y * data.y + data.z * data.z)) * (180 / Math.PI);
// Determine orientation
const orientation = this.getOrientation(data);
return {
roll: roll.toFixed(1),
pitch: pitch.toFixed(1),
orientation: orientation,
isFlat: this.isDeviceFlat(data),
isUpright: this.isDeviceUpright(data)
};
}
private getOrientation(data: AccelerationData): string {
const absX = Math.abs(data.x);
const absY = Math.abs(data.y);
const absZ = Math.abs(data.z);
if (absZ > absX && absZ > absY) {
return data.z > 0 ? 'face-up' : 'face-down';
} else if (absY > absX && absY > absZ) {
return data.y > 0 ? 'portrait' : 'portrait-upside-down';
} else {
return data.x > 0 ? 'landscape-right' : 'landscape-left';
}
}
private isDeviceFlat(data: AccelerationData): boolean {
return Math.abs(data.z - 9.8) < 2.0;
}
private isDeviceUpright(data: AccelerationData): boolean {
return Math.abs(data.y - 9.8) < 2.0;
}
}
// Usage
const tiltDetector = new TiltDetector();
tiltDetector.start((tiltInfo) => {
console.log('Roll:', tiltInfo.roll);
console.log('Pitch:', tiltInfo.pitch);
console.log('Orientation:', tiltInfo.orientation);
});
Implement a basic step counter:
class StepCounter {
private accelerometer: AccelerometerService;
private steps = 0;
private lastMagnitude = 0;
private threshold = 11;
private minStepInterval = 300; // ms
private lastStepTime = 0;
constructor() {
this.accelerometer = new AccelerometerService();
}
async start(onStep: Function) {
await this.accelerometer.startMonitoring(50);
this.accelerometer.subscribe('steps', (data: AccelerationData) => {
this.detectStep(data, onStep);
});
}
async stop() {
this.accelerometer.unsubscribe('steps');
await this.accelerometer.stopMonitoring();
}
private detectStep(data: AccelerationData, onStep: Function): void {
const magnitude = this.accelerometer.calculateMagnitude(data);
const currentTime = Date.now();
// Detect peak
if (magnitude > this.threshold &&
this.lastMagnitude < this.threshold &&
currentTime - this.lastStepTime > this.minStepInterval) {
this.steps++;
this.lastStepTime = currentTime;
console.log('Step detected! Total steps:', this.steps);
onStep(this.steps);
}
this.lastMagnitude = magnitude;
}
getSteps(): number {
return this.steps;
}
resetSteps(): void {
this.steps = 0;
}
setThreshold(threshold: number) {
this.threshold = threshold;
}
}
// Usage
const stepCounter = new StepCounter();
stepCounter.start((steps: number) => {
console.log('Steps:', steps);
document.getElementById('step-count').textContent = steps.toString();
});
Detect different types of motion:
class MotionDetector {
private accelerometer: AccelerometerService;
private motionBuffer: number[] = [];
private bufferSize = 20;
constructor() {
this.accelerometer = new AccelerometerService();
}
async start(onMotionChange: Function) {
await this.accelerometer.startMonitoring(100);
this.accelerometer.subscribe('motion', (data: AccelerationData) => {
const activity = this.analyzeMotion(data);
onMotionChange(activity);
});
}
async stop() {
this.accelerometer.unsubscribe('motion');
await this.accelerometer.stopMonitoring();
}
private analyzeMotion(data: AccelerationData): string {
const magnitude = this.accelerometer.calculateMagnitude(data);
this.motionBuffer.push(magnitude);
if (this.motionBuffer.length > this.bufferSize) {
this.motionBuffer.shift();
}
if (this.motionBuffer.length < this.bufferSize) {
return 'unknown';
}
const variance = this.calculateVariance(this.motionBuffer);
const mean = this.calculateMean(this.motionBuffer);
// Classify activity
if (variance < 0.5) {
return 'stationary';
} else if (variance < 2.0 && mean < 11) {
return 'walking';
} else if (variance > 2.0 && mean < 13) {
return 'running';
} else if (variance > 5.0) {
return 'intense-activity';
} else {
return 'moving';
}
}
private calculateMean(values: number[]): number {
const sum = values.reduce((acc, val) => acc + val, 0);
return sum / values.length;
}
private calculateVariance(values: number[]): number {
const mean = this.calculateMean(values);
const squaredDiffs = values.map(value => Math.pow(value - mean, 2));
return this.calculateMean(squaredDiffs);
}
}
// Usage
const motionDetector = new MotionDetector();
motionDetector.start((activity: string) => {
console.log('Current activity:', activity);
document.getElementById('activity').textContent = activity;
});
Smooth accelerometer data to reduce noise:
class AccelerometerFilter {
private alpha = 0.8; // Low-pass filter coefficient
private filteredX = 0;
private filteredY = 0;
private filteredZ = 0;
filter(data: AccelerationData): AccelerationData {
// Low-pass filter
this.filteredX = this.alpha * data.x + (1 - this.alpha) * this.filteredX;
this.filteredY = this.alpha * data.y + (1 - this.alpha) * this.filteredY;
this.filteredZ = this.alpha * data.z + (1 - this.alpha) * this.filteredZ;
return {
x: this.filteredX,
y: this.filteredY,
z: this.filteredZ,
timestamp: data.timestamp
};
}
// Remove gravity component
removeGravity(data: AccelerationData): AccelerationData {
const gravityX = this.alpha * this.filteredX;
const gravityY = this.alpha * this.filteredY;
const gravityZ = this.alpha * this.filteredZ;
return {
x: data.x - gravityX,
y: data.y - gravityY,
z: data.z - gravityZ,
timestamp: data.timestamp
};
}
}
Choose Appropriate Intervals:
Battery Optimization: Higher frequencies drain battery faster
Data Filtering: Use low-pass filters to reduce noise
Remove Listeners: Always clean up when done
Test on Devices: Simulators don't provide realistic data
Consider Context: Combine with other sensors for better accuracy
const filter = new AccelerometerFilter();
Accelerometer.addListener('accelerationChange', (data) => {
const filtered = filter.filter(data);
// Use filtered data
});
// Use longer intervals when precision isn't critical
await Accelerometer.start({ interval: 500 }); // Instead of 100ms
// Increase threshold or add cooldown period
shakeDetector.setThreshold(20); // Higher threshold
The @capgo/capacitor-accelerometer plugin provides powerful motion sensing capabilities for iOS, Android, and Web platforms. By properly implementing accelerometer monitoring, you can create engaging gesture-based interfaces, fitness tracking, and motion-aware applications.
For more information, visit the official documentation or check the GitHub repository.