WebXR and <model-viewer>

WebXR is an evolutionary development of the old WebVR APIs that addresses user feedback and additional use cases that were not available when the original WebVR APIs were created.

The Immersive Web Community Group expects the final WebVR compatible draft to be released this summer (2019) and the candidate recommendation with two interoperable implementations to be released towards the end of the year.

While we wait for the WebXR specification to solidify, we can play with libraries that abstract some of the underlying complexities of WebVR, WebXR, and the basic Three.js library.

Google <model -viewer>

An interesting tool I saw at SFHTML5 is model-viewer, a web component-based tool that will display 3D content in a 2D hosting page.

model-viewer provides a way to view 3D content in a web browser, independently of the form factor you’re using (either a 2d Desktop browser or the browser built into a 3D device like the Oculus Quest). It is a web component so we need to polyfill web components for older versions of Edge and Firefox.

In the video below, Chris Joel, from the DeviceXR at Google explains model-viewer, what it is and how it works.

This assumes that we’ve loaded the polyfills using NPM. This is not an absolute requirement. You can always load the script from a CDN but, I prefer to have the scripts in my local directory

npm i @google/model-viewer \
@webcomponents/webcomponentsjs \
fullscreen-polyfill \
intersection-observer \
resize-observer-polyfill

I normally put the scripts at the bottom of the page, before the closing body tag and before loading the model viewer scripts.

<!--
webcomponents-loader is required to support older
versions of Edge and FIrefox
-->
<script src="./js/webcomponents-loader.js"></script>
<script src="./js/intersection-observer.js"></script>
<script src="./js/ResizeObserver.js"></script>
<!--
Fullscreen polyfill is required for experimental
AR features in Canary
-->
<script src="./js/fullscreen.polyfill.js"></script>-->

<!--
Magic Leap support
-->
<script src="./js/prismatic.js"></script>`

After we load the polyfills, we can load the model-viewer scripts. We use modules to load the scripts in modern browsers and nomodule to load a different script for those browsers that don’t support modules.

<script type="module" src="./js/model-viewer.js"></script>
<script nomodule src="./js/model-viewer-legacy.js"></script>

Once we have all the scripts installed we can use model-viewer by creating one or more instances of the custom element like shown in the example below:

<model-viewer
  src="./models/2CylinderEngine.gltf"
  alt="A 3D model of a 2 cylinder"
  auto-rotate
  camera-controls></model-viewer>

You can have as many instances of model-viewer as you need on your page. They can each be customized independently from each other. The two additional instances have different background colors. They can also be manipulated separately.

We can also load multiple models

But the more intriguing, to me, the thing about model-viewer is how, with a few extra attributes you can provide a range of experiences, from mouse manipulation as shown in the prior examples to full support for Google’s ARCore, Apple’s Quicklook (with some additional requirements), and Magic Leap.

For example, adding the ar attribute will provide a way to launch Google’s Scene Viewer in supported devices. See Using model-viewer to launch Scene Viewer for more information.

The code now looks like this:

<model-viewer
  src="./models/Satellite.gltf"
  alt="3D Satellite"
  ar
  camera-controls></model-viewer>

The model won’t look any different in desktop or devices where Scene Viewer is not supported but, in devices where the feature is supported, users will have an option to click on to place an interact with the object.

Apple provides its own AR experiences on supported iOS 12+ devices via AR Quick Look on Safari. This requires an additional USDZ model which will be used only in supported iOS devices.

If you have the USDZ model, you can add it to your experience using the ios-src attribute and the URL to the model.

<model-viewer
  src="./models/Diner.gltf"
  alt="3D Diner"
  camera-controls
  ios-src="./models/Diner.usdz"></model-viewer>

And, of course, you can provide support for both, using ar and ios-src together to cover both Android and iOS in the same element

<model-viewer
  src="./models/Diner.gltf"
  alt="3D Diner"
  camera-controls
  ar
  ios-src="./models/Diner.usdz"></model-viewer>

Final Notes

This barely begins to scratch the surface of what we can do with model-viewer.For more information see the [model-viewer documentation and this 2018 post about Augmented reality for the web that talks about the ideas that led to

Usdz is the wrinkle in the process. Most of the models in this page are loaded from glTF files, an interchange format created by the Khronos Group and that has wide support in terms of tools and technologies. As pointed out in All about Apple’s new USDZ File Format — Simplified the technology was developed by a single vendor, and has no open source tool support but there is commercial support, bot from small companies like Vectary and large tool companies like Adobe with Project Aero.

For more information about UDSZ, you can check the following resources.

Finally, I’ve made a copy of this page in Github where you can play with the working demos and test with Android and iOS: https://caraya.github.io/model-viewer-demo/index.html

Open Type Features in CSS

I’ve been playing a little with Open Type font variations (and their CSS equivalents) for a few weeks now and I’ve revisited a few areas I had played with before and left because, at the time, I didn’t see the need or usefulness.

That said, there are projects where good typography is just as important than size, line height, and other basic elements. In this essay, we’ll look at some less common features of OpenType fonts and how to use them in CSS, both the original specification using font-feature-settings and the newer version using font-variants-*. We’ll also look at ways to DRY our code and keep ourselves from repeating code.

CSS Fonts Module Level 3 defines the set of OpenType features supported by spec-compliant browsers

Older browsers may support a font-feature-settings, a lower level syntax to control access to the OpenType features.

To cover all our bases we’d have to do something like this:

.liga {
  @supports not (font-variant-ligatures: common-ligatures) {
    font-feature-settings: "liga";
  }

  /* IE doesn’t support @supports; explicitly use the prefixed version. */
  -ms-font-feature-settings: "liga";

  /* Best case scenario, just use `font-variant-*`. */
  font-variant-ligatures: common-ligatures;
}

We test if the browser does not support font feature settings. If it doesn’t we fall back to font feature settings for ligatures.

Because neither Edge nor IE supports the @supports command. Because of this, we need to add the Microsoft specific version.

The final case uses font-variant-ligatures.

A more elegant, and effortless, solution is to use the Utility OpenType library from Kenneth Normandy that takes a slightly different approach to use the different supported way of doing things.

It sets up the default values and then tests if either the webkit- or the regular value for font-variant-ligatures are not supported and if the tests fail (meaning they are supported) it changes the support to use font-feature-settings and the WebKit prefixed version.

.liga {
  -ms-font-feature-settings: "liga";
  -webkit-font-variant-ligatures: common-ligatures;
          font-variant-ligatures: common-ligatures;
}

@supports not ((-webkit-font-variant-ligatures: common-ligatures) or
              (font-variant-ligatures: common-ligatures)) {
  .liga {
    -webkit-font-feature-settings: "liga", "liga", "clig";
    font-feature-settings: "liga", "liga", "clig";
  }
}

Wakamaifondue provides both a visual demonstration of all the features a font supports, whether they are Variable Font Axes or Open Type features and a CSS stylesheet that you can incorporate into your own projects to take advantage of these features.

Utility OpenType provides classes for OpenType features reducing the cognitive load of having to know how to use each feature. You’re still required to know if the font supports the features you want to use.

Using these features in combination with variable fonts that support the OpenType features we need gives us a lot of flexibility when it comes to typography.

Links and Resources

WebAssembly: A new Swiss Army Knife

WebAssembly and its predecessor asm.js provide a portable target for compilation of high-level languages like C, C++, Go, and Rust among others, enabling deployment on the web for client and server applications.

See the Awesome WebAssembly Languages for a curated list of languages that can be compiled to WASM.

It is very important to point out that WASM is not trying to replace Javascript on the web.

According to the WebAssembly FAQ:

WebAssembly is designed to be a complement to, not replacement of, JavaScript. While WebAssembly will, over time, allow many languages to be compiled to the Web, JavaScript has an incredible amount of momentum and will remain the single, privileged (as described above) dynamic language of the Web. Furthermore, it is expected that JavaScript and WebAssembly will be used together in a number of configurations

The video below, from Google I/O 2019, explains how web developers can use WebAssembly with C/C++ and other languages.

Emscripten

Both C/C++ and Rust use the Emcscripten compiler toolchain, a drop-in replacement for regular compilers in C/C++ and Windows.

The process is broken in three

  • Download and prepare Emscripten
    • Clone the repository
    • Change to the directory you downloaded Emscripten to
    • Update the repository if it’s not the first time you’re using the software
  • Update and activate the code
    • Use emsdk install to install the latest version of the SDK
    • Run emsdk activate to activate the version you just installed
  • Activate the installation
    • Run the source command to add the emsdk you installed. The command is source ./emsdk_env.sh
# Clone the repository
git clone https://github.com/emscripten-core/emsdk.git

# Enter the directory where the code is installed
cd emsdk

# Update the repository if this is not the first run
git pull

# Download and install the latest SDK tools.
./emsdk install latest

# Make the "latest" SDK "active" for the current user.
# (writes ~/.emscripten file)
./emsdk activate latest

# Activate PATH and other environment variables
# in the current terminal
source ./emsdk_env.sh

Once we have done this we have the toolchain we’ll need for C/C++ and Rust.

C and C++

WebAssembly, and asm.js before it, was first created to port C and C++ code to LLVM bit code that could then be converted to Javascript.

If we take this program that will print Hello, World to the screen. save it as hello.c.

#include <stdio.h>
int main(int argc, char ** argv) {
  printf("Hello, world!\n");
}

To convert the hello.c to a WebAssembly file that will play on your web browser run the following command

emcc hello.c -s WASM=1 -o hello.html

For those of you familiar with the GCC compiler toolchain, the differences between traditional C/C++ compilation and our WebAssembly compilation are:

  • The command is emcc provided by Emscripten as a replacement for GCC and Clang
  • The -s flag passed commands to the transpiler. In this case, we tell it that we want WebAssembly instead of asm.js
  • the -o hello.html tells the compiler to create an HTML file to display the module, a Javascript file with the code to give access to the .wasm code and the wasm file that contains the binary Web assembly code.

The compilation will produce the following files:

.
├── hello.c
├── hello.html
├── hello.js
└── hello.wasm

In order to test the project, start a web server and open hello.html in your favorite web browser. They will produce results similar to this:

Emscripten HTML page show results of the code
Emscripten HTML page show results of the code

A more complex example

As far as examples go, Hello, World is as basic as it gets. While it’s good to work out any possible kinks in the system there has to be more we can do with it.

We’ll create a function that generates a Fibonacci Sequeence for the number we pass as a parameter.

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int fib(int n) {
  int i, t, a = 0, b = 1;
  for (i = 0; i < n; i++) {
    t = a + b;
    a = b;
    b = t;
  }
  return b;
}

The compilation is a little different too, we do heavy optimizations to the generated code and we pass cwrap as the value of the EXTRA_EXPORTED_RUNTIME_METHODS flag.

cwrap is one of the ways we can call C/C++ from Javascript

emcc -O3 \
-s WASM=1 \
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' \
fib.c

Instead of letting Emscripten generate the HTML file we take that into our own hands. The resulting files from the compilation are listed below

.
├── a.out.js
├── a.out.wasm
└── fib.c

To display the result we need to generate our own HTML. This is better in a way because we don’t have to use the pre-built template that looks really ugly.

The important part of the HTML file is the following block. In it we do the following:

  1. Load the generated Javascript file (a.out.js)
  2. When the module is initialized we use Module.cwrap to call the function from the WASM file
  3. Log the result to the console
<script src="a.out.js"></script>
<script>
  Module.onRuntimeInitialized = _ => {
    const fib = Module.cwrap('fib', 'number', ['number']);
    console.log(fib(12));
  };
</script>

The image below shows the result along with a warning.

Chrome console showing warning and the results of the code
Chrome console showing warning and the results of the code

The warning happens because the web server I used (Python 3’s built-in HTTP module) doesn’t assign the correct mime type for the wasm module. I imagine in production this won’t be a problem but during development, you need to take this into account.

Go

Go is Google’s open source language that makes it easy to build simple, reliable, and efficient software.

I’m torn between taking Go or Rust as my second backend language after C++ and it’s not an easy decision to make as they both have their advantages and disadvantages

That said, this is the code that we’ll use to tests Go’s WASM support.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello wasm")
}

Unlike C and Rust, support for WASM is baked into the core language runtime. To compile the code into WASM we need to run the following command.

GOOS=js GOARCH=wasm go build -o test.wasm test.go

The GOOS and GOARCH flags tell the compiler what we want to do, we want to compile the file using js as the operating system and wasm as the architecture. This is similar to how we’d compile Go for architectures other than the one we’re working on (and, sadly, you have to compile Go for each platform you want to use it for).

In order to run the code we need to copy files from our Go root directory into our current working directory and, if desired, rename the HTML file we copied

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
cp "$(go env GOROOT)/misc/wasm/wasm_exec.html" .
# we rename the html file to index.html for convenience.
mv wasm_exec.html index.html

Go is more strict than Emscripten when it comes to mime types. It refuses to run the wasm file because the mime type is incorrect.

Rust

Rust is a Mozilla project that seeks to create reliable and efficient software.

There are two ways to get Rust ready to go. The first one is to install rustup, the Rust package manager directly using the following commands

# 1. Download and install rustup
curl https://sh.rustup.rs -sSf | sh

# 2. Install the default version of Rust
rustup install stable

# 3. Make the stable version the default
rustup default stable

# 4. Add support for WASM to your Rust installation
rustup target add wasm32-unknown-emscripten

However, if you are on a Mac and use Homebrew, the commands are a little different. The software to install is rustup-init and it will present you with a series of options for installing or customizing the installation. To being, select option 1 to install the default.

# 1. Install rustup-init with homebrew
brew install rustup-init

# 2. Run rustup-init
rustup-init

# 3. Select option 1 or press enter

# 4. Add support for WASM to your target installation
rustup target add wasm32-unknown-emscripten

We have our Rust toolchain installed and ready to go. Next step is to set up our package.

# Create a new binary package
cargo new hello-world --bin
# Switch to the directory you just created
cd hello-world

The package looks like this:

├── Cargo.toml
└── src
    └── main.rs

Inside main.rs we have the following code that will print Hello World! to the console and then exit.

fn main() {
  // Print text to the console
  println!("Hello World!");
}

We compile this file using the command below:

rustc --target=wasm32-unknown-emscripten src/main.rs -o hello.html

First surprise, the command work when invoked directly from Node but fails when run from a web browser. I discovered that you need special configurations

We have to configure the type of library and dependencies in our Cargo.toml file. It now looks like this:

[package]
name = "hello-world"
version = "0.1.0"
authors = ["Carlos <[email protected]>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.44"

[dependencies.web-sys]
version = "0.3.4"
features = [
  'Document',
  'Element',
  'HtmlElement',
  'Node',
  'Window',
]

Next, we install wasm-pack as our compiler rather than using rustc as we did in the previous iteration.

cargo install wasm-pack

The final step is to use wasm-pack to build the package for the web

wasm-pack build --target web

Conclusion

We now have WebAssembly-ready toolchains for fours languages: C, C++, Go and Rust. So what’s next?

The code examples in this post barely scratch the surface of what you can do. Explore the tools, libraries and modules/packages and how they interact when used in WASM code. There is much still to learn.

Don’t feel like everything you do on the web now has to be coded in C, Rust or Go and then transpiled to WebAssembly to be used on the web. Like Surma says in his presentation, use WASM to enhance what’s already there.

By the way, I got all the code working by uploading it to Github and using Github Pages to run the code from. GH Pages has already taken care of the WebAssembly mime type for me 🙂

Links

Fetch and Axios: When and How

Fetch, the replacement for XmlHTTPRequest object (xhr for short) has been around long enough for me to consider it well baked into the platform and safe to use.

As a quick refresher, this script will fetch the latest 10 posts from my blog in JSON format that we can then feed into a framework or templating engine.

We’re using async/await to make the code easier to read.

const url = "https://publishing-project.rivendellweb.net/wp-json/wp/v2/posts";
const getData = async (url) => {
  try {
    const response = await fetch(url);
    const json = await response.json();
    console.log(json);
  } catch (error) {
    console.log(error);
  }
};
getData(url);

While this is widely supported in browsers this is not the case with Node. There is no built-in equivalent so we have to go the module route.

The easiest way to do it is to install node-fetch which provides a syntax identical to native fetch.

After you install it in your project use async/await with the same syntax as the native fetch example.

const fetch = require("node-fetch");

const urlToFetch = "https://publishing-project.rivendellweb.net/wp-json/wp/v2/posts";
const getData = async (urlToFetch) => {
  try {
    const response = await fetch(urlToFetch);
    const json = await response.json();
    console.log(json);
  } catch (error) {
    console.log(error);
  }
};
getData(urlToFetch);

There are some drawbackks to the fetch methods of retrieving data. The biggest one is that there is now way to cancela fetch request already in process.

There are third party libraries that work with and enchance the native fetch functionality, I’ve chosen to work with Axios as a replacement for native fetch.

The two differences between the axios version, below, and the node-fetch from the previous examples:

  • Axios uses get instead of fetch
  • The data payload appears in response.data instead of response.json

Otherwise the codde is the same.

const axios = require("axios");
const urlToFetch = "https://publishing-project.rivendellweb.net/wp-json/wp/v2/posts";

const getData = async urlToFetch => {
  try {
    const response = await axios.get(urlToFetch);
    const data = response.data;
    console.log(data);
  } catch (error) {
    console.log(error);
  }
};

getData(url);

So, which one do we use?

It depends on who are your target users, what browsers they use and whether you’re working client-side only or with Node.

But as with everything else on the web, test for performance and test for support in your target platforms; if it ain’t broken, don’t fix it.

Javascript Inheritance: Prototypes and Classes

Until recently Javascript did not have a class system. This doesn’t mean that we couldn’t create reusable objects but that we had to work through hoops to make it work.

Prototypal Inheritance: It’s all about the prototype

Early versions of Javascript until ES2015 used prototypal inheritance. We create a master object, inherit from the base object, and assign new properties to the object’s prototype.

In this example, Employee is the base objects with a few attributes. Other types of objects will inherit from this directly or indirectly.

function Employee() {
  this.name = '';
  this.dept = 'general';
}

The next block of functions shows how objects can inherit directly from the base class object.

We use Employee.call with the object we want to use as a parent and this to represent the object we’re calling it from as a super constructor call; this will give us access to the Employee object methods and attributes

function Manager() {
  Employee.call(this);
  this.reports = [];
}
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;

function WorkerBee() {
  Employee.call(this);
  this.projects = [];
}
WorkerBee.prototype = Object.create(Employee.prototype);
WorkerBee.prototype.constructor = WorkerBee;

To further illustrate how prototypal inheritance works, we’ve defined two additional types of employees and, rather than define them based on the base Employee object we define it based on one of the children, WorkerBee.

Engineer and SalesPerson inherit from WorkerBee, they also get all the properties in Employee without having to do any further setup.

function SalesPerson() {
   WorkerBee.call(this);
   this.dept = 'sales';
   this.quota = 100;
}
SalesPerson.prototype = Object.create(WorkerBee.prototype);
SalesPerson.prototype.constructor = SalesPerson;
console.log(SalesPerson.prototype);

function Engineer() {
   WorkerBee.call(this);
   this.dept = 'engineering';
   this.machine = '';
}
Engineer.prototype = Object.create(WorkerBee.prototype)
Engineer.prototype.constructor = Engineer;
console.log(Engineer.prototype);

Next we instantiate different objects:

let jim = new Employee();
let sally = new Manager();
let mark = new WorkerBee();
let fred = new SalesPerson();
let jane = new Engineer();

An earlier post, Prototypal Inheritance and Classes showed how to create objects and add to the prototype, but did not talk about how to inherit from the prototype chain.

Classes to the rescue

If you’ve done any programming at all you’re likely to have found classes. Surprisingly until ES2015, there were no classes in JavaScript forcing us to use the earlier prototypal chain model. Under the hood, Javascript classes use prototypal inheritance so only the sugar on top has changed.

I’ve reworked some of the prototypal inheritance demos to classes and added some additional things that will make life easier.

The base Employee class has the basic information for all the people that we will work with.

Because we will change the department for an individual we create a getter/setter pair to set and retrieve the value of the property, dept in this case.

We also set two class methods: greeting() and farewell.

class Employee {
  // default constructor
  constructor(first, last, dept) {
    this.name = {
      first,
      last
    };
    this._dept = dept;
  }

  // Getters and setters

  get dept() {
    return this._dept;
  }

  set dept(newDept) {
    this._dept = newDept;
  }

  // class methods

  greeting() {
    console.log(`Hi! I'm ${this.name.first}`);
  }

  farewell() {
    console.log(`${this.name.first} has left the building. Bye for now!`);
  }
}

We instantiate

let han = new Employee('Han', 'Solo');
han._dept = 'smuggling';
console.log(han.dept);
han.greeting();
// Hi! I'm Han

let leia = new Employee('Leia', 'Organa');
leia._dept = 'politics';
console.log(leia.dept);
leia.farewell();
// Leia has left the building. Bye for now

We can create new classes that extend from our base Employee class by using the extends keyword and the name of the class we want to base it on, and using the super() method in the constructor with the name of the fields from the parent class we want to use.

class SalesPerson extends Employee {
  constructor(first, last, dept, quota) {
    // calls the parent class constructor
    // with only the items we want to
    super(first, last);
    // override the dept item that we didn't
    // take from the constructor
    this.dept = 'sales';
    // set the quota
    this.quota = 100;
  }

  deptWork() {
    console.log(`${this.name.last} works in the ${this.dept}`);
  }
}

The SalesPerson class uses values from Employee and adds functionality that goes beyond the parent class. We didn’t write greeting() and farewell() methods for SalesPerson but they are available as part of Employee, so we can still use them, we get them “for free” in the derived class.

let snape = new SalesPerson('Severus', 'Snape');
snape.greeting();
snape.deptWork();
snape.farewell();

We’ll use the WorkerBee class to extend Employee and Engineer to extend WorkerBee. You can say that a WorkerBee is an Employee and an Engineer is a WorkerBee.

The WorkerBee class introduces one additional element to Employee. projects will list the projects this particular WorkerBee is working on.

class WorkerBee extends Employee {
  constructor(first, last, dept, projects) {
    super(first, last, dept);
    this.projects = [];
  }
}

When we instantiate a new WorkerBee, we get all the methods, setters and getters from Employee without having to duplicate any of the functionality

let newbee = new WorkerBee('buble', 'bee', 'working', 'cleanup')

newbee.greeting();
newbee._dept = 'accountring';
console.log(newbee.dept);
newbee.farewell();

Engineer is a child of WorkerBee. It adds its own attribute, getter, setter and class method.

class Engineer extends WorkerBee {
  constructor(first, last, dept, projects, language) {
    super(first, last, dept, projects);
    this._language = language;
  }

  get language() {
    return this._language;
  }

  set language(newLanguage) {
    this._language = newLanguage;
  }

  programIn() {
    console.log(`${this.name.last} programs in ${this._language}`)
  }
}

When we instantiate a new Engineer we get objects from all its ancestors available without having to write additional or duplicate code.

let pip = new Engineer('engineer', 'dude', 'SRE', 'DEVOPS');

pip.greeting();
pip._language = 'Go';
pip.programIn();
pip.farewell();

Public and Private Class Fields

While classes work well as they are now, TC 39 introduced a couple of additional features that make working with classes smoother and without having to necessarily use the class constructor.

Public class fields provide an easier way to provide name and values for class variables.

Private class fields provide a way to encapsulate data so it’s only available in the body of the class and causing an error when you attempt to use it outside.

Class fields (both public and private) are at Stage 3 in the TC39 process. They are already implemented in V8.

See Mathias Bynens article Public and private class fields for more information.