Open it in a browser. Put the glasses on.

Say it. Wear it.

VibeXR is a single-file WebXR framework. Grabbing, pointing, pressing buttons and the hand menu work out of the box, so a project only writes its own world: small enough for one sentence to make.

no install no build step one HTML file three.js · WebXR works on tethered glasses and headsets alike
01

Worlds

Everything built on VibeXR is a world, and every world opens from one place. Pick one and you walk straight in; from inside, a portal or the Worlds window on your wrist takes you to the next. Quest carries you across without leaving immersion.

Open the lobby → vibexr.io · VITURE · PICO · Quest · Vision Pro · Galaxy XR · desktop preview with mouse and WASD

Credit. Dream Cage, Robot Zoo, Microduck, Archiscape, Physics Cabinet, Roadster Night, Immersive Vibe and the Interaction Playground are built and maintained by rongxuanmu and published at rongxuanmu.com, where each has a full write-up and demo video.

02

A world is the unit

One idea holds VibeXR together: a project is a world, and a world is the only thing anyone writes. Not an app, not an engine setup — an object that builds its content and says which things can be touched. Hands, rays, poke, menus, passthrough and the session belong to the framework, and every world inherits them unchanged.

Small enough to say

Tens to a few hundred lines. That is what lets an agent write one in a single pass, and what makes a sentence-to-world box plausible at all.

Impossible to make feel wrong

A world never touches input or rendering rules, so a boring world is possible but a world where nothing can be grabbed is not.

Clean in, clean out

init / enter / update / exit. Everything a world creates under its root is disposed when it unloads, so worlds can be swapped all day.

Worlds connect

Because every world is the same kind of thing, they link by default: one address, portals between them, a Worlds window on your wrist, and the people in a world see each other's head and hands.

This is a whole world

const Hello = {
  id: "hello",
  title: "Hello VibeXR",
  init(ctx) {
    const { THREE: T, root } = ctx;
    root.add(new T.HemisphereLight(0xffffff, 0x222222, 1.6));
    const cube = new T.Mesh(new T.BoxGeometry(0.12, 0.12, 0.12), new T.MeshStandardMaterial({ color: 0x7fb2ff }));
    const head = ctx.app.input.head;                       // in front of you, a little below eye height
    cube.position.set(head.x, head.y - 0.25, head.z - 0.8);
    root.add(cube);
    const it = ctx.add(cube, { draggable: true, twoHand: true });   // grab near or far, scale with two hands
    it.on("tap", () => cube.material.color.setHex(Math.random() * 0xffffff));
    const reset = ctx.button("Reset", { onClick: () => ctx.app.worlds.reload() });
    reset.root.position.copy(cube.position).add(new T.Vector3(0, -0.16, 0));
    root.add(reset.root);
  }
};
VXR.app.worlds.register(Hello);
await VXR.app.worlds.load("hello");

Grab it however you like; tap to recolour; scale with two hands. Paste it as a second module script after the framework — the framework file itself is never edited. More examples →

03

The expensive part isn't the pixels

Rendering a cube takes a dozen lines. Making it grabbable from across the room — keeping its pick distance, scalable with two hands, pokeable, with the ray stopping exactly at the surface — takes thousands, and every one of them is a tuning decision. Models are good at the first. They are not good at the second.

01

Pictures, not interactionScene, materials and animation look right. You put the headset on and nothing can be picked up.

02

No sense of what feels rightRays punch through surfaces. Grabbed objects fly into your face. Panels spawn outside the field of view.

03

Every prototype restartsTwo days of grab tuning, thrown away. The next prototype re-derives it, and re-breaks it.

04

Context runs outDeep engine hierarchies and boilerplate fill the window, and then the syntax hallucinations start.

All four point at one thing: what the model lacks isn't rendering, it's interaction sense. It has never stood inside a headset. It doesn't know that a ray punching through a desk is wrong, or that a far-grabbed object landing on your nose is wrong. That knowledge isn't in the public corpus, and no prompt recovers it.

So the thing to supply isn't a better prompt. It's a layer that is already correct — and that the model never touches.

For glasses, this matters more, not less. The questions a prototype exists to answer are the feel questions: can you grab it, is it comfortable to press, does the image hold when you turn your head. Code that merely runs cannot answer them — and if it can't, the prototype has no decision value.

04

Three parts

Three jobs. Each part serves a human and a model at the same time, and that duality is the whole design.

Part one

Worlds

Everything you can walk into right now — paintings, robots, toys, physics, a whole town — each a single world, all reachable from one lobby.

For people
Something to try in ten seconds, and a sense of what is reachable.
For models
Few-shot corpus. Editing a working world beats writing one.
Part two

Framework

The interaction core: four verbs across four input types, a UI kit, a palm menu, and the world contract.

For people
New prototypes start from interaction that already works.
For models
The RAG corpus and the harness — what to call, and what not to touch.
Part three

Studio

Where worlds get published: upload, automatic review, a link to share. Next, one box: say a sentence and walk into the world it makes, in thirty seconds.

For people
The one place you feel AI creation instead of watching it.
For models
The task shrinks to "write one world" — which is what makes 30s reachable.
05

Framework

To an object, every input is the same thing. They share four verbs — point, pinch, poke, grab — near and far. You write an object once; it works with all of them, and the model never needs to know which headset is on someone's head.

Near and far decided by hover

No target: a ray. Target within reach: the ray retracts, direct touch takes over. Target far away: the ray stays. Cursor and ray blend, never snap.

Depth-preserving grab

A far grab keeps its pick distance, with gain only on fast motion. Wheel or thumbstick pushes and pulls. Without this, everything you grab lands on your nose.

Poke has volume

Fingertips and controller tips press surfaces and volumes and report press depth, so a button can have real travel.

Objects declare themselves

precise, constrain, translateOnly, multi, twoHand, longPress, draggable — writing an object means saying how it may be treated.

One feedback language

Constant-width white hover outline that stays clean on multi-part CAD meshes, an outlined hand that occludes what's behind it, a soft click on press and release.

Passthrough first

One Enter button: AR with video see-through on where it exists, VR through the same button where it doesn't. Table height calibrates from head position on entry.

The corpus has three layers

Feeding a model the source dump is not retrieval. The framework splits into three layers because a model is missing something different at each stage.

LAYER 1

API surface

Every interactable option, the world lifecycle, the UI kit inventory — one page. Fixes calls that are simply wrong: invented parameters, misspelled options.

LAYER 2

Conventions & lessons

Every default carries why it is that value and how it was wrong before. Fixes code that runs but feels wrong. This is the layer models are missing, and the only one that can't be copied from anywhere else.

LAYER 3

World examples

Full source of every gallery world, tagged by genre. Fixes "I don't know what a finished world looks like." Editing beats authoring.

Face-the-user is held-only and keeps the angle you left it at. Rotation pivots on the window centre, not the bottom handle. The far ray ends at the hit point with no temporal smoothing. Windows scale with distance (K=0.8) so angular size stays constant. No physical inertia.

Layer 2, verbatim. Each line is one "built it, it was wrong, changed it" — exactly what a model cannot learn from public corpora.

And a harness, so it checks its own work

The corpus gets it written correctly; the harness gets it verified before delivery. Three pieces: an agent skill stating the world contract and the zero-edits-to-framework line; a desktop self-test loop that drives synthetic pointer events, steps frames by hand, and asserts numerically (“head turns 45°, object heading changes 0.000°”) rather than eyeballing a screenshot; and single-file delivery — one HTML, three.js via import map, no build chain, so generate → verify → share collapses to minutes.

06

Build one with your agent

Everything a coding agent needs to write a VibeXR world is published here as plain Markdown: a skill that sets the boundaries, and the three corpus layers it retrieves from. Your agent writes the world; the framework already did the hands.

Give your agent the skill

For Claude Code, drop it into your skills folder. Any other agent: point it at llms.txt, which indexes everything below.

mkdir -p ~/.claude/skills/vibexr && curl -fsSL https://docs.vibexr.io/skill/vibexr/SKILL.md -o ~/.claude/skills/vibexr/SKILL.md

Say what you want

Describe the world, not the interaction. Grab, ray, poke, menus and passthrough come with the framework.

Use the vibexr skill: a desk-sized solar system I can pick planets out of, with a button that pauses the orbits.

Open it, then put the headset on

The result is one HTML file. The agent checks it on the desktop first — synthetic pointer events and numeric asserts, not screenshots — then you open the same file in a headset browser and press Enter.

Publish it in Studio

Upload the HTML file at studio.vibexr.io and sign in with GitHub. An automatic review runs first: a safety scan, then a test render in a headless browser with a simulated headset. A person approves it, and it gets its own link.

07

Studio

beta

Where a world goes once it works: upload the HTML file, let Studio check it, get a link to send around. The same place will grow a box that turns a sentence into a world.

Open Studio →Beta · sign in with GitHub

Upload

One HTML file, the same one your agent made. Your worlds and their versions are listed in one place.

Automatic review

A safety scan of the code, then a test render in a headless browser with a simulated headset, at desktop and phone sizes. You get the report.

A person approves

Anything the checks are unsure about waits for a human before it goes out.

Its own link

Approved worlds get a link that opens in any headset browser. Listing in the lobby comes next.

Next: from a sentence to a world in progress

This half does not exist yet — it is what the framework and the worlds are for. One box in Studio: say a sentence, or type a line, and walk into the world it makes. Not a demo reel, not a file to download — the thing you just said, close enough to reach out and grab.

30sthe goal
60sthe ceiling

We are designing against these numbers rather than measuring them yet. Latency is not a performance metric here — it is whether the experience exists at all. Past a minute, people switch away, and the feeling of the thing I said became a world is gone by the time they come back. So it is a hard constraint to design against, not something to optimise afterwards.

Why this works here and not elsewhere

Every reason is in the two sections above. Strip any one of them and 30 seconds stops being reachable.

Only a world gets generated

Tens to a few hundred lines, not a whole application. Without the world contract there is no 30-second budget to begin with.

Interaction isn't generated at all

So the result is grabbable, pointable and pokeable on the first try — no "it works but feels wrong, go again" round trips. Those were the expensive ones.

Single file, no build

Nothing to compile or bundle. The world hot-mounts onto the framework already running; the page never reloads.

Three-layer corpus keeps the prompt short

One fewer round trip is several fewer seconds.

What has to be engineered

STREAM

Mount as it generates

Compile and attach what has already formed instead of waiting for the last token. The moment things start appearing matters far more than the moment they finish.

WARM

Pre-warmed skeletons

Keep skeletons for common genres ready so the model only fills in the difference.

FALL

Never a black screen

When a generated world throws, fall back to the last good one and say why. One failure costs more than ten slow seconds.

MEAS

Split the latency budget

Time to first token, time to complete, time to compile and mount — measured separately, or you cannot tell which part to cut.

08

Where this is going

The framework runs today and Studio publishes worlds; turning a sentence into a world is the next thing to build. Honest about the order:

Close the loop

Box to generated world to hot-mount, end to end — correctness first, speed second.

Earn the thirty seconds

Stream the mount, pre-warm the skeletons, and measure each segment rather than guessing at it.

Never a black screen

When a generated world throws, fall back to the last good one and say why.

Grow the conventions

The defaults and the reasoning behind them are published in docs/rules.md; every headset session adds to them.

Studio worlds in the lobby

Approved Studio worlds show up in the vibexr.io lobby next to the built-in ones, and e-mail sign-in joins GitHub.

Verify on hardware

Most paths are currently exercised on the desktop. The headset passes come next.