Skip to content

coderofsalvation/aframe-verse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LATEST NEWS: for an AFRAME & file-format-agnostic version see the XR Fragments standard instead

AFRAME-verse, deadsimple immersive navigation

A single-player-verse component for AFRAME:

TRY THE ONLINE DEMO

  • ❤️ easily teleport between aframe apps & aframe-verse clusters
  • ❤️ does not exit immersive-mode when navigating to different aframe experiences
  • ❤️ standalone & serverless: no servers (NAF/signaling) needed
  • ❤️ HTML-first: even runs from wordpress, no ninja javascript-skills needed
  • ❤️ #networkless #decentralized #noblockchain #permissionless-first #federatedpullrequests

Similar to a WEBring, you can easily create DOMrings and VERSErings with this component, that can be Zuckerburgered by yourself (or friends).

Usage


<script src="aframe-verse-component.js"></script>

<a-scene>
  <a-entity aframe-verse="register: /aframe-verse.json">

    <!-- everything nested under `aframe-verse`, will be replaced upon navigation  -->

    <a-box href="/"></a-box>          <!-- home = the cluster-client (index.html) -->
    <a-box href="./app2.html"></a-box>  
    <a-box href="https://somefriend.com/some_aframe_app.html"></a-box>
    <a-box href="https://somefriend.com/supercustom_webxr_app.html"></a-box>

  <a-entity>

  <!-- entities below survive during teleporting                                    -->
  <!-- use 'wearable' to indicate sticking objects to controller in immersive mode  -->
  <a-entity id="player">
    <a-entity camera position="0 1.6 0" wasd-controls look-controls mouse-cursor></a-entity>
    <a-entity id="leftHand"  laser-controls="hand: left"  raycaster="objects: [button]" raycaster="lineColor: #888; lineOpacity:0.5"></a-entity>
    <a-entity id="rightHand" laser-controls="hand: right" raycaster="objects: [button]" raycaster="lineColor: #888; lineOpacity:0.5"></a-entity>
    <a-entity navigator position="0 1.092 -0.595" rotation="-45 0 0" wearable="el: #leftHand; rotation: -45, 0, 0; position: -0.02, -0.03, -0.1"></a-entity>
  </a-entity>
  
  <!-- ps. multiple (nested) aframe-verse components are supported!                -->

</a-scene>

Recursive description of a verse (aframe-verse.json):

{
  "schema":"aframe-verse/0.1",
  "destinations":[ 
    {"url":"./index.html"},               // immersive navigation
    {"url":"./app2.html" },               // 
    {"url":"https://coderofsalvation.gitlab.io/aframe-urwhatuthink/experience.html", "title":"Leon Du Star - URWHATUTHINK", "scripts":true},
    {"url":"https://fabien.benetou.fr/pub/home/future_of_text_demo/engine/",
     "author":"Fabien Benetou", 
     "newtab": true                       // opens in new tab (does not contain aframe-verse component)
    }
  ], 
  "verses":[  // import trusted destinations
    {"url":"https://coderofsalvation.github.io/aframe-verse-leondustar/aframe-verse.json", "scripts":true}
  ]
}

Click here for properties


property type info
debug bool (false) shows info in the browserconsole
register string location of aframe-verse.json destinations
hrefEvents stringarray (click, collide) events which trigger teleport to href
fade integer (100) amount (in ms) of fade-in fade-out time
fadeColor string ('black') (hex)color(name) for fading

Click here for events


component property promise info
aframe-verse registerJSON no fires when loading aframe-verse JSON file(s)
href beforeNavigate yes fires before navigation fadeout
href navigate yes fires after navigation fadeout
href loadHTML yes fires before inserting new DOM content
href loaded yes fires after all DOM content is loaded ('domready' e.g.)

See chapter Customizing (with code) > Customizing navigation Further for flowcontrol using promises.

How it works


A visitor in an aframe-verse just teleports to other destinations and clusters ("beam me up scotty!").
aframe-verse.json is just a telephone-book of destinations.

When a visitor surfs to a cluster-client (index.html), it loads all components, which other linked experiences use.

How does this works in large?

The concept above is an answer to the fact that each tile-based 'metaverse' will always turn into some kind of hypercentralized client-project. Instead, a visitor in the aframe-verse just teleports to other destinations and clusters.
When the visitor surfs to a cluster-client (index.html), it basically loads all components, which other linked experiences use.
This is a security-limitation and a performance-feature, because this:

  • makes traveling between experiences (within a cluster) very fluid and fast.
  • it creates a decentralized incentive between developer(s) to:
    • collaborate on a seamless & secure end-visitor cluster-client (index.html)
    • consistent UX because of:
      • shared components
      • shared global objects: wearables, UI, AR/VR controller-support e.g.

As an exception to the rule, the developer(s) (YOU) of a cluster-client (index.html) can load remote (trusted) components/scripts, which is demonstrated by aframe-verse-component-scripts.

Worstcase, a destination can be loaded in a new tab (newtab:true which exits immersive navigation ), which then basically becomes the new cluster.

Federated HTML-first verse-clusters


aframe-verse describes a verse using the lowest common denominator between Aframe authors (=a webdirectory)

This could be a github-repo, or linuxserver where:

  • the maintainer(s) maintain a pool of trusted aframe apps (& components)
  • the maintainer(s) allow DOM-sharing (a DOM-ring) between eachothers aframe-apps
  • the maintainer(s) agree on shared garbage collection

Ideally, the maintainers need to approve new (website-specific) scripts/components, and include them in index.html when a new app arrives thru merge requests.

But..but..what about privacy & security?

This is all up to the maintainers of a verse, just think of it as running a shared website & linksharing.
For more info read this

Project scope


Out of the box, this component is good enough for seamlessly navigating between simple read-only aframe experiences (galleries, portfolios, vr movies, viewing scenes e.g.).
A monoverse is the opposite of a 'metaverse'-concept (in which multiplayer-communication is fundamental). Therefore, the following is out of scope, but can still be used to progressively enhance an aframe-verse:

  • multiplayer: see the (way more complex) NAF approach which requires you to run your own server.
  • hardened security/privacy: introduce activitypub-layer, p2p webrtc like yjs

How to add experiences?


Just check index.html and app2.html, Basically:

  • put your aframe apps in apps/* (they should have an aframe-verse-attribute set somewhere)
  • add href-attributes to clickable items (see example)
  • use href="./afile.html" to teleport to relative files
  • whitelist href="https://..."-links by including them in aframe-verse.json (see browserconsole for errors)
  • use href="/" to guide the visitor back to the original cluster

How to add components?


Typically these are included in the cluster-client index.html.

What if other apps require certain components/scripts?

As an exception to the rule, you can load remote (trusted) components/scripts, which is demonstrated by aframe-verse-component-scripts.

Customizing (with code)


Rule of thumb: load (or extend loading) components in the cluster-client (index.html)

Extending navigation interactions


By defining hrefEvents, you can trigger navigation for other events too:

<... aframe-verse="register: /yourverse.json; hrefEvents: click, mouseenter, collide, foobar">
   <a-box href="./show.html"/>  
</...>

Profit! Now navigation is triggered to show.html whenever it is clicked, mousehovered or colliding with another object

calling $('[aframe-verse] [href]').emit('foobar', {}) would trigger navigation too

Customizing navigation further


You can control navigation-events by creating a custom component:

// use like: <a-entity aframe-verse="..." navigate></a-entity>

AFRAME.registerComponent('navigate', {
  init: function(){
    console.log("initing navigation")
    this.el.addEventListener('beforeNavigate', (e) => this.beforeNavigate(e) )
    this.el.addEventListener('navigate',       (e) => this.navigate(e) )
    this.el.addEventListener('loadHTML',       (e) => this.loadHTML(e) )
    this.el.addEventListener('registerJSON',   (e) => this.registerJSON(e) )
  }, 
  beforeNavigate(e){
    // let promise = e.detail.promise()   
    console.log("about to navigate to: "+e.detail.destination.url)
    // promise.resolve()
    // promise.reject("not going to happen")
  }, 
  navigate(e){
    // let promise = e.detail.promise()   
    console.log("navigating to: "+e.detail.destination.url)
    // promise.resolve()
    // promise.reject("not going to happen")
  }, 
  loadHTML(e){
    let newdom = e.detail.dom.querySelector("[aframe-verse]")
    // let promise = e.detail.promise()   
		console.log("loading html")
    // promise.resolve()
    // promise.reject("not going to happen")
  },
  registerJSON(e){
    let json = e.detail.json
    /* example: skip non-immersive navigation links */
    // json.destinations = json.destinations.filter( (d) => d.newtab ? null : d )
    /* example: launch external verses in a new tab (so its components get loaded too) */
    // json.destinations.map( (d) => d.url.match(/index\.html$/) ? d.newtab = true : null )
  }
})

This is the place to show a consent popup e.g. (most trusted experiences can do fine without that in the beginning).

Connecting, Nesting & Securing verse-clusters


For navigation, you can add external verses to the .verses-array in aframe-verse.json, that's all!
Optionally, you can secure the import-behaviour further using the registerJSON-event as shown above in 'Customizing navigation further'.

Fadetime & nesting verses


You can have multiple persisting verses at the same time. Usecases for this are: a menu system, mini-games, inventory or a teleporting-maze e.g.:

<a-entity aframe-verse="register: aframe-verse.json">
  ...
</a-entity>

<a-entity aframe-verse="register: menu.json; fade: 0">   <!-- NOTE: superfast fade in ms (0=off) -->
  ...
</a-entity>

NOTE: for heavy scenes you can set fade: 4000 (4seconds fade) e.g.

Publish, selfhost & connect your verse (for free)


3 ways of hosting:

GITHUB / GITLAB


  • click the fork-button on github or gitlab
  • rename the repository to aframe-verse-* (aframe-verse-myorganisation e.g.) for easy discoverability
  • github: go to settings-tab > enable github pages (use the main-branch)
  • profit! your verse can now be accessed thru
    • github: https://yourusername.github.io/aframe-verse-myorganisation/apps
    • gitlab: https://yourusername.gitlab.io/aframe-verse-myorganisation/apps

GLITCH


  • REMIX this glitch
  • rename the project to aframe-verse-* (aframe-verse-myorganisation e.g.) for easy discoverability
  • your verse can now be accessed thru https://aframe-verse-myorganisation.glitch.me/apps/

SELFHOSTING (redbean/wordpress/apache e.g.)


  • download zip and unpack it in your apache/worpress dir e.g.

Later: please connect your verse to this repo, by submitting a PR or mentioning your json-URL in an issue. That way, future verses (forks) will automatically include your verse too.

Scope / Contributing

All feedback,bugfixes are very welcome ❤️

Other things (features/privacy/security/consent-stuff e.g.) should be published as separate components (see the navigate custom-component example in the aframe-verse README.md in the Customizing (with code) > Customizing navigation further section.

Please publish any useful components under reponame aframe-verse-component-mycomponent for discoverability.

As a startingpoint for extending, you can simply fork the scripts-component as well