Glad to share with you the long awaited 2020 update to the LiveCoding.space project, so let me introduce to you:

  • What's new in 2020 update!
  • Answer the question, why LiveCoding.space is still based on Croquet VWF instead of Croquet.io
  • And finally dive into the details of new "Minimal" application

LiveCoding.space 2020 updates

  1. LiveCoding.space is moving towards ES6 compliance! It means, that all Croquet VWF core application components are now ES6 modules (including VWF model, view, drivers etc).
  2. The minimal Croquet VWF core is now distributed separately from the drivers/application entanglement, compared to original VWF. This new core consists of refactored components: Croquet Virtual Time, Reflector Client, Luminary, VWF core app, Kernel, Model, View, JavaScript etc., all in ES6 modules format. Look at example: "Minimal" application.
  3. The core now has an experimental support of "stream of messages", based on functional reactive streams. That feature allows to record/replay/delay etc. external & internal (reflector/luminary) messages on peers (ongoing changes).
  4. Use very fast binary heap priority queue for sorting messages on Croquet core.
  5. All LiveCoding.space application components and worlds are now described in JSON format, with no YAML support anymore.
  6. There is no "super" user anymore, who should own proxy components for bootsrapping worlds from decentralized storage. Now every user could own it's own proxy and built the world from corresponding components.
  7. Any saved World could be used now as a World prototype for later reuse.
  8. Experimental driver for communication with external hardware over Web Bluetooth and functional reactive streams. Look at example: "Collaborative Robots".
  9. Finally add support for Oculus Quest/VIVE/Windows MR/Gear VR headsets in all spaces by default with teleportation control. Look at example: "Paint" application.
  10. Avatars are now have virtual pointers for interaction with in-world UI's.
  11. Added QR codes for world instance's URL for user friendly connection from mobile devices.
  12. Added costumes functionality for avatars and pointers.
  13. A-Frame library is updated to the latest 1.1 version, with support of the latest Web XR, AR JS and Layers. Look at example: "AR" application.
  14. Gun DB is updated to the latest stable version 0.2020.520

paint_app
Screenshot from "Paint" application.


Croquet VWF or Croquet.io?

If you are aware of LiveCoding.space project, you may noticed, that it is based on Croquet architecture (VWF version). While working with VWF just from 2012, I have started this project in 2017. VWF is known as Virtual World Framework an open source version of Croquet architecture for Web Browser.
Later in 2019 the latest version of Croquet was introduced Croquet.io, known nowadays as just Croquet (no version supplement) - and which is Awesome!
Check here experimental projects, where I explore the potential features.
Yet, that latest version of Croquet requires Internet connection and is closed sourced, but provides the SDK for developers. The SDK is well documented and allows to create collaborative apps just in minutes using Croquet.io architecture in Web Browser, but with two restrictions:

  • you could not modify/extend the Croquet core, as it is closed sourced
  • you are only allowed to use Croquet specific reflector servers, that are provided by Croquet.io on Internet, and could not create your own ones

Actually, for the most applications, that are developed using Croquet architecture, these restrictions are not limitations, as may appear. In most cases, the developer will never need to touch the Croquet core, as applications are fully client side. And geo-distributed reflector servers running 24/7 are also can satisfy nearly every needs.
But, unfortunately, for the LiveCoding.space project these two restrictions are the critical limitations, as there is no way to implement the latest Krestianstvo Luminary and other decentralized extensions in the Croquet core.

For now LiveCoding.space project will remain to be based on the open source version Croquet (VWF).

While looking closely on the latest Croquet.io, one can find, that the new version is implemented on the latest ES6 standards, have introduced pub/sub events for inter - Model/View communications and other things, but architecturally still relies on specific reflector servers and snapshots much like in Croquet VWF.

In comparison, LiveCoding.space alongside with using own standard minimal reflector servers, it moves Croquet VWF architecture towards decentralization, by introducing Krestianstvo Luminary.

reflector_vs_luminary

So, the combination of  Croquet VWF & Krestianstvo Luminary does not need any Croquet specific reflector servers at all, comparing to the original Croquet VWF or Croquet.io architectures.


Example of LiveCoding.space application: Minimal

minimal_app-1

Let's explore the  "Minimal" application details:

This "Minimal" application every virtual second updates a world property with Croquet Time and shows it on a web page and debug console; counts clicks on Model on every peer, without transmitting this data; generating a random number in Model on every peer.  
There are only three steps needed to complete a working application:

  1. Create World's description file in JSON format:
{
  "extends": "proxy/node.vwf",
  "properties": {
    "timeCount": 0,
    "clicks": 0,
    "randomNumber": null
  },
  "methods":{
    "initialize": {},
    "run": {},
    "incClicks": {},
    "decClicks": {},
    "getRandom": {}
  },
  "scripts":{
    "source": "minimal.js"
  }
}
  1. Create minimal.js file with Model driver methods
this.initialize = function () {
    this.run()
}

this.run = function () {
    this.timeCount = this.time;
    console.log(this.timeCount);
    this.future(1).run();
}

this.incClicks = function () {
    this.clicks = this.clicks + 1
}

this.decClicks = function () {
    this.clicks = this.clicks - 1
}

this.getRandom = function () {
    this.randomNumber = this.random();
}

  1. Create/modify default index.vwf.js file with View driver methods.
import { h, text,patch } from "$host/lib/ui/superfine.js"

class UserView {

    constructor(view) {
        this.view = view;
        this.init();
    }
    

    init() {
        let self = this;

        vwf_view.initializedNode = function (nodeID, childID, childExtendsID, childImplementsIDs,
            childSource, childType, childIndex, childName) {
            
            if (childID == vwf_view.kernel.application()) {

                ["time", "clicks", "random"].forEach(name => {

                    let el = document.createElement(name);
                    el.setAttribute("id", name);
                    document.querySelector("body").appendChild(el);

                })

                self.satTime(0);
                self.satClicks(0);
                self.satRandom(0);

            }
        }

        vwf_view.satProperty = function (nodeID, propertyName, propertyValue) {

            if (propertyValue === undefined || propertyValue == null) {
                return;
            }
            //let el = document.querySelector("[id='" + nodeID + "']");

            if (!document.getElementById("time"))
                 return


            if (propertyName == 'timeCount') {
                self.satTime(propertyValue);
            }

            if(propertyName == 'clicks'){
                self.satClicks(propertyValue)
            }

            if(propertyName == 'randomNumber'){
                self.satRandom(propertyValue);

                //update body color
                let randomColor = Math.floor(parseFloat(propertyValue)*16777215).toString(16);
                document.body.style.backgroundColor = "#" + randomColor;
            }

        }
    }

    satTime(state) {
        patch(
            document.getElementById("time"), 
            h("time", {style: "position: absolute; top: 100px; margin-left: 20px;" }, [
                    h("h2", {}, text('Time: ')), 
                    h("h1", {}, text(state))
        ]))
    }

    satClicks(state) {
        patch(
          document.getElementById("clicks"),
          h("clicks", {style: "position: absolute; top: 240px; margin-left: 20px;"}, [
            h("h2", {}, text('Clicks: ')), 
            h("h1", {}, text(state)),
            h("button", { onclick: () => vwf_view.kernel.callMethod(vwf.application(), "incClicks") }, text("+")),
            h("button", { onclick: () => vwf_view.kernel.callMethod(vwf.application(), "decClicks") }, text("-"))
          ])
        )
    }

    satRandom(state) {
        patch(
          document.getElementById("random"),
          h("random", {style: "position: absolute; top: 400px; margin-left: 20px;"}, [
            h("h2", {}, text('Random number: ')), 
            h("h1", {}, text(state)),
            h("button", { onclick: () => vwf_view.kernel.callMethod(vwf.application(), "getRandom") }, text("Generate"))
          ])
        )
    }
}

export {UserView as default}
  1. Optional add Model/View drivers to the app config if needed (like editor driver etc)
{
  "info":{
    "title": "Minimal App"
  },
  "model": {},
  "view": {}
}
  1. Optional add simple description for World catalog on it's Web page
{
    "info": {
        "en": {
            "title": "Minimal app",
            "imgUrl": "/defaults/assets/webimg.jpg",
            "text": "Minimal app example"
        }
    }
}

In the next post, we will look into details of Functional Reactive Streams for Croquet VWF & Luminary and "Collaborative Robots" application example.