What “web AR” actually is
You don't need an app. Google's free <model-viewer> web component loads a 3D model file and renders it as a draggable object. On AR-capable phones it adds a “View in your space” button that places the model in the camera view at real-world scale.
A 3D model file
Usually .glb (or .gltf) — a compact, web-friendly 3D format.
One web component
Add a script, then use <model-viewer> like any HTML tag.
AR on phones
iOS (Quick Look) and Android (Scene Viewer) handle the camera — no app install.
3D on desktop
No AR hardware? You still get a fully rotatable model.
Add the model-viewer library
Drop this <script> once, near the top of your page (it loads as a module). That's the only dependency.
HTML — in your <head>
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"> </script>
<model-viewer> anywhere in your HTML — no build step, no framework.Show a 3D model (live)
Point src at a .glb file and add camera-controls so visitors can drag to rotate and scroll to zoom. Try it — drag the model below.
HTML
<model-viewer src="astronaut.glb" alt="A 3D model of an astronaut" camera-controls <!-- drag to rotate, scroll to zoom --> auto-rotate> <!-- gently spins on its own --> </model-viewer>
CSS
model-viewer {
width: 100%;
height: 340px;
border-radius: 12px;
background: #eef0f5;
}
Turn on augmented reality
Add the ar attribute (it's already on the model above). On a supported phone, model-viewer shows a “View in your space” button that opens the camera and places the model on the floor. ar-modes lists the technologies to try, in order.
HTML
<model-viewer src="chair.glb" alt="An armchair" ar ar-modes="webxr scene-viewer quick-look" ar-scale="fixed" <!-- keep real-world size --> camera-controls> </model-viewer>
- webxr — in-browser AR on supported Android/Chrome.
- scene-viewer — Android's native AR launcher.
- quick-look — iOS AR (needs a
.usdzversion too; see below).
ios-src="chair.usdz" alongside your .glb so iPhones/iPads can launch Quick Look AR.Polish: poster, loading & controls
A few attributes make it feel finished — a poster image while the model loads, a custom AR button, and dimensions to prevent layout shift.
HTML
<model-viewer
src="chair.glb" ios-src="chair.usdz"
alt="An armchair"
poster="chair-poster.webp" <!-- shown while loading -->
loading="lazy" <!-- load when scrolled near -->
camera-controls auto-rotate
ar ar-modes="webxr scene-viewer quick-look">
<button slot="ar-button" class="ar-btn">
View in your room
</button>
</model-viewer>
CSS
model-viewer { width: 100%; height: 420px; }
.ar-btn {
position: absolute; bottom: 16px; right: 16px;
background: #8b5cf6; color: #fff; border: none;
border-radius: 999px; padding: 10px 18px; font-weight: 600;
}
JavaScript — optional: swap model on swatch click
// model-viewer is fully declarative — no JS needed for 3D or AR.
// A tiny bit of JS is handy for product configurators: clicking a
// swatch swaps the .glb file (and the iOS .usdz, if you provide one).
// <div class="swatches">
// <button class="swatch active" data-src="chair-violet.glb"></button>
// <button class="swatch" data-src="chair-charcoal.glb"></button>
// <button class="swatch" data-src="chair-tan.glb"></button>
// </div>
const viewer = document.querySelector('model-viewer');
const swatches = document.querySelectorAll('.swatch');
swatches.forEach(swatch => {
swatch.addEventListener('click', () => {
swatches.forEach(s => s.classList.remove('active'));
swatch.classList.add('active');
viewer.src = swatch.dataset.src;
if (swatch.dataset.iosSrc) viewer.iosSrc = swatch.dataset.iosSrc;
});
});
auto-rotate, shadow-intensity="1", exposure, camera-orbit (starting angle), and disable-zoom.Where to get (or make) a model
You need a .glb file. Grab a free one or export your own.
Free libraries
Google Poly Pizza, Sketchfab (downloadable models), and Khronos sample models — check each license.
Make your own
Model in Blender (free) and export as .glb via File › Export › glTF 2.0.
Optimize it
Run models through glTF–Transform or Squoosh-style tools — keep files under a few MB.
Make USDZ
Use Apple's Reality Converter (free) to turn a .glb into the .usdz iOS needs.
https://modelviewer.dev/shared-assets/models/Astronaut.glb — handy for experimenting before you have your own model.Device support & gotchas
- 3D rotation works in every modern browser, desktop and mobile.
- AR “in your space” needs a recent phone — iOS 12+ (Quick Look) or ARCore-capable Android.
- Serve over HTTPS — AR and the camera won't start on plain
http://. - Always set
alttext — it's announced to screen readers like an image. - Keep models small; big files are slow to load on mobile data.
The whole thing as one file
Everything above — the model-viewer script, a 3D/AR product viewer with a custom “View in your room” button, and a poster while it loads — combined into one complete HTML file. Copy it into a new file called index.html, open it in a browser, and drag the astronaut to spin it. Here's the live result, then the full code.
Live preview
Complete HTML — copy this whole file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nebula Chair — View in AR</title>
<!-- The only dependency: Google's model-viewer web component -->
<script type="module"
src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f6f7fb; color: #1f2433; line-height: 1.6;
}
.wrap { max-width: 820px; margin: 0 auto; padding: 40px 24px 64px; }
.product {
background: #fff; border: 1px solid #e5e7eb; border-radius: 18px;
overflow: hidden; box-shadow: 0 10px 40px rgba(20,20,40,.06);
}
/* ===== The 3D / AR viewer ===== */
model-viewer {
width: 100%; height: 420px;
background: #eef0f5; --poster-color: #eef0f5;
}
/* Custom AR button slotted into the viewer */
.ar-btn {
position: absolute; bottom: 16px; right: 16px;
display: inline-flex; align-items: center; gap: 6px;
background: #8b5cf6; color: #fff; border: none; cursor: pointer;
border-radius: 999px; padding: 11px 20px; font-size: .9rem; font-weight: 600;
box-shadow: 0 6px 16px rgba(139,92,246,.4);
}
.ar-btn:hover { background: #7c3aed; }
/* model-viewer hides the slotted AR button automatically when AR
is unavailable (e.g. on desktop), so it only appears on phones. */
.info { padding: 24px 28px 28px; }
.tag { font-size: .72rem; text-transform: uppercase; letter-spacing: .14em;
color: #8b5cf6; font-weight: 700; }
.info h1 { font-size: 1.7rem; font-weight: 800; margin: 6px 0 4px; }
.price { font-size: 1.25rem; font-weight: 700; color: #111; margin-bottom: 12px; }
.info p { color: #555; font-size: .95rem; margin-bottom: 16px; }
.hint {
display: flex; gap: 10px; align-items: center;
background: rgba(139,92,246,.08); border: 1px solid rgba(139,92,246,.25);
border-radius: 10px; padding: 12px 16px; font-size: .88rem; color: #5b4a8a;
}
.swatches { display: flex; gap: 10px; margin: 16px 0; }
.swatch {
width: 34px; height: 34px; border-radius: 50%; cursor: pointer;
border: 2px solid #fff; outline: 2px solid transparent;
}
.swatch.active { outline-color: #8b5cf6; }
</style>
</head>
<body>
<div class="wrap">
<div class="product">
<model-viewer
src="https://modelviewer.dev/shared-assets/models/Astronaut.glb"
alt="A 3D model of the Nebula lounge chair"
camera-controls
auto-rotate
shadow-intensity="1"
ar
ar-modes="webxr scene-viewer quick-look"
ar-scale="fixed"
loading="eager">
<button slot="ar-button" class="ar-btn">
📱 View in your room
</button>
</model-viewer>
<div class="info">
<span class="tag">Augmented Reality</span>
<h1>Nebula Lounge Chair</h1>
<div class="price">$489</div>
<p>Drag to spin it in 3D. On a phone, tap
“View in your room” to place it at real scale on your floor
with augmented reality — no app needed.</p>
<div class="swatches">
<span class="swatch active" style="background:#8b5cf6" title="Violet"></span>
<span class="swatch" style="background:#1f2433" title="Charcoal"></span>
<span class="swatch" style="background:#d4a373" title="Tan"></span>
</div>
<div class="hint">
👈 Drag the model to rotate · scroll to zoom
</div>
</div>
</div>
</div>
<script>
// Tiny interactivity: let the color swatches highlight on click.
var swatches = document.querySelectorAll('.swatch');
swatches.forEach(function (s) {
s.addEventListener('click', function () {
swatches.forEach(function (o) { o.classList.remove('active'); });
s.classList.add('active');
});
});
</script>
</body>
</html>
src at your .glb, add ios-src="chair.usdz" for iPhone AR, and update the alt text. Everything else stays the same.Keep building
See more immersive work in Three.js Lab and WebGL, other media in Media Patterns — or browse the full Component Library.