2026年8月2日

2026年8月2日

WordPressでGeneric Sensor APIを使いスマホのセンサーデータを取得する実装

Generic Sensor APIを使うと、スマートフォンの加速度センサーやジャイロスコープの値をJavaScriptで直接読み取れます。ただし対応しているのはChromeなどChromium系のブラウザに限られ、iPhoneのSafariでは使えません。この記事では、WordPressの固定ページにショートコードでデモを置き、Generic Sensor APIで加速度を取得しつつ、未対応のブラウザでは devicemotion イベントに切り替える実装を、HTTPS・権限・プライバシーの注意点とあわせて解説します。

Generic Sensor APIの基本と対応状況

Generic Sensor APIは、センサーごとに用意されたクラスを同じ書き方で扱えるようにした仕様です。主なクラスは次のとおりです。

  • Accelerometer: 端末にかかる加速度(重力を含む、単位はm/s²)
  • LinearAccelerationSensor: 重力を除いた加速度
  • GravitySensor: 重力の向きと大きさ
  • Gyroscope: 各軸まわりの回転の速さ(rad/s)
  • AbsoluteOrientationSensor / RelativeOrientationSensor: 端末の向き(四元数)

どのクラスも、new で作って start() を呼び、reading イベントで x・y・z などの値を受け取り、不要になったら stop() を呼ぶ、という流れです。

対応ブラウザは、Android版ChromeやEdgeなどのChromium系が中心で、SafariとFirefoxは対応していません(2026年10月現在)。磁気センサーや照度センサーは、Chromium系でも初期設定では使えない扱いです。そのため、実際のサイトに組み込むなら、Generic Sensor APIを使える環境では使い、使えない環境では以前からある DeviceMotionEvent に切り替える二段構えが現実的です。対応状況は変わるので、公開前にMDNやCan I useで最新の情報を確認してください。

動作の前提: HTTPS・Permissions Policy・権限

センサーが動かないときは、たいてい次の3点のどれかが原因です。

  1. HTTPSで配信されていない: センサー系のAPIは安全なコンテキストでしか使えません。本番サイトがHTTPSでも、開発用の http://192.168.x.x などで開くと使えません。パソコン上の http://localhost は例外的に安全とみなされますが、スマホ実機で試すときはHTTPSの検証環境を用意します。
  2. Permissions Policyで禁止されている: セキュリティ用のプラグインやサーバー設定で Permissions-Policy: accelerometer=(), gyroscope=() のようなヘッダーを送っていると、そのサイトではセンサーが使えません。開発者ツールのネットワークタブでHTMLのレスポンスヘッダーを確認してください。iframeの中で使う場合は、<iframe allow="accelerometer; gyroscope"> のように親ページ側での許可も要ります。
  3. ユーザーやブラウザの設定で拒否されている: Chromeではサイトの設定に「モーションセンサー」の項目があり、ユーザーがブロックできます(表記はバージョンで変わることがあります)。

子テーマでショートコードとスクリプトを用意する

デモを置きたいページにショートコード [sensor_demo] を書くと、ボタンと表示欄が出て、そのページでだけスクリプトを読み込むようにします。作業は子テーマの functions.php で行い、事前にバックアップを取ってください。

<?php
// 子テーマの functions.php
add_shortcode( 'sensor_demo', function () {
	$path = get_stylesheet_directory() . '/js/sensor-demo.js';
	wp_enqueue_script(
		'my-sensor-demo',
		get_stylesheet_directory_uri() . '/js/sensor-demo.js',
		array(),
		file_exists( $path ) ? filemtime( $path ) : null,
		array(
			'strategy'  => 'defer',
			'in_footer' => true,
		)
	);

	ob_start();
	?>
	<div class="sensor-demo">
		<button type="button" class="sensor-demo__start"><?php echo esc_html__( 'センサーを開始', 'my-child' ); ?></button>
		<p class="sensor-demo__status" aria-live="polite"></p>
		<pre class="sensor-demo__values">x: -  y: -  z: -</pre>
	</div>
	<?php
	return ob_get_clean();
} );

ショートコードは文字列を返す必要があるので、ob_start() と ob_get_clean() でHTMLをまとめて返しています。ショートコードの中で wp_enqueue_script() を呼んだ場合、スクリプトはフッター側に出力されます。そのため in_footer を有効にしています。

Generic Sensor APIで加速度を取得するJavaScript

センサーの開始はボタンを押したときに限定します。ページを開いただけで計測を始めないことは、利用者への配慮になるうえ、iPhoneの devicemotion の許可ダイアログはユーザー操作の中でしか出せないという制約にも合っています。

// 子テーマの js/sensor-demo.js
( function () {
	const root = document.querySelector( '.sensor-demo' );
	if ( ! root ) {
		return;
	}
	const button = root.querySelector( '.sensor-demo__start' );
	const status = root.querySelector( '.sensor-demo__status' );
	const out = root.querySelector( '.sensor-demo__values' );
	let latest = null;
	let frame = 0;
	let sensor = null;

	// 値の表示は描画のタイミングに合わせて間引く
	function show( x, y, z ) {
		latest = { x, y, z };
		if ( frame ) {
			return;
		}
		frame = requestAnimationFrame( () => {
			frame = 0;
			const f = ( v ) => ( typeof v === 'number' ? v.toFixed( 2 ) : '-' );
			out.textContent = `x: ${ f( latest.x ) }  y: ${ f( latest.y ) }  z: ${ f( latest.z ) }`;
		} );
	}

	function startGenericSensor() {
		try {
			sensor = new Accelerometer( { frequency: 30 } );
		} catch ( err ) {
			// Permissions Policy で禁止されていると SecurityError になる
			status.textContent = 'センサーを利用できません: ' + err.name;
			return false;
		}
		sensor.addEventListener( 'reading', () => show( sensor.x, sensor.y, sensor.z ) );
		sensor.addEventListener( 'error', ( event ) => {
			const name = event.error.name;
			if ( name === 'NotAllowedError' ) {
				status.textContent = 'センサーの利用が許可されていません。';
			} else if ( name === 'NotReadableError' ) {
				status.textContent = 'この端末ではセンサーを読み取れません。';
			} else {
				status.textContent = 'エラー: ' + name;
			}
		} );
		sensor.start();
		status.textContent = 'Generic Sensor API で取得中';
		return true;
	}

	async function startDeviceMotion() {
		// iOS Safari では許可を求める関数がある
		if ( typeof DeviceMotionEvent !== 'undefined' &&
			typeof DeviceMotionEvent.requestPermission === 'function' ) {
			const result = await DeviceMotionEvent.requestPermission();
			if ( result !== 'granted' ) {
				status.textContent = 'モーションの利用が許可されませんでした。';
				return;
			}
		}
		window.addEventListener( 'devicemotion', ( e ) => {
			const a = e.accelerationIncludingGravity;
			if ( a ) {
				show( a.x, a.y, a.z );
			}
		} );
		status.textContent = 'devicemotion で取得中';
	}

	button.addEventListener( 'click', async () => {
		button.disabled = true;
		if ( 'Accelerometer' in window && startGenericSensor() ) {
			return;
		}
		if ( 'DeviceMotionEvent' in window ) {
			try {
				await startDeviceMotion();
			} catch ( err ) {
				status.textContent = '開始できませんでした: ' + err.name;
			}
			return;
		}
		status.textContent = 'このブラウザはモーションセンサーに対応していません。';
	} );

	// タブが裏に回ったらセンサーを止め、戻ったら再開する
	document.addEventListener( 'visibilitychange', () => {
		if ( ! sensor ) {
			return;
		}
		if ( document.hidden ) {
			sensor.stop();
		} else {
			sensor.start();
		}
	} );
} )();

ポイントをいくつか補足します。

  • **new Accelerometer() は try で囲む**: Permissions Policyで禁止されているとコンストラクタの時点で例外が出ます。権限の拒否やハードウェアの問題は、start() の後に error イベントとして届きます。両方を受け止めないと、画面上は何も起きないまま止まって見えます。
  • **frequency は控えめに**: 1秒あたりの読み取り回数の希望値です。端末が対応する範囲に丸められるため、指定どおりになるとは限りません。値を大きくすると電池の消費が増えます。
  • **表示は textContent で**: センサーの値は数値ですが、エラーメッセージなどを innerHTML で差し込む書き方は避け、文字列として表示します。
  • 座標軸の違い: devicemotion の値は端末やブラウザによって軸の向きや符号の扱いが異なる場合があります。両方の経路で同じ見え方になるか、実機で確かめてください。

センサーの値を使って要素を動かす演出を加える場合は、prefers-reduced-motion: reduce が指定された環境では動きを抑え、数値の表示だけにする配慮も入れておくと安心です。

実機での確認方法

Android端末のChromeで試すときは、パソコンとUSBで接続し、パソコン側のChromeで chrome://inspect を開くと、スマホで表示中のページのコンソールを確認できます。エラー名がそのまま見えるので、原因の切り分けが速くなります。パソコンのChromeの開発者ツールにも、端末の向きなどを擬似的に変えるセンサーのエミュレーション機能がありますが、扱えるセンサーの種類は限られるため、最終確認は実機で行ってください。

プライバシーとデータの扱い

加速度やジャイロの細かな値は、端末の識別(フィンガープリント)や、画面のどこを触っているかの推測に悪用されうる情報として知られています。サイトでセンサーを使う場合は、次の点を守ると利用者に安心してもらえます。

  • 計測はユーザーがボタンを押してから始め、用途を画面上で説明する
  • 必要な間だけ動かし、ページを離れたりタブが裏に回ったりしたら止める
  • 生の値をサーバーに送らない。送る必要がある場合は集計した最小限の値にし、REST APIの受け口にはnonceと権限確認、値の検証を入れる
  • 収集する場合はプライバシーポリシーに目的と内容を記載する

ゲームや体験型のコンテンツでは楽しい機能ですが、多くの記事ページには不要です。使うページを限定し、ショートコードを置いたページでだけスクリプトを読み込む今回の形にしておくと、他のページの表示速度にも影響しません。

まとめ

  • Generic Sensor APIは Accelerometer などのクラスを new して start() し、reading イベントで値を受け取ります。
  • 対応はChromium系が中心で、SafariとFirefoxは非対応です。devicemotion への切り替えを用意し、iPhoneではユーザー操作の中で許可を求めます。
  • HTTPS、Permissions Policyのヘッダー、ブラウザ側の許可の3点が、動かないときの主な確認箇所です。
  • コンストラクタの例外と error イベントの両方を受け止め、利用者に状態を表示します。
  • センサーの値は個人の識別に使われうる情報なので、必要な間だけ動かし、生の値を送らない設計にします。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲