{"id":13733169,"url":"https://github.com/networked-aframe/networked-aframe","last_synced_at":"2026-04-02T19:02:53.130Z","repository":{"id":37385023,"uuid":"79324058","full_name":"networked-aframe/networked-aframe","owner":"networked-aframe","description":"A web framework for building multi-user virtual reality experiences.","archived":false,"fork":false,"pushed_at":"2025-03-02T15:52:27.000Z","size":37547,"stargazers_count":1188,"open_issues_count":41,"forks_count":297,"subscribers_count":62,"default_branch":"master","last_synced_at":"2025-04-18T12:06:48.722Z","etag":null,"topics":["aframe","game","game-development","multi-user-vr","socialvr","virtual-reality","webrtc","webvr","webxr"],"latest_commit_sha":null,"homepage":"https://naf-examples.glitch.me","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/networked-aframe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"vincentfretin","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2017-01-18T09:25:59.000Z","updated_at":"2025-04-14T19:26:07.000Z","dependencies_parsed_at":"2023-02-10T03:01:40.012Z","dependency_job_id":"6181a6f5-9c14-4121-a823-d1833e3bfa83","html_url":"https://github.com/networked-aframe/networked-aframe","commit_stats":{"total_commits":1089,"total_committers":36,"mean_commits":30.25,"dds":0.6648301193755739,"last_synced_commit":"585610c98f47cc39f3b62ec097c7218ccc676a88"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networked-aframe%2Fnetworked-aframe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networked-aframe%2Fnetworked-aframe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networked-aframe%2Fnetworked-aframe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networked-aframe%2Fnetworked-aframe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/networked-aframe","download_url":"https://codeload.github.com/networked-aframe/networked-aframe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249581859,"owners_count":21294608,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aframe","game","game-development","multi-user-vr","socialvr","virtual-reality","webrtc","webvr","webxr"],"created_at":"2024-08-03T03:00:38.773Z","updated_at":"2026-04-02T19:02:53.099Z","avatar_url":"https://github.com/networked-aframe.png","language":"JavaScript","readme":"[discord]: https://discord.gg/jJxvuW97c4\n\n\u003cimg src=\"http://i.imgur.com/7ddbE0q.gif\" width=\"300\"\u003e\n\n\nNetworked-Aframe\n=======\n\n\u003cspan class=\"badge-npmversion\"\u003e\u003ca href=\"https://npmjs.org/package/networked-aframe\" title=\"View this project on NPM\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/networked-aframe.svg\" alt=\"NPM version\" /\u003e\u003c/a\u003e\u003c/span\u003e\n\u003cspan class=\"badge-npmdownloads\"\u003e\u003ca href=\"https://npmjs.org/package/networked-aframe\" title=\"View this project on NPM\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/networked-aframe.svg\" alt=\"NPM downloads\" /\u003e\u003c/a\u003e\u003c/span\u003e\n\n**Multi-user VR on the Web**\n\nA framework for writing multi-user VR apps in HTML and JS.\n\nBuilt on top of [A-Frame](https://aframe.io/).\n\n\u003cdiv\u003e\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e\n  \u0026mdash;\n  \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n  \u0026mdash;\n  \u003ca href=\"#more-examples\"\u003eExamples\u003c/a\u003e\n  \u0026mdash;\n  \u003ca href=\"#documentation\"\u003eDocumentation\u003c/a\u003e\n  \u0026mdash;\n  \u003ca href=\"#stay-in-touch\"\u003eContact\u003c/a\u003e\n\u003c/div\u003e\n\n\u003cbr\u003e\n\n\nFeatures\n--------\n* Support for WebRTC and/or WebSocket connections.\n* Voice chat. Audio streaming to let your users talk in-app (WebRTC only).\n* Video chat. See video streams in-app.\n* Bandwidth sensitive. Only send network updates when things change.\n* Cross-platform. Works on all modern Desktop and Mobile browsers. Oculus Rift, Oculus Quest, HTC Vive and Google Cardboard.\n* Extendable. Sync any A-Frame component, including your own, without changing the component code at all.\n\n\nRelease notes\n-------------\n\nYou can read [the release notes](https://github.com/networked-aframe/networked-aframe/blob/master/docs/RELEASE_NOTES.md) to know what changed in the latest releases.\n\n\nGetting Started\n---------------\n\n[Fork naf-project on StackBlitz](https://stackblitz.com/github/networked-aframe/naf-project)\n\nFollow [the NAF Getting Started tutorial](https://github.com/networked-aframe/networked-aframe/blob/master/docs/getting-started-local.md) to build your own example from scratch, including setting up a local server.\n\nTo run the examples on your own PC:\n\n```sh\ngit clone https://github.com/networked-aframe/networked-aframe.git  # Clone the repository.\ncd networked-aframe\nnpm install  # Install dependencies.\nnpm run dev  # Start the local development server.\n```\nWith 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.\n\nFor info on how to host your experience on the internet, see the [NAF Hosting Guide](https://github.com/networked-aframe/networked-aframe/blob/master/docs/hosting-networked-aframe-on-a-server.md).\n\n\nBasic Example\n-------------\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eMy Networked-Aframe Scene\u003c/title\u003e\n    \u003cscript src=\"https://aframe.io/releases/1.7.0/aframe.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"/easyrtc/easyrtc.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"https://unpkg.com/networked-aframe@^0.14.0/dist/networked-aframe.min.js\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ca-scene networked-scene\u003e\n      \u003ca-assets\u003e\n        \u003ctemplate id=\"avatar-template\"\u003e\n          \u003ca-sphere\u003e\u003c/a-sphere\u003e\n        \u003c/template\u003e\n      \u003c/a-assets\u003e\n      \u003ca-entity id=\"player\" networked=\"template:#avatar-template;attachTemplateToLocal:false;\" camera wasd-controls look-controls\u003e\n      \u003c/a-entity\u003e\n    \u003c/a-scene\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nMore Examples\n-------------\n\nOpen in two tabs to see the other avatar.\n\n* [Basic](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/basic.html)\n* [Basic with 4 clients](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/basic-4.html)\n* [Positional Audio](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/basic-audio.html)\n* [Video Streaming](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/basic-video.html)\n* [Nametags](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/nametag.html)\n* [Tracked Controllers](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/tracked-controllers.html)\n* [More...](https://cf9dc236-8b31-41d7-a0f0-3a87c89c70ea.pub.instances.scw.cloud/)\n\nMore complete examples:\n* [Nametags with UI in SolidJS/Tailwind CSS](https://github.com/networked-aframe/naf-nametag-solidjs)\n* [Realistic animated avatars with UI to choose your avatar](https://github.com/networked-aframe/naf-valid-avatars)\n\nMade something awesome with Networked-Aframe? [Let us know](https://github.com/networked-aframe/networked-aframe/issues) and we'll include it here.\n\n\nDocumentation\n-------------\n\n### Overview\n\nNetworked-Aframe works by syncing entities and their components to connected users. To connect to a room you need to add the [`networked-scene`](#scene-component) 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](#syncing-custom-components). For more advanced control over the network messages see the sections on [Broadcasting Custom Messages](#sending-custom-messages) and [Options](#options).\n\n\n### Scene component\n\nRequired on the A-Frame `\u003ca-scene\u003e` component.\n\n```html\n\u003ca-scene networked-scene=\"\n  serverURL: /;\n  app: \u003cappId\u003e;\n  room: \u003croomName\u003e;\n  connectOnLoad: true;\n  onConnect: onConnect;\n  adapter: wseasyrtc;\n  audio: false;\n  video: false;\n  debug: false;\n\"\u003e\n  ...\n\u003c/a-scene\u003e\n```\n\n| Property | Description | Default Value |\n| -------- | ----------- | ------------- |\n| serverURL  | Choose where the WebSocket / signalling server is located. | / |\n| app  | Unique app name. Spaces are not allowed. | default |\n| 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 \u0026 room. | default |\n| connectOnLoad  | Connect to the server as soon as the webpage loads. | true |\n| onConnect  | Function to be called when client has successfully connected to the server. | onConnect |\n| adapter | The network service that you wish to use, see [adapters](#adapters). | wseasyrtc |\n| audio  | Turn on / off microphone audio streaming for your app. Only works if the chosen adapter supports it. | false |\n| video  | Turn on / off video streaming for your app. Only works if the chosen adapter supports it. | false |\n| debug  | Turn on / off Networked-Aframe debug logs. | false |\n\n### Connecting\n\nBy 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.\n\n```javascript\nAFRAME.scenes[0].emit('connect');\n```\n\n### Disconnecting\n\nTo disconnect simply remove the `networked-scene` component from the `a-scene` element.\n\n```javascript\nAFRAME.scenes[0].removeAttribute('networked-scene');\n```\n\nCompletely removing `a-scene` from your page will also handle cleanly disconnecting.\n\n\n### Creating Networked Entities\n\n```html\n\u003ca-assets\u003e\n  \u003ctemplate id=\"my-template\"\u003e\n    \u003ca-entity\u003e\n      \u003ca-sphere color=\"#f00\"\u003e\u003c/a-sphere\u003e\n    \u003c/a-entity\u003e\n  \u003c/template\u003e\n\u003c/a-assets\u003e\n\n\u003c!-- Attach local template by default --\u003e\n\u003ca-entity networked=\"template: #my-template\"\u003e\n\u003c/a-entity\u003e\n\n\u003c!-- Do not attach local template --\u003e\n\u003ca-entity networked=\"template:#my-template;attachTemplateToLocal:false\"\u003e\n\u003c/a-entity\u003e\n```\n\nCreate an instance of a template to be synced across clients. The position and rotation will be synced by default. The [`buffered-interpolation`](https://github.com/InfiniteLee/buffered-interpolation) library is used to allow for less network updates while keeping smooth motion.\n\nTemplates 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.\n\n#### Example `attachTemplateToLocal=true`\n\n```html\n\u003ca-entity wasd-controls networked=\"template:#my-template\"\u003e\n\u003c/a-entity\u003e\n\n\u003c!-- Locally instantiated as: --\u003e\n\u003ca-entity wasd-controls networked=\"template:#my-template\"\u003e\n  \u003ca-sphere color=\"#f00\"\u003e\u003c/a-sphere\u003e\n\u003c/a-entity\u003e\n\n\u003c!-- Remotely instantiated as: --\u003e\n\u003ca-entity networked=\"template:#my-template;networkId:123;\"\u003e\n  \u003ca-sphere color=\"#f00\"\u003e\u003c/a-sphere\u003e\n\u003c/a-entity\u003e\n```\n#### Example `attachTemplateToLocal=false`\n\n```html\n\u003ca-entity wasd-controls networked=\"template:#my-template;attachTemplateToLocal:false;\"\u003e\n\u003c/a-entity\u003e\n\n\u003c!-- No changes to local entity on instantiation --\u003e\n\n\u003c!-- Remotely instantiated as: --\u003e\n\u003ca-entity networked=\"template:#my-template;networkId:123;\"\u003e\n  \u003ca-sphere color=\"#f00\"\u003e\u003c/a-sphere\u003e\n\u003c/a-entity\u003e\n```\n\n| Property              | Description                                                                                                                              | Default Value |\n| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------- |\n| template              | A css selector to a template tag stored in `\u003ca-assets\u003e`                                                                                  | ''            |\n| 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          |\n| persistent            | On remote creator (not owner) disconnect, attempts to take ownership of persistent entities rather than delete them                      | false         |\n\n### Deleting Networked Entities\n\nCurrently 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.\n\n\n### Syncing Custom Components\n\nBy default, the `position` and `rotation` components on the root entity are synced.\n\nTo 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:\n\n```javascript\nNAF.schemas.add({\n  template: '#avatar-template',\n  components: [\n    'position',\n    'rotation',\n    'scale',\n    {\n      selector: '.hairs',\n      component: 'show-child'\n    },\n    {\n      selector: '.head',\n      component: 'material',\n      property: 'color'\n    },\n  ]\n});\n```\n\nComponents 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.\n\nOnce you've defined the schema then add it to the list of schemas by calling `NAF.schemas.add(YOUR_SCHEMA)`.\n\nComponent 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.\n\n### Syncing components optimization\n\nFor each component, you can define a `requiresNetworkUpdate` function that\ntakes the current value and return true if the current value changed from the\nprevious value. You can return false if current and previous value are close\nenough, to not send this change to other participants.\n\nBy default when you don't define it, it always use the `defaultRequiresUpdate` function (defined at the top of [`networked.js`](https://github.com/networked-aframe/networked-aframe/blob/master/src/components/networked.js)) which is using a generic `deepEqual` function to compare the current value with the previous value and use `cachedData = AFRAME.utils.clone(newData);` when both values are different to keep the previous value for the next comparison.\n`AFRAME.utils.clone` implementation is doing `JSON.parse(JSON.stringify(obj))` that can be used with any type, but this may not be the best performance implementation for Vector3 type like position, rotation, scale.\n\nJust so you know what this is doing:\n\n```\n\u003e const v = new THREE.Vector3(1,2,3);\nVector3 {x: 1, y: 2, z: 3}\n\u003e JSON.parse(JSON.stringify(v))\n{x: 1, y: 2, z: 3}\n```\n\nSo this is creating a new object in memory each time the syncing process is done if the value changed, those objects are garbage collected at one point. Garbage collection in general can take 1ms or more if you have a heavy scene, a consequence could be that the browser drops some frames, so not having a consistent fps.\nYou can use Chrome profiler to confirm this. This is barely noticeable with just your moving avatar, but it may be important for your use case if the user owns a lot of continuously moving objects.\n\nMoreover with the current aframe `wasd-controls` implementation and how the position is smoothed, the player position is still changing below the millimeter precision 2s after the user stopped pressing a key, so sending a lot of NAF messages over the network for visually unnoticeable changes of position.\nNAF already includes position interpolation to smooth the position changes received, so sending all those position changes over the network is even redundant.\n\nYou can use a dedicated function to compare two Vector3 with a given precision to achieve better performance in term of sending less messages over the network and memory by avoiding creating new objects:\n\n```\nconst vectorRequiresUpdate = epsilon =\u003e {\n  return () =\u003e {\n    let prev = null;\n\n    return curr =\u003e {\n      if (prev === null) {\n        prev = new THREE.Vector3(curr.x, curr.y, curr.z);\n        return true;\n      } else if (!NAF.utils.almostEqualVec3(prev, curr, epsilon)) {\n        prev.copy(curr);\n        return true;\n      }\n\n      return false;\n    };\n  };\n};\n```\n\nThis function is actually defined in `NAF.utils.vectorRequiresUpdate` for you\nto use.\n\nTo use it in your networked schema for a position precision of 1 millimeter\nand rotation precision of 0.5 degree, use it like this:\n\n```\n{\n  template: '#avatar-template',\n  components: [\n    {\n      component: 'position',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)\n    },\n    {\n      component: 'rotation',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)\n    }\n  ]\n}\n```\n\n### Syncing nested templates - eg. hands\n\nTo sync nested templates setup your HTML nodes like so:\n\n```html\n\u003ca-entity id=\"player\" networked=\"template:#player-template;attachTemplateToLocal:false;\" wasd-controls\u003e\n  \u003ca-entity camera look-controls networked=\"template:#head-template;attachTemplateToLocal:false;\"\u003e\u003c/a-entity\u003e\n  \u003ca-entity hand-controls=\"hand:left\" networked=\"template:#left-hand-template\"\u003e\u003c/a-entity\u003e\n  \u003ca-entity hand-controls=\"hand:right\" networked=\"template:#right-hand-template\"\u003e\u003c/a-entity\u003e\n\u003c/a-entity\u003e\n```\n\nIn this example the head/camera, left and right hands of controllers will spawn their own templates which will be networked independently of the root player. Note: this is not related to hand tracking which is currently not supported. This parent-child relationship only works between one level, ie. a child entity's direct parent must have the `networked` component.\n\nYou need to define your left and right hand templates yourself to show hand models for the other users. Only the position and rotation will be synced to the other users. To sync the hand gesture, see the `networked-hand-controls` component below.\n\n### Tracked Controllers w/ Synced Gestures\n\nThis is a much simpler alternative to the above.\nNAF allows easily adding hand models visible to the others that show emulated gestures (not hand tracking) matching to which buttons are touched--so you can point and give a thumbs up or make a fist to other people in the room.\n\nAll you have to do is use the built in `networked-hand-controls` component, by adding these two entities as children of your camera rig:\n\n```html\n\u003ca-entity\n  id=\"my-tracked-left-hand\"\n  networked-hand-controls=\"hand:left\"\n  networked=\"template:#left-hand-default-template\"\n\u003e\u003c/a-entity\u003e\n\u003ca-entity\n  id=\"my-tracked-right-hand\"\n  networked-hand-controls=\"hand:right\"\n  networked=\"template:#right-hand-default-template\"\n\u003e\u003c/a-entity\n```\n\nTo see a working demo, check out the [Glitch NAF Tracked Controllers Example](https://naf-examples.glitch.me/tracked-controllers.html).\n\nThe public schema properties you can set are:\n\n| Property           | Description                                 | Default Value | Values                              |\n| ------------------ | ------------------------------------------- | ------------- | ----------------------------------- |\n| color              | Will be set as material color               | white         |\n| hand               | Specify if entity is for left or right hand | left          | left, right                         |\n| handModelStyle     | Available built-in models from A-Frame      | highPoly      | highPoly, lowPoly, toon, controller |\n| customHandModelURL | Optional custom hand model url              |               |                                     |\n\nNote the 'controller' option--that will use a model of the controller itself, automatically set correctly according to your platform--it will also broadcast model-supported button mesh updates. (Unfortunately, there's currently a bug with the Quest 2 model button meshes, so that one doesn't show any updates.)\n\nThe `networked-hand-controls` is replacing completely `hand-controls`, don't use both.\nIf you use the networked component as described above, you don't need to define the template and the networked schema for each hand.\nDefault templates and networked schemas are already defined as follow:\n\n```html\n\u003ctemplate id=\"left-hand-default-template\"\u003e\n  \u003ca-entity networked-hand-controls=\"hand:left\"\u003e\u003c/a-entity\u003e\n\u003c/template\u003e\n\u003ctemplate id=\"right-hand-default-template\"\u003e\n  \u003ca-entity networked-hand-controls=\"hand:right\"\u003e\u003c/a-entity\u003e\n\u003c/template\u003e\n```\n\n```javascript\nNAF.schemas.add({\n  template: '#left-hand-default-template',\n  components: [\n    {\n      component: 'position',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)\n    },\n    {\n      component: 'rotation',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)\n    },\n    'networked-hand-controls'\n  ]\n});\nNAF.schemas.add({\n  template: '#right-hand-default-template',\n  components: [\n    {\n      component: 'position',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)\n    },\n    {\n      component: 'rotation',\n      requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)\n    },\n    'networked-hand-controls'\n  ]\n});\n```\n\n### Sending Custom Messages\n\n```javascript\nNAF.connection.subscribeToDataChannel(dataType, callback)\nNAF.connection.unsubscribeToDataChannel(dataType)\n\nNAF.connection.broadcastData(dataType, data)\nNAF.connection.broadcastDataGuaranteed(dataType, data)\n\nNAF.connection.sendData(clientId, dataType, data)\nNAF.connection.sendDataGuaranteed(clientId, dataType, data)\n```\n\nSubscribe 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.\n\n| Parameter | Description\n| -------- | -----------\n| clientId | ClientId to send this data to\n| dataType  | String to identify a network message. `u` (Update), `um` (UpdateMulti) and `r` (Remove) are reserved data types, don't use them please\n| callback  | Function to be called when message of type `dataType` is received. Parameters: `function(senderId, dataType, data, targetObj)` With the easyrtc adapter `targetObj` can be `{targetRoom: 'roomId'}` when broadcasting a message or `{targetEasyrtcid: 'targetId'}` when sending a message to a specific participant. With the janus adapter, senderId is always null and `targetObj` is more a `source` parameter and generally equals to \"janus-event\".\n| data | Object to be sent to all other clients\n\n\n### Transfer Entity Ownership\n\nThe 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](./examples/ownership-transfer.html) and the [toggle-ownership component](./examples/js/toggle-ownership.component.js) show how to take ownership of an entity and update it.\n\n```javascript\nNAF.utils.takeOwnership(entityEl)\n```\n\nTake ownership of an entity.\n\n```javascript\nNAF.utils.isMine(entityEl)\n```\n\nCheck if you own the specified entity.\n\n\n### Events\n\nEvents are fired when certain things happen in NAF. To subscribe to these events follow this pattern:\n\n```javascript\ndocument.body.addEventListener('clientConnected', function (evt) {\n  console.error('clientConnected event. clientId =', evt.detail.clientId);\n});\n```\nEvents 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](https://github.com/networked-aframe/networked-aframe/blob/master/examples/basic-events.html) as an example.\n\nList of events:\n\n| Event | Description | Values |\n| -------- | ----------- | ------------- |\n| clientConnected | Fired when another client connects to you | `evt.detail.clientId` - ClientId of connecting client |\n| clientDisconnected | Fired when another client disconnects from you | `evt.detail.clientId` - ClientId of disconnecting client |\n| entityCreated | Fired when a networked entity is created | `evt.detail.el` - new entity |\n| entityRemoved | Fired when a networked entity is deleted | `evt.detail.networkId` - networkId of deleted entity |\n\nThe following events are fired on the `networked` component. See the [toggle-ownership component](./examples/js/toggle-ownership.component.js) for examples.\n\nList of ownership transfer events:\n\n| Event | Description | Values |\n| -------- | ----------- | ------------- |\n| ownership-gained | Fired when a networked entity's ownership is taken | `evt.detail.el` - the entity whose ownership was gained |\n| | | `evt.detail.oldOwner` - the clientId of the previous owner |\n| ownership-lost | Fired when a networked entity's ownership is lost | `evt.detail.el` - the entity whose ownership was lost |\n| | | `evt.detail.newOwner` - the clientId of the new owner |\n| ownership-changed | Fired when a networked entity's ownership is changed | `evt.detail.el` - the entity whose ownership was lost |\n| | | `evt.detail.oldOwner` - the clientId of the previous owner |\n| | | `evt.detail.newOwner` - the clientId of the new owner |\n\n### Adapters\n\nNAF 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:\n\n- How many concurrent users do you need to support in one room?\n- Do you want to host your own server? Or would a \"serverless\" solution like Firebase do the job?\n- Do you need audio (microphone) streaming?\n- Do you need custom server-side logic?\n- Do you want a WebSocket (client-server) network architecture or WebRTC (peer-to-peer)?\n\nBy default the `wseasyrtc` adapter is used, which does not support audio and uses a TCP connection. This is not ideal for production deployments however due to inherent connection issues with WebRTC we've set it as the default. To support audio via WebRTC be sure the server is using https and change the adapter to `easyrtc` (this uses UDP).\n\nRead the page [NAF adapters comparison](https://github.com/networked-aframe/networked-aframe/wiki/NAF-adapters-comparison) to know more about the different use cases and tradeoffs of the supported adapters:\n\n| Adapter | Description | Supports Audio/Video | WebSockets or WebRTC | How to start |\n| -------- | ----------- | ------------- | ----------- | ---------- |\n| wseasyrtc | DEFAULT - Uses the [open-easyrtc](https://github.com/open-easyrtc/open-easyrtc) library | No | WebSockets | `npm run dev` |\n| easyrtc | Uses the [open-easyrtc](https://github.com/open-easyrtc/open-easyrtc) library | Audio and Video (camera and screen share) | WebRTC (peer to peer) (1) | `npm run dev` |\n| janus | Uses the [Janus WebRTC server](https://github.com/meetecho/janus-gateway) and [janus-plugin-sfu](https://github.com/networked-aframe/janus-plugin-sfu) | Audio and Video (camera OR screen share) | WebRTC (SFU) | See [naf-janus-adapter](https://github.com/networked-aframe/naf-janus-adapter/tree/3.0.x) |\n| uws | Uses the highly performant [uWebSockets](https://github.com/uNetworking/uWebSockets) (supports room instancing (2)) | No | WebSockets | See comments in [server/uws-server.cjs](./server/uws-server.cjs) |\n| socketio | SocketIO implementation (supports [room instancing](https://github.com/networked-aframe/networked-aframe/pull/458)) | No | WebSockets | `npm run dev-socketio` |\n| webrtc | Native WebRTC implementation without external library excepted socketio (work in progress, currently no maintainer) | Audio | WebRTC (peer to peer) | `npm run dev-socketio` |\n| firebase | [Firebase](https://firebase.google.com/) for WebRTC signalling (currently no maintainer) | No | WebRTC  (peer to peer) | See [naf-firebase-adapter](https://github.com/networked-aframe/naf-firebase-adapter) |\n\n(1) WebRTC in the table means that component updates is using WebRTC Datachannels\n(UDP) instead of the WebSocket (TCP). You still have a WebSocket for the signaling\npart.\n\n(2) Room instancing means the server is creating a new room for the same scene if the number of participants reach a maximum threshold, instead of accepting infinite participants in the same room that may hurt server and browser performance. Some adapters like janus will just reject the participant connection if a threshold is reached.\n\n### Audio\n\nAfter 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 entity's position. The `networked-audio-source` component must be added to an entity (or a child of an entity) with the `networked` component.\n\nTo quickly get started, try the [Glitch NAF Audio Example](https://naf-examples.glitch.me/basic-audio.html).\n\nTo mute/unmute the microphone, you can use the following API (easyrtc and janus adapters):\n\n```javascript\nNAF.connection.adapter.enableMicrophone(enabled)\n```\n\nwhere `enabled` is `true` or `false`.\n\n### Video\n\nAfter adding `video: true` (not needed for the janus adapter) to the `networked-scene` component (and using an adapter that supports it) you will not see any video by default. Though the video will be streaming, it will not be visible until an entity using a mesh (`\u003ca-plane\u003e` for example) with a `networked-video-source` is created. The video from the owner of this entity will be visible in 3D space from that entity's position. The `networked-video-source` component must be added to an `\u003ca-plane\u003e` child entity of an entity with the `networked` component.\n\nThis currently applies only to the easyrtc and janus adapters that supports the `getMediaStream(clientId, type=\"video\")` API.\n\nSee the [Video Streaming](https://github.com/networked-aframe/networked-aframe/blob/master/examples/basic-video.html) example\nthat shows the user camera without audio.\n\nTo disable/reenable the camera, you can use the following API (easyrtc adapter only):\n\n```javascript\nNAF.connection.adapter.enableCamera(enabled)\n```\n\nwhere `enabled` is `true` or `false`.\n\nWith the easyrtc adapter, you can add an additional video track like a screen\nshare with the `addLocalMediaStream` and `removeLocalMediaStream` API:\n\n```javascript\nnavigator.mediaDevices.getDisplayMedia().then((stream) =\u003e {\n  NAF.connection.adapter.addLocalMediaStream(stream, \"screen\");\n});\n```\n\n```javascript\nNAF.connection.adapter.removeLocalMediaStream(\"screen\");\n```\n\nSee the [Multi Streams](https://github.com/networked-aframe/networked-aframe/blob/master/examples/basic-multi-streams.html) example\nthat uses a second plane with `networked-video-source=\"streamName: screen\"` to\nshow the screen share to the other participants.\nBe sure to look at the comments at the end of the html file of this example for known issues.\n\n### Misc\n\n```javascript\nNAF.connection.isConnected()\n```\n\nReturns true if a connection has been established to the signalling server.\n\n```javascript\nNAF.connection.getConnectedClients()\n```\n\nReturns the list of currently connected clients.\n\n\n### Options\n\n```javascript\nNAF.options.updateRate\n```\n\nFrequency the network component `sync` function is called, per second. 10-20 is normal for most Social VR applications. Default is `15`.\n\n```javascript\nNAF.options.useLerp\n```\n\nBy default when an entity is created the [`buffered-interpolation`](https://github.com/InfiniteLee/buffered-interpolation) library is used to smooth out position, rotation and scale network updates. Set this to false if you don't want this feature to be used on creation.\n\nOffline usage\n-------------\n\nNAF already includes easyrtc thus running `npm run dev` will provide a fully working solution without accessing an external server. The examples though do rely on both AFrame and other dependencies that not packaged with NAF. Consequently one would have to first [adapt AFrame to work offline](https://aframe.io/docs/1.7.0/introduction/faq.html#can-i-use-a-frame-offline-or-self-hosted) then do the same for all additional components. This basically boils down to downloading the scripts used and their content, e.g assets like 3D models, fonts, etc. It is recommended to load the page while the network console open and identify what requests go outside of the host.\n\nFor VR you will also need https as browsers require it for immersive mode. Instructions are provided in the `server/easyrtc-server.js` file. Namely you will have to generate a key and certificate, add them to your local CA then load them via the express server provided by NAF. Make sure to configure that properly at the top of `server/easyrtc-server.js` and enable https itself further down via `https.createServer` as instructed. Once you connect to the NAF server in VR the browser will still complain that the certificate is unknown. You can click on advanced and proceed.\n\nStay in Touch\n-------------\n\n- Join the [WebXR Discord][discord] and select the #a-frame channel\n- Follow changes on [GitHub](https://github.com/networked-aframe/networked-aframe/subscription)\n- Let us know if you've made something with Networked-Aframe. We'd love to see it!\n\n\nHelp and More Information\n------------------------------\n\n* [Getting started tutorial](https://github.com/networked-aframe/networked-aframe/blob/master/docs/getting-started-local.md)\n* [Edit live example on StackBlitz](https://stackblitz.com/github/networked-aframe/naf-project)\n* [Live demo site](https://naf-examples.glitch.me)\n* [Networked-Aframe Adapters](https://github.com/networked-aframe)\n* [A-Frame](https://aframe.io/)\n* [WebXR](https://immersiveweb.dev)\n* [Open EasyRTC WebRTC library](https://github.com/open-easyrtc/open-easyrtc)\n* [Hayden Lee, NAF creator and previous maintainer](https://twitter.com/haydenlee37)\n* [Vincent Fretin](https://twitter.com/vincentfretin) is handling new contributions and releases since the version 0.8.0\n* Bugs and requests can be filed on [GitHub Issues](https://github.com/networked-aframe/networked-aframe/issues)\n\n\nFolder Structure\n----------------\n\n * `/ (root)`\n   * Licenses and package information\n * `/dist/`\n   * Packaged source code for deployment\n * `/server/`\n   * Server code\n * `/examples/`\n   * Example experiences\n * `/src/`\n   * Client source code\n * `/tests/`\n   * Unit tests\n\n\nRoadmap\n-------\n\n* More examples!\n* [Add your suggestions](https://github.com/networked-aframe/networked-aframe/issues)\n\nInterested in contributing? [Open an issue](https://github.com/networked-aframe/networked-aframe/issues) or send a pull request.\n\n\nLicense\n-------\n\nThis program is free software and is distributed under an [MIT License](LICENSE).\n","funding_links":["https://github.com/sponsors/vincentfretin"],"categories":["By Industry","Web-Based Frameworks","JavaScript"],"sub_categories":["Gaming","Motion Controllers inside Unity!"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetworked-aframe%2Fnetworked-aframe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetworked-aframe%2Fnetworked-aframe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetworked-aframe%2Fnetworked-aframe/lists"}