An extensible library of Web Components for the spatial web.
Overview
MRjs is a mixed-reality-first, WebXR user interface library meant to bootstrap spatial web development. It implements much of the foundational work so that developers can spend less time on the basics and more time on their app.
Main Links
- landing-page - includes about, info, and high def and community-created samples
- docs - includes onboarding information, engine setup (ECS, Contributing, etc.), HTML tag helpers, and JavaScript API documentation
- dev-examples - the examples from the main MRjs repository used as development explainers and for testing purposes.
Getting started
<head>
of your HTML file:
Via a script tag in the For the latest stable version:
<head>
…<script src="https://cdn.jsdelivr.net/npm/mrjs@latest/dist/mr.js"></script>
…</head>
for the daily build. No guarantee of stability.
<head>
…<script src="https://cdn.jsdelivr.net/gh/volumetrics-io/mrjs/dist/mr.js"></script>
…</head>
Via NPM:
npm i mrjs
From source:
CLONE AND BUILD
You will need Node installed on your computer
Then, clone this repository
If you are planning to contribute to this repo instead of just using is as a source you will need its submodules for proper samples and testing
git clone --recurse-submodules the.cloning.url
. If you’ve already cloned the repo the normal way (git clone the.cloning.url
) you can update for the submodule as follows:git submodule update –init –recursive`
Next, setup your node environment:
npm install
and now build:
npm run build
RUNNING THE SAMPLES
We serve some of our examples and testing files from submodules, if you are planning to contribute, there will be times when the submodule for your work might be out of date. Since we run scripts along with our submodule update, make sure to run the following in that case (note, we wont have to do this that often, so you probably wont need to do this unless the test fails and tells you to do so):
npm run update-submodules
You are able to try the samples locally and in headset by running the following:
(note for in headset testing: https requirement)
npm run server
RUNNING THE TESTING
(this follows the need for the same update-submodules
note as the ‘running the samples’ section)
npm run test
Documentation:
Check docs.mrjs.io or our repository for the full documentation.
For local documentation or to check the local output when writing your own PR to see how it will update, run the below command. As a heads-up, the order of creation of docs depends on your operating system, so if when you run this and the order looks different, no worries - in the repository itself our action will handle that for you and default to use the right version for these automatically generated docs.
npm run docs
HTTPS Requirement
To test in headset, WebXR requires that your project be served using an HTTPS server. If you’re using Webpack, you can achieve this by utilizing the Dev Server webpack plugin with https: true
.
Here are some additional solutions:
- Live Server for VS Code
- via Python
Both options require you generate an SSL certificate and a key via OpenSSL:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
Features
Familiar 2D UI API
Create 2D UI using CSS and mr-panel
<style>
.layout {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 10px;
grid-auto-rows: minmax(100px, auto);
}.title {
margin: 0 auto;
font-size: 5vw;
line-height: 100%;
color: rgba(24, 24, 24, 0.75);
grid-column: 2;
}
mr-img {object-fit: cover;
grid-row: 3 / 6;
grid-column: 1 / -1;
}
#logo {
grid-column : 2;
scale: 0.001; /* set 3D content size */
z-index: 100; /* set position on Z-axis */
}</style>
<mr-app>
<!-- The 2D UI Panel -->
<mr-panel class="layout">
<mr-text class="title">
This is a quick example of an image gallery with explainer text.</mr-text>
<mr-img src="..."></mr-img>
<!--wrap non-UI components in mr-div to anchor to UI-->
<mr-div id="logo">
<mr-model src="./assets/models/logo.glb"></mr-model>
</mr-div>
</mr-panel>
</mr-app>
Built-in Physics Engine
Rapier.js is fully integrated out of the box. It is used to power collision-based hand interactions, but also to support other common features such as:
- Gravity
- Rag doll physics
- Joint constraints
- Vehicles
- Complex collision shapes
- Kinematics
Extensible
Designed to be extensible, MRjs provides a familiar interface via THREE.js, the Custom Elements API, and is leveled up with a built-in ECS (Entity Component System).
Entity Component System
MRjs is designed from the ground up using the Entity-Component-System Architecture. This is a common architecture implemented by Game Engines such as Unity, Unreal, and RealityKit.
Entity
An Entity is an object. It stores only the most fundamental data, such as a unique identifier, a THREE.js Object3D, a physics body, and dimension data such as width and scale.
Any mr-*
tag within the mr-app
is an Entity. mr-entity
is the spatial equivalent of a div
.
Creating a custom Entity is as simple as creating a Custom Element via the Web Components API.
Example:
class Spacecraft extends MREntity {
constructor(){
this.object3D = this.generateSpacecraft()
}
// function to procedurally generate a 3D spacecraft
generateSpacecraft(){
...
}
}
.get('mr-spacecraft') || customElements.define('mr-spacecraft', Spacecraft) customElements
Systems
A System contains logic that is applied to all entities that have a corresponding Component, using the data stored by the component. Unlike Entities & Components, Systems have no HTML representation and are implemented entirely in JavaScript.
When a component is attached to or detached from an entity, it is added or removed from its System’s registry of entities.
Example:
class OrbitSystem extends System{
constructor(){
super()
}
// called every frame
update(deltaTime, frame) {
for(const entity in this.registry) {
// Update entity position
let component = entity.components.get('orbit')
.radius
component.target
component//...
.component.set('orbit', { speed : 1 })
entity
}
}
// Called when an orbit component is attached
attachedComponent(entity) {
//...
}
// do something when an orbit component is updated
updatedComponent(entity, oldData) {
//...
}
// do something when an orbit component is detached
detachedComponent(entity) {
//...
} }
When you define a custom system, it listens for events triggered when the System’s corresponding component is attached, updated, or detached. In the above case, data-comp-orbit
.
Components
Components are attached to entities and used to store data. In MRjs they are implemented using data attributes beginning with the prefix data-comp-
.
Example:
<mr-spacecraft data-comp-orbit="radius: 0.5; target: #user;"></mr-spacecraft>
Note: the mapping between components and systems is 1-to-1, and the naming convention (data-comp-<name>
and <Name>System
) is strictly enforced.