Когда реальность становится частью браузера: глубоко про WebXR и AR
Если кто-то вам скажет, что дополненная реальность — это сложно, он прав. Но если он скажет, что это невозможно в браузере — он отстал от жизни. WebXR API делает реальное расширяемым буквально в два клика, а иногда и в один — если повезёт с железом, браузером и звёздами на небе.

Это не просто «вставили коробку в A-Frame». Это статья для тех, кто хочет разобраться, как WebXR работает по-настоящему. Без магии, без примочек, с низкоуровневым контролем и болью от несовместимости устройств. Но с наградой в виде реально работающего AR прямо в Chrome на Android.
Что такое WebXR
WebXR Device API — это стандарт от W3C, предназначенный для взаимодействия с XR-устройствами (VR и AR) из браузера. Он пришёл на смену WebVR и теперь охватывает больше — включая дополненную реальность.
AR-режим в WebXR инициируется сессией типа immersive-ar, при которой браузер запускает камеру и позволяет проецировать объекты в реальный мир с учётом движений и положения устройства.
Первые грабли: поддержка и окружение
На момент написания статьи, поддержка AR через WebXR ограничена:
Chrome на Android с флагом #webxr-incubations включённым
Ограниченный список устройств, поддерживающих ARCore
Нет поддержки на iOS вообще (Safari и WebXR пока не дружат)
Проверить поддержку можно здесь: https://immersive-web.github.io/webxr-samples/
Минимальный AR с нуля (без A-Frame)
Давайте сразу к мясу. Пример ниже — чистый JavaScript + WebGL, никакой магии:
<!-- HTML -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebXR AR Minimal</title>
</head>
<body>
<canvas id="xr-canvas"></canvas>
<script type="module" src="main.js"></script>
</body>
</html>
// JavaScript (main.js)
const canvas = document.getElementById('xr-canvas');
const gl = canvas.getContext('webgl', { xrCompatible: true });
let xrSession = null;
let xrRefSpace = null;
let xrFrame = null;
navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local-floor']
}).then(session => {
xrSession = session;
session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
session.requestReferenceSpace('local-floor').then(refSpace => {
xrRefSpace = refSpace;
session.requestAnimationFrame(onXRFrame);
});
});
function onXRFrame(time, frame) {
let session = frame.session;
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(xrRefSpace);
gl.bindFramebuffer(gl.FRAMEBUFFER, session.renderState.baseLayer.framebuffer);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if (pose) {
for (const view of pose.views) {
const viewport = session.renderState.baseLayer.getViewport(view);
gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
// тут можно отрисовать 3D-объекты, мы пока оставим пусто
}
}
}
Да, выглядит просто. Но если запустить — камера включится, и всё, что ты нарисуешь через WebGL, будет «парить» в реальном мире.
Работа с 3D-объектами: подключаем Three.js
Реалистично делать AR на WebGL вручную — удовольствие ниже среднего. Поэтому подключим Three.js, который умеет работать с WebXR через WebXRManager.
// JavaScript + Three.js
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 20);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.xr.enabled = true;
document.body.appendChild(ARButton.createButton(renderer, { requiredFeatures: ['hit-test'] }));
const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
scene.add(light);
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, -0.5);
scene.add(cube);
function animate() {
renderer.setAnimationLoop(() => {
cube.rotation.y += 0.01;
renderer.render(scene, camera);
});
}
animate();
Не забудьте подключить ARButton
из three/examples/jsm/webxr/ARButton.js
— он отвечает за инициацию XR-сессии и автоматом проверяет поддержку.
Взаимодействие: тап, гест, тык
Чтобы взаимодействовать с объектами, можно использовать select событие сессии:
renderer.xr.getSession().addEventListener('select', () => {
cube.material.color.set(Math.random() * 0xffffff);
});
Да, всё просто — пользователь «тапает» в пространство, и происходит событие.
UX-ад: как всё сломать красиво
Не рисуйте текст в AR — он не читается.
Избегайте малых объектов — они теряются в реальности.
Не полагайтесь на точность hit-test — поверхность может не определиться вообще.
Тестируйте на реальных людях — многие не понимают, что они должны делать.
Заключение
WebXR — это тот редкий случай, когда браузер становится больше, чем просто окно в интернет. Он становится порталом в AR. Но эта магия требует нервов, экспериментов и постоянной отладки. Оно того стоит — особенно, когда видишь, как твой 3D-объект вписывается в комнату пользователя.
Если вы готовы к приключениям и не боитесь нестабильности — WebXR ждёт.