3D AR Studio
Apache-2.0 · one script tag · works on iPhone and Android

Put anything in your room.

A complete augmented-reality studio you can drop into any web page. Place as many 3D models as you like in your real space through the camera, describe a new one and watch it appear, arrange everything by hand, then share the whole scene as a link, a QR code, or a live room someone else can build in with you.

Open the live demo Add it to your site Star on GitHub

That frame is the real package, running the same code you install. On a phone, tap Camera and the models stand on your actual floor.

Quick start

One line, and you have AR.

No build step, no API key, no account. The models and the text-to-3D lane are free and keyless.

Plain HTML

<script type="module" src="https://unpkg.com/3d-ar-studio/dist/ar-studio.min.js"></script>
<ar-studio></ar-studio>

npm

npm i 3d-ar-studio three
import { createArStudio } from '3d-ar-studio'

const studio = createArStudio('#stage', {
  assets: 'https://your.cdn/models.json',   // your catalogue, or leave it out for the free CC0 library
  branding: { title: 'Acme AR', accent: '#00b894' },
})

studio.on('add', ({ placement }) => console.log('placed', placement.title))

Scaffold a deployable page

npx 3d-ar-studio create my-ar-site      # a ready-to-publish folder
npx 3d-ar-studio deploy                 # push it and turn on GitHub Pages
What you get

Not a model viewer. A studio.

Many models, one room

Place, drag, pinch-resize, twist-rotate and duplicate as many models as you want in a single live camera view. Every other web-AR drop-in stops at one.

Generate without leaving the camera

Type "a brass desk lamp" into the dock. The generation runs behind the live view and the finished model drops into the room. Free, keyless, no account.

Real WebXR, not a fallback

An always-armed hit-test reticle, one XRAnchor per model, real-world light estimation, and depth occlusion so models hide behind your furniture.

Real ARKit on iPhone, not an approximation

iPhones have no WebXR, so they get Apple's AR Quick Look: true plane detection, scale and occlusion. The model is converted to USDZ on the device in about a second. Android without WebXR gets Scene Viewer.

Scenes are links

The whole arrangement (models, positions, rotations, scales) round-trips through the URL. Compose on a laptop, scan the QR, it reopens exactly on your phone.

Build together, live

Open a room, share a six-character code, and every add and move syncs to everyone in it in real time, with live presence.

Characters actually move

Any humanoid GLB with no baked animation gets an idle clip retargeted onto its own skeleton: Mixamo, VRM, Avaturn, Unreal, Daz. No rig allow-list, no T-poses.

Agents can drive it

A bundled MCP server lets Claude, ChatGPT or any agent generate a model, compose an arrangement, and hand your user one link that opens it in their room.

Your models

Bring your own catalogue.

Out of the box the tray is filled from three.ws: a few hundred public-domain (CC0) props, free for commercial use, served with open CORS. Swap in your own with one option a URL, an array, or a function.

// 1. A JSON file anywhere. Five common shapes are read without reshaping.
createArStudio(el, { assets: 'https://cdn.acme.com/models.json' })

// 2. A list you hold in code.
import { staticSource } from '3d-ar-studio/sources'
createArStudio(el, { assets: staticSource({
  label: 'Our furniture',
  items: [{ src: 'https://cdn.acme.com/chair.glb', title: 'Aero chair', poster: '…' }],
}) })

// 3. Several tabs at once, in order.
createArStudio(el, { assets: ['recent', myCatalogue, 'objects', 'link'] })

// 4. Anything else: a source is just an object with a list().
createArStudio(el, { assets: {
  id: 'search', label: 'Search', searchable: true,
  async list() {
    const r = await fetch('/api/models').then((x) => x.json())
    return r.map((m) => ({ src: m.glb, title: m.name, poster: m.thumb }))
  },
} })

Your users can also point the studio somewhere else themselves, without touching your code: add ?assets=https://…/manifest.json to the page URL. Only https URLs are accepted, and every model source is re-validated before it reaches the loader.

Configuration

Options

OptionDefaultWhat it does
assets'three.ws'Where models come from: a preset, a manifest URL, a source object, or an array of them.
generateenabled, free three.ws lane{ enabled, endpoint, kind, tier }. endpoint is any MCP server exposing a compatible generate tool.
roomsenabled{ enabled, server }. Point server at your own Colyseus deployment to host shared rooms yourself.
animationsthree.ws idle clip{ enabled, manifestUrl, clip }. The clip retargeted onto humanoid models that ship no animation.
lighting'studio' HDRI{ preset, urls }. Set preset: null for procedural lighting only and zero HDRI download.
branding { title, accent, backHref, backLabel }.
shareBaseUrlthis pageWhere share links and QR codes point.
persistKey'ar-studio:scene:v1'localStorage key for the saved scene. Change it to run two studios on one origin.
maxPlacements20Cap on simultaneous models. Keeps low-end phones interactive.
allowUrlOverridetrueHonour ?assets=, ?src=, ?room= and ?forge= on the hosting page's URL.
onEventnullCalled with (event, detail) for every notable action: wire it to your analytics.

Events

Subscribe with studio.on(name, fn), or listen for ar-studio:<name> DOM events on the mounted element: add, remove, select, clear, generate, generate-error, camera, xr, room, share.

Provenance

Extracted from a studio people already use.

This is not a demo written to look good in a README. The rendering ladder, the anchor lifecycle, the retargeting pipeline, the scene format and the shared-room protocol are lifted from the AR surfaces running in production on three.ws, and generalized so they work on your site with your models.