WebXR (VR/AR in the Browser) Implementation

When implementing WebXR development for VR in browser and AR in browser, developers often struggle with hit-testing and immersive-ar sessions. A user opens a page on an iPhone with Safari and sees a “View in AR” button—but the model doesn't load because WebXR AR isn't supported on iOS. On Android Ch

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1033
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1104
  • image_website-_0.webp
    Website development for Red Pear
    554

When implementing WebXR development for VR in browser and AR in browser, developers often struggle with hit-testing and immersive-ar sessions. A user opens a page on an iPhone with Safari and sees a “View in AR” button—but the model doesn't load because WebXR AR isn't supported on iOS. On Android Chrome, the same model works perfectly. This situation is a typical mistake when implementing immersive experiences: without accounting for platform differences, half the audience is left out. Over 5 years, we've completed more than 50 projects in this domain, building a library of standard solutions and optimizations. Our certified 3D engineers guarantee quality with 10+ years of experience. Cost estimates begin at $2,500 for basic implementations, with typical savings of 30% compared to native development.

Typical Problems When Implementing WebXR Development

The first mistake is ignoring platform differences. Android Chrome supports immersive-ar; iOS only supports AR Quick Look via USDZ. Without a fallback, iPhone users see a blank screen. The second is performance: heavy models with high-poly geometry cause lag on mobile, and improper LOD settings increase load times by 2–3 times. The third is the lack of hit-testing (placing objects on real surfaces), which degrades the AR experience. Saving time at this stage leads to up to 40% loss in engagement. Hit-testing accuracy exceeds 95% on supported devices. Contact us for a 3D model audit—we'll advise on optimizing it for mobile devices, reducing polygon count by 60% and texture size by 50%.

Ensuring Support on All Devices

We use a combined approach: detect the platform via user-agent, launch a WebXR AR session with hit-testing for Android, and show a USDZ link via AR Quick Look for iOS. For desktops and VR headsets, immersive-vr. Detection takes just a few lines, as shown below. It's recommended to also check support via navigator.xr.isSessionSupported before showing the interface. This ensures 95% device coverage.

const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) const isAndroid = /Android/.test(navigator.userAgent) if (isIOS) { document.getElementById('ar-link')!.style.display = 'block' // USDZ } else if (isAndroid && await navigator.xr?.isSessionSupported('immersive-ar')) { document.getElementById('ar-button')!.style.display = 'block' // WebXR AR } 

Why Three.js Is the Best Choice for WebXR Customization?

Three.js gives full control over rendering, animation, and controllers. It's 2–3 times faster than A-Frame for complex physics and large object counts. Hit-testing, reticles, and controller response are at the API level. A-Frame is good for quick prototypes, but for production with unique UX, we choose Three.js. An alternative is React Three Fiber, which provides a React wrapper over Three.js and simplifies integration into modern projects.

Three.js + WebXR

Three.js has built-in WebXR support:

import * as THREE from 'three' import { ARButton } from 'three/examples/jsm/webxr/ARButton' import { VRButton } from 'three/examples/jsm/webxr/VRButton' import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory' function WebXRScene({ mode }: { mode: 'ar' | 'vr' }) { const mountRef = useRef<HTMLDivElement>(null) useEffect(() => { const container = mountRef.current! const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) renderer.setPixelRatio(window.devicePixelRatio) renderer.setSize(container.clientWidth, container.clientHeight) renderer.xr.enabled = true container.appendChild(renderer.domElement) const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera(70, container.clientWidth / container.clientHeight, 0.01, 100) scene.add(new THREE.AmbientLight(0xffffff, 1)) const dirLight = new THREE.DirectionalLight(0xffffff, 2) dirLight.position.set(0, 5, 3) scene.add(dirLight) const button = mode === 'ar' ? ARButton.createButton(renderer, { requiredFeatures: ['hit-test'], optionalFeatures: ['dom-overlay'], domOverlay: { root: container }, }) : VRButton.createButton(renderer) document.body.appendChild(button) if (mode === 'vr') { const controllerModelFactory = new XRControllerModelFactory() for (let i = 0; i < 2; i++) { const controller = renderer.xr.getController(i) controller.addEventListener('selectstart', onSelectStart) controller.addEventListener('selectend', onSelectEnd) scene.add(controller) const controllerGrip = renderer.xr.getControllerGrip(i) controllerGrip.add(controllerModelFactory.createControllerModel(controllerGrip)) scene.add(controllerGrip) } } let hitTestSource: XRHitTestSource | null = null let hitTestSourceRequested = false const reticle = createReticle() scene.add(reticle) renderer.xr.addEventListener('sessionstart', async () => { if (mode !== 'ar') return const session = renderer.xr.getSession()! const viewerSpace = await session.requestReferenceSpace('viewer') hitTestSource = await session.requestHitTestSource!({ space: viewerSpace })! }) renderer.setAnimationLoop((timestamp, frame) => { if (mode === 'ar' && frame) { const referenceSpace = renderer.xr.getReferenceSpace()! const hitTestResults = frame.getHitTestResults(hitTestSource!) if (hitTestResults.length > 0) { const hit = hitTestResults[0] const pose = hit.getPose(referenceSpace) if (pose) { reticle.visible = true reticle.matrix.fromArray(pose.transform.matrix) } } else { reticle.visible = false } } renderer.render(scene, camera) }) function onSelectStart(event: THREE.Event) { if (reticle.visible) { const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1) const material = new THREE.MeshStandardMaterial({ color: 0x2563eb }) const mesh = new THREE.Mesh(geometry, material) mesh.position.setFromMatrixPosition(reticle.matrix) mesh.quaternion.setFromRotationMatrix(reticle.matrix) scene.add(mesh) } } return () => { renderer.setAnimationLoop(null) renderer.dispose() button.remove() container.removeChild(renderer.domElement) } }, [mode]) return ( <div ref={mountRef} style={{ width: '100%', height: '600px', position: 'relative' }} /> ) } function createReticle(): THREE.Mesh { const geometry = new THREE.RingGeometry(0.05, 0.07, 32).rotateX(-Math.PI / 2) const material = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide }) const reticle = new THREE.Mesh(geometry, material) reticle.matrixAutoUpdate = false reticle.visible = false return reticle } 

Comparison of Approaches for AR in the Browser

Parameter WebXR AR AR Quick Look A-Frame AR
iOS support No Yes No
Interactivity High (hit-test, controllers) Low (view only) Medium (limited)
Integration complexity Medium Low (link + USDZ) Low
Performance Depends on model Native Good
Customization Full Minimal Medium

Framework Comparison: Three.js vs A-Frame

Criteria Three.js A-Frame
Performance High (up to 60fps on mobile) Medium (up to 30fps on complex scenes)
Flexibility Full control over rendering Limited by declarative components
Learning curve Steep (requires 3D graphics knowledge) Gentle (HTML-like syntax)
WebXR support Full (controllers, hit-test) Basic (controllers via components)

iOS: AR Quick Look

Safari iOS doesn't support WebXR AR but supports AR Quick Look via USDZ files:

<a href="/models/product.usdz" rel="ar" id="ar-link"> <img src="/models/product-preview.jpg" alt="View in AR" /> <span>See in your space</span> </a> 

3D Model Conversion

AR Quick Look requires USDZ; WebXR requires GLTF. Conversion is done via Blender or command-line tools like usd-from-gltf. Quality loss is minimal (under 5%) with proper export settings. More details on formats in the official WebXR documentation.

Example: Model optimization before conversion For mobile devices, it's recommended to reduce polygon count to 50,000, use 1024x1024 textures, and merge materials. This reduces model size by 60% and speeds up loading by 40%.

What's Included in the Work

  • Analysis of target devices and platforms
  • 3D model preparation (mobile optimization, format conversion)
  • Implementation of WebXR AR/VR scene with hit-testing and controller support
  • Integration of iOS AR Quick Look with proper fallback
  • Testing on real devices (Android, iPhone, Quest)
  • Delivery of documentation and source code
  • Training your team on the solution

Development Stages

  1. Analysis – Determine use cases, choose stack (Three.js, A-Frame, React Three Fiber).
  2. Design – Interaction prototype, scene layout, budget estimation.
  3. Implementation – WebXR code, integration with your site, performance optimization.
  4. Testing – Test on headsets and phones, fix bugs, measure Core Web Vitals.
  5. Deployment – Set up HTTPS, publish models, use CDN for fast loading.

Approximate Timelines and Costs

  • AR product viewer (Android + iOS) – 5–7 days, from $2,500.
  • Interactive VR scene with controllers – 8–12 days, from $4,500.
  • Full custom cycle – from 14 days, from $7,000.

Cost is calculated individually. Our team guaranteed 10+ years of experience in 3D graphics and web development. Get a consultation on your project—we'll assess complexity and propose the optimal solution with a 95% device coverage rate.