← Component Library
Media Component

AR Viewer

Put a 3D model on your page that visitors can spin — and, on a phone, drop into their real room with augmented reality. It takes one web component and a model file. Here's the whole recipe.

01 · The Idea

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.

02 · Step 1

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>
It's a web component, so once the script loads you can use <model-viewer> anywhere in your HTML — no build step, no framework.
03 · Step 2

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;
}
04 · Step 3

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>
iOS needs a USDZ file. Add ios-src="chair.usdz" alongside your .glb so iPhones/iPads can launch Quick Look AR.
05 · Step 4

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;
  });
});
Common extra attributes: auto-rotate, shadow-intensity="1", exposure, camera-orbit (starting angle), and disable-zoom.
06 · Models

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.

Test asset: the astronaut above is https://modelviewer.dev/shared-assets/models/Astronaut.glb — handy for experimenting before you have your own model.
07 · Support

Device support & gotchas

Full docs and an interactive editor live at modelviewer.dev — you can tweak attributes and copy the code.
08 · Full Page

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>
Swap in your own model: point src at your .glb, add ios-src="chair.usdz" for iPhone AR, and update the alt text. Everything else stays the same.
09 · Related

Keep building

See more immersive work in Three.js Lab and WebGL, other media in Media Patterns — or browse the full Component Library.