3D Web Development: Going Through the Hoops with Croquet

You can design and build your breakthrough product in one take, or better still deliver your project in stages. If this sounds like an agile consultant’s message, well it is. Another reliable way is to slice up your proposition into different independent layers.
I mention this to get around the various statements made by Croquet, all of which have slightly different audiences. It lets you “build completely client-side multi-user web apps”. But Croquet is also “the Operating System for the Metaverse.” Also “Croquet OS is a powerful and easy open way to build microverses”. That’s microverses; not microservices, or metaverses.
Once one accepts that progressive building is meaningful, then there is no need to worry about all the things that are tied up with the term “metaverse.” Today, concerns around an imminent Metaverse/Cyberspace/Oasis would center on the social and regulatory problems. Nick Clegg of Meta looks at them here in anxious detail. At some point Croquet will need to face these issues, but for this article I’m only looking at the more developer-friendly engineering challenges that Croquet deals with.
In the multiplayer gaming world, the server has to be the point of truth — largely for trust reasons. When one or more players face off in Fortnite, each needs to be sure that given fair latency and frames per second, only their weapon skill dictates events. However for many user-initiated actions, even in the same game, there are no real trust issues. Looting a predetermined chest is an example.
Croquet makes it clear that within its system there are no servers — but there are reflectors. Indeed a global fleet of them. The quickest way to appreciate what they do is simply to run croquet.io/w3 on the browsers of two separate devices side by side. For the second device, copy the URL generated in the first device’s browser, or use the QR code lurking in the bottom left. You will see the same manipulatable landscape (much like the old god sim Populous) through both browsers. If you deform one, the other also changes, even if you scroll around only in one. You might think “well, both devices are looking at the same server model.” But no, they are running their own model code but looking at a shared message stream. So the job of the reflectors is to ensure that when I change the facts on the ground (literally in this case, by altering the surface in the simulation) the clients running the same model are updated at the same time with the same messages.
A Croquet client runs synchronized models that communicate via publish/subscribe. Just like the internet itself, reflectors are just messaging infrastructure — leaving the computing to be done at the edges.
Hello World Example
I’ll dive straight into a Codepen example right here to show how it works.
You should see the “Hello World!” program (it doesn’t actually print that line) with the three live files in consecutive windows (HTML, CSS and Javascript) framed within Codepen. And you will see a number counting down in the bottom window. We will not be needing any great coding skills to determine what is going on.
Croquet sees an app as split into a view and a model. We know what the model is doing — it’s running the show. Its job is to publish the activities, and provide methods to do this. But while that is going on, the thing you see in the browser is the view. The view subscribes to all the events it needs to know about. It also publishes its interaction events from the user. So when you hit that ever-increasing number, it resets.
In our example, when you click inside the countDisplay div (see the HTML window), a local click event is captured inside MyView (see the Javascript window). This was set in the constructor. The referenced method counterReset does what you imagine — publishes a message to the appropriate channel:
1 2 3 4 5 6 7 8 9 10 |
constructor() { ... countDisplay.onclick = event => this.counterReset(); ... } counterReset() { this.publish("counter", "reset"); } |
So your local MyModel (and any other models logged into the session) will react through the subscription to the “counter” channel of events. On receiving the “reset” message, the model resets the counter and publishes the “changed” message:
1 2 3 4 5 6 7 8 9 10 11 |
init() { ... this.subscribe("counter", "reset", this.resetCounter); ... } resetCounter() { this.count = 0; this.publish("counter", "changed"); } |
And as sure as night follows day, MyView picks up the “changed” message that it is subscribed to and changes the view by reading from the model:
1 2 3 4 5 6 7 8 9 10 |
constructor() { ... this.subscribe("counter", "changed", this.counterChanged); ... } counterChanged() { countDisplay.textContent = this.model.count; } |
All pretty straightforward. We note that the only direct interaction between view and model without the message passing chaperone was when the view read the count value directly from the model, as you can see above.
I mentioned a session, and this is what the last section in the Javascript code handles; it joins a session with your API key and the view and model class names. The only other significant thing in the Javascript is the registering of MyModel with Croquet.
What Happens When You Change the View or Model
So what happens if you change parts of the view?
Let’s fiddle with the code and add an extra welcoming banner div ourselves:
Other than the expected greeting being visible, nothing else has happened at all.
But what happens if you change the model?
Now that is a different story. I have merely added a comment, but you will probably notice the random letters in the color box and the QR code change, as soon as the reload happens:
From what we’ve seen, there is no way we could sync multiple models if the models were actually different (granted, I only added a comment). Remember the model is registered, so if the model then changes, something has to give.
The session is identified with two automatically assigned colors and a five-letter nonsense word connected to a QR code and url. Users who are participating in the same session — who are seeing the same badge — are guaranteed to be in sync.
Any change to the model code will create a new session, with a different badge. Only when other users load that changed code will they be able to join the new session. As you would expect, the model should never access the view directly; remember how we changed the view without disturbing the system. So the view is local and possibly unique to the user — a shared model cannot know about users’ independent views. And equally, the view cannot write to the model — the model is shared by everyone, so it can’t alter. Only communicate through the publish/subscribe message passing chaperone, to avoid inappropriate behavior.
You can get your own API key from Croquet to see what you can make. While this method of synchronizing multi-user activity needs deeper examination at the trust boundaries, for now it represents an interesting fresh perspective on the serverless toolset.