Share 3D Models with WebSockets Demo

Babylon.js
4 min readApr 10, 2020

--

Hello Babylon.js community!

In this week’s demo, I will be showing you how to share models between different clients using web sockets. This week’s demo was inspired by a question on the forum but you can leverage this technique for all sorts of real-time interaction use cases. Here is a video of it in action:

Baby shark, doo doo doo doo doo doo

The code for this demo can be found here and consists almost entirely of just two files:

  1. index.html which has the Babylon.js engine and loaders, a canvas to render scenes, a simple UI to upload model files, and a WebSocket listener for new models.
  2. index.js which has a Node.js server. This serves index.html and shares models to connected clients.

The code for the WebSocket server is pretty simple, so let’s start there. After setting up a typical Express server with a root route that serves index.html, I wrote the WebSocket logic shown in a snippet below.

// socket.io listener
io.on('connection', (socket) => {
const id = clientCount;
addClient(currentConnectedClients, id)

// if there is already a model being shared, new connected clients will get it
if(currentFile){
socket.emit('file uploaded', currentFile);
}
// when a new model is shared, send to all other connected clients
socket.on('file uploaded', (fileData) => {
currentFile = fileData;
socket.broadcast.emit('file uploaded', fileData);
});
// when a user leaves, remove them from the list
socket.on('disconnect', () => {
removeClient(currentConnectedClients, id);
// if there are no users left, reset the file to null
if(currentConnectedClients.length === 0){
currentFile = null;
}
});
});

Socket.io makes this pretty easy and the code follows familiar Javascript event handling patterns. Here are the events and messages Socket.io is listening for:

  1. “connection”: When a new client (a new tab or window loading the index.html for example) is connected, the “connection” event is received and in response, a “socket” between the server and the page is created. If there was a model already being shared with other clients, the server sends the file data to it.
  2. “file uploaded”: When a client fires a “file uploaded” message, it sends with it some file data. The server then updates the current file stored in state and sends out the new file data to all the other sockets attached.
  3. “disconnect”: When a client closes (like a page refresh, or closes the tab), it sends a “disconnect” event. Here, I just remove the client and update the app state.

Now let’s take a look at the client code!

Client-side code for sharing models via WebSockets

Most of the HTML is just the starter template grabbed from the Babylon.js documentation homepage which, will add the Babylon.js engine to the page and initialize a basic scene with a sphere and plane mesh. Additionally, lines 9 and 11 import the Babylon.js Loaders, which are needed to load model files on the client, and the Socket.io library, respectively.

Let’s scroll past the initialization and set-up of the basic scene which greets users when the page first loads, to line 103 where the socket connection to the server is created.

On line 105, an on “change” EventListener is attached to the file input element. When a user selects a local file to upload, a Javascript File object for that file is created and stored in browser memory. Then I create an object called fileData that holds the file name and the file data itself. Finally, the socket emits a “file uploaded” message to the server and passes along the fileData object. Remember how the server is listening for a “file uploaded” message? This is it. This is the message. This is the file that is going to get sent to all the other connected clients.

Let’s take a look at the helper function called addModelToScene defined on line 135. This is where the magic happens. If you are familiar with Babylon.js, you may have used the SceneLoader class before. Conveniently, it has a method called SceneLoader.ImportMesh(). In this demo, I am using the ImportMesh() method to add models to the scene we create on page load. Commonly, people use ImportMesh() with a URL to remote assets (for example, files hosted somewhere else like GitHub). But in our case, we want to import the mesh we’ve put into the browser’s memory. A cool thing about File objects is that we can reference them by turning them into blobs and then creating a URL that will point to the file data. Once we have a URL that points to the file data in the browser memory, I can pass that URL to the ImportMesh() method and it will add the mesh to the scene. Nice!

“But wait a minute!”, you ask. “What about the other clients? How will they get the models from my browser?” Well, remember that the file data was sent to the server with the “file uploaded” message. On line 119, we are telling the client to listen for “file uploaded” messages and then to process the associated data by passing it to the addModelToScene() helper method. So when one client adds a model to their page, the other clients will add it to theirs as well. And it works both ways!

Share with ALL the clients! Every direction! Even NEW ones!

Thanks for reading this Babylon.js demo! I hope you found it cool and informative. Please feel free to check out even cooler demos on our homepage or ask any questions on our forum. Our mission is to create one of the most powerful, beautiful, and simple Web rendering engines in the world. We can’t wait to see what you guys come up with using our tech!

--

--

Babylon.js
Babylon.js

Written by Babylon.js

Babylon.js: Powerful, Beautiful, Simple, Open — Web-Based 3D At Its Best. https://www.babylonjs.com/

No responses yet