Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@tlaukkan/networked-aframe
Advanced tools
A web framework for building multi-user virtual reality experiences.
Multi-user VR on the Web
Write full-featured multi-user VR experiences entirely in HTML.
Built on top of the wonderful A-Frame.
Follow the NAF Getting Started tutorial to build your own example from scratch, including setting up a local server.
To run the examples on your own PC:
git clone https://github.com/networked-aframe/networked-aframe.git # Clone the repository.
cd networked-aframe
npm install && npm run easyrtc-install # Install dependencies.
npm run dev # Start the local development server.
With the server running, browse the examples at http://localhost:8080
. Open another browser tab and point it to the same URL to see the other client.
For info on how to host your experience on the internet, see the NAF Hosting Guide.
<html>
<head>
<title>My Networked-Aframe Scene</title>
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
<script src="easyrtc/easyrtc.js"></script>
<script src="https://unpkg.com/networked-aframe/dist/networked-aframe.min.js"></script>
</head>
<body>
<a-scene networked-scene>
<a-assets>
<template id="avatar-template">
<a-sphere></a-sphere>
</template>
</a-assets>
<a-entity id="player" networked="template:#avatar-template;attachTemplateToLocal:false;" camera wasd-controls look-controls>
</a-entity>
</a-scene>
</body>
</html>
Open in two tabs if nobody else is online.
Made something awesome with Networked-Aframe? Let me know and I'll include it here!
Networked-Aframe works by syncing entities and their components to connected users. To connect to a room you need to add the networked-scene
component to the a-scene
element. For an entity to be synced, add the networked
component to it. By default the position
and rotation
components are synced, but if you want to sync other components or child components you need to define a schema. For more advanced control over the network messages see the sections on Broadcasting Custom Messages and Options.
Required on the A-Frame <a-scene>
component.
<a-scene networked-scene="
serverURL: /;
app: <appId>;
room: <roomName>;
connectOnLoad: true;
onConnect: onConnect;
adapter: wseasyrtc;
audio: false;
debug: false;
">
...
</a-scene>
Property | Description | Default Value |
---|---|---|
serverURL | Choose where the WebSocket / signalling server is located. | / |
app | Unique app name. Spaces are not allowed. | default |
room | Unique room name. Can be multiple per app. Spaces are not allowed. There can be multiple rooms per app and clients can only connect to clients in the same app & room. | default |
connectOnLoad | Connect to the server as soon as the webpage loads. | true |
onConnect | Function to be called when client has successfully connected to the server. | onConnect |
adapter | The network service that you wish to use, see adapters. | wseasyrtc |
audio | Turn on / off microphone audio streaming for your app. Only works if the chosen adapter supports it. | false |
debug | Turn on / off Networked-Aframe debug logs. | false |
By default, networked-scene
will connect to your server automatically. To prevent this and instead have control over when to connect, set connectOnLoad
to false in networked-scene
. When you are ready to connect emit the connect
event on the a-scene
element.
AFRAME.scenes[0].emit('connect');
To disconnect simply remove the networked-scene
component from the a-scene
element.
AFRAME.scenes[0].removeAttribute('networked-scene');
Completely removing a-scene
from your page will also handle cleanly disconnecting.
<a-assets>
<template id="my-template">
<a-entity>
<a-sphere color="#f00"></a-sphere>
</a-entity>
</template>
<a-assets>
<!-- Attach local template by default -->
<a-entity networked="template: #my-template">
</a-entity>
<!-- Do not attach local template -->
<a-entity networked="template:#my-template;attachTemplateToLocal:false">
</a-entity>
Create an instance of a template to be synced across clients. The position and rotation will be synced by default. The aframe-lerp-component
is added to allow for less network updates while keeping smooth motion.
Templates must only have one root element. When attachTemplateToLocal
is set to true, the attributes on this element will be copied to the local entity and the children will be appended to the local entity. Remotely instantiated entities will be a copy of the root element of the template with the networked
component added to it.
attachTemplateToLocal=true
<a-entity wasd-controls networked="template:#my-template">
</a-entity>
<!-- Locally instantiated as: -->
<a-entity wasd-controls networked="template:#my-template">
<a-sphere color="#f00"></a-sphere>
</a-entity>
<!-- Remotely instantiated as: -->
<a-entity networked="template:#my-template;networkId:123;">
<a-sphere color="#f00"></a-sphere>
</a-entity>
attachTemplateToLocal=false
<a-entity wasd-controls networked="template:#my-template;attachTemplateToLocal:false;">
</a-entity>
<!-- No changes to local entity on instantiation -->
<!-- Remotely instantiated as: -->
<a-entity networked="template:#my-template;networkId:123;">
<a-sphere color="#f00"></a-sphere>
</a-entity>
Parameter | Description | Default |
---|---|---|
template | A css selector to a template tag stored in <a-assets> | '' |
attachTemplateToLocal | Does not attach the template for the local user when set to false. This is useful when there is different behavior locally and remotely. | true |
Currently only the creator of a network entity can delete it. To delete, simply delete the element from the HTML using regular DOM APIs and Networked-Aframe will handle the syncing automatically.
By default, the position
and rotation
components on the root entity are synced.
To sync other components and components of child entities you need to define a schema per template. Here's how to define and add a schema:
NAF.schemas.add({
template: '#avatar-template',
components: [
'position',
'rotation',
'scale',
{
selector: '.hairs',
component: 'show-child'
},
{
selector: '.head',
component: 'material',
property: 'color'
},
]
});
Components of the root entity can be defined with the name of the component. Components of child entities can be defined with an object with both the selector
field, which uses a standard CSS selector to be used by document.querySelector
, and the component
field which specifies the name of the component. To only sync one property of a multi-property component, add the property
field with the name of the property.
Once you've defined the schema then add it to the list of schemas by calling NAF.schemas.add(YOUR_SCHEMA)
.
Component data is retrieved by the A-Frame Component data
property. During the network tick each component's data is checked against its previous synced value; if the data object has changed at all it will be synced across the network.
To sync nested templates setup your HTML nodes like so:
<a-entity id="player" networked="template:#player-template;attachTemplateToLocal:false;" wasd-controls>
<a-entity camera look-controls networked="template:#head-template;attachTemplateToLocal:false;"></a-entity>
<a-entity hand-controls="left" networked="template:#left-hand-template"></a-entity>
<a-entity hand-controls="right" networked="template:#right-hand-template"></a-entity>
</a-entity>
In this example the head/camera, left and right hands will spawn their own templates which will be networked independently of the root player. Note: this parent-child relationship only works between one level, ie. a child entity's direct parent must have the networked
component.
NAF.connection.subscribeToDataChannel(dataType, callback)
NAF.connection.unsubscribeToDataChannel(dataType)
NAF.connection.broadcastData(dataType, data)
NAF.connection.broadcastDataGuaranteed(dataType, data)
NAF.connection.sendData(clientId, dataType, data)
NAF.connection.sendDataGuaranteed(clientId, dataType, data)
Subscribe and unsubscribe callbacks to network messages specified by dataType
. Broadcast data to all clients in your room with the broadcastData
functions. To send only to a specific client, use the sendData
functions instead.
Parameter | Description |
---|---|
clientId | ClientId to send this data to |
dataType | String to identify a network message. u is a reserved data type, don't use it pls |
callback | Function to be called when message of type dataType is received. Parameters: function(senderId, dataType, data, targetId) |
data | Object to be sent to all other clients |
The owner of an entity is responsible for syncing its component data. When a user wants to modify another user's entity they must first take ownership of that entity. The ownership transfer example and the toggle-ownership component show how to take ownership of an entity and update it.
NAF.utils.takeOwnership(entityEl)
Take ownership of an entity.
NAF.utils.isMine(entityEl)
Check if you own the specified entity.
Events are fired when certain things happen in NAF. To subscribe to these events follow this pattern:
document.body.addEventListener('clientConnected', function (evt) {
console.error('clientConnected event. clientId =', evt.detail.clientId);
});
Events need to be subscribed after the document.body element has been created. This could be achieved by waiting for the document.body onLoad
method, or by using NAF's onConnect
function. Use the NAF Events Demo as an example.
List of events:
Event | Description | Values |
---|---|---|
clientConnected | Fired when another client connects to you | evt.detail.clientId - ClientId of connecting client |
clientDisconnected | Fired when another client disconnects from you | evt.detail.clientId - ClientId of disconnecting client |
entityCreated | Fired when a networked entity is created | evt.detail.el - new entity |
entityDeleted | Fired when a networked entity is deleted | evt.detail.networkId - networkId of deleted entity |
The following events are fired on the networked
component. See the toggle-ownership component for examples.
List of ownership transfer events:
Event | Description | Values |
---|---|---|
ownership-gained | Fired when a networked entity's ownership is taken | evt.detail.el - the entity whose ownership was gained |
evt.detail.oldOwner - the clientId of the previous owner | ||
ownership-lost | Fired when a networked entity's ownership is lost | evt.detail.el - the entity whose ownership was lost |
evt.detail.newOwner - the clientId of the new owner | ||
ownership-changed | Fired when a networked entity's ownership is changed | evt.detail.el - the entity whose ownership was lost |
evt.detail.oldOwner - the clientId of the previous owner | ||
evt.detail.newOwner - the clientId of the new owner |
NAF can be used with multiple network libraries and services. An adapter is a class which adds support for a library to NAF. If you're just hacking on a small project or proof of concept you'll probably be fine with the default configuration and you can skip this section. Considerations you should make when evaluating different adapters are:
I'll write up a post on the answers to these questions soon (please bug me about it if you're interested).
By default the wsEasyRtc
adapter is used, which is an implementation of the open source EasyRTC library that only uses the WebSocket connection. To quickly try WebRTC instead of WebSockets, change the adapter to easyrtc
, which also supports audio. If you're interested in contributing to NAF a great opportunity is to add support for more adapters and send a pull request.
List of the supported adapters:
Adapter | Description | Supports Audio | WebSockets or WebRTC | How to start |
---|---|---|---|---|
wsEasyRTC | DEFAULT - EasyRTC that only uses the WebSocket connection | No | WebSockets | npm run start |
EasyRTC | EasyRTC | Yes | WebRTC | npm run start |
uWS | Custom implementation of uWebSockets | No | WebSockets | See naf-uws-adapter |
Firebase | Firebase for WebRTC signalling | No | WebRTC | See naf-firebase-adapter |
Deepstream | DeepstreamHub for WebRTC signalling | No | WebRTC | See naf-deepstream-adapter |
After adding audio: true
to the networked-scene
component (and using an adapter that supports it) you will not hear any audio by default. Though the audio will be streaming, it will not be audible until an entity with a networked-audio-source
is created. The audio from the owner of this entity will be emitted in 3d space from that entities position. The networked-audio-source
component must be added to an entity (or a child of an entity) with the networked
component.
To quickly get started, try the Glitch NAF Audio Example.
NAF.connection.isConnected()
Returns true if a connection has been established to the signalling server.
NAF.connection.getConnectedClients()
Returns the list of currently connected clients.
NAF.options.updateRate
Frequency the network component sync
function is called, per second. 10-20 is normal for most Social VR applications. Default is 15
.
NAF.options.useLerp
By default when an entity is created the aframe-lerp-component
is attached to smooth out position and rotation network updates. Set this to false if you don't want the lerp component to be attached on creation.
NAF.options.compressSyncPackets
Compress each sync packet into a minimized but harder to read JSON object for saving bandwidth. Default is false
.
To measure bandwidth usage, run two clients on Chrome and visit chrome://webrtc-internals
/ (root)
/dist/
/server/
/server/static/
/src/
/tests/
Interested in contributing? Shoot me a message or send a pull request.
NAF is not supported on nodejs version 7.2.0. Please use a different version of nodejs.
This program is free software and is distributed under an MIT License.
FAQs
A web framework for building multi-user virtual reality experiences.
We found that @tlaukkan/networked-aframe demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.