Home Try Online Gallery Docs GitHub

Simple Client

This sample is a minimal web page to render a MorphCharts scene in a browser. It creates a WebGPU ray-tracing renderer, loads a spec, and runs a render loop.

The example on this page can be tried directly using the link below. To build and run your own version, make sure you have the prerequisites in place.

Overview

  1. Import the necessary modules
  2. Add a canvas element to the page
  3. Create and initialize a renderer for the canvas
  4. Create a scene from a spec
  5. Pass the scene to the renderer
  6. Run a render loop

Complete Example

Listing 1. Simple Client example
Rendered result — a sphere on a white ground plane
Figure 1. Rendered result — a sphere on a white ground plane.

Try Online

Step-by-Step

Canvas

Add a canvas element to the page with a width and height attribute.

<canvas id="canvas" width="1280" height="720"></canvas>

Module Imports

<script type="importmap">
{
  "imports": {
    "core": "https://microsoft.github.io/morphcharts/lib/morphcharts-core.js",
    "spec": "https://microsoft.github.io/morphcharts/lib/morphcharts-spec.js",
    "webgpuraytrace": "https://microsoft.github.io/morphcharts/lib/morphcharts-webgpuraytrace.js"
  }
}
</script>
import * as Core from 'core';
import * as Spec from 'spec';
import * as WebGPURenderer from 'webgpuraytrace';

The import map and module descriptions are discussed in the prerequisites.

Create the Renderer

This example uses the WebGPU ray-tracing renderer. The constructor reads the canvas element's width and height attributes to set the render target size:

const renderer = new WebGPURenderer.Main(canvas);

If necessary, you can override the defaults with an options object:

const renderer = new WebGPURenderer.Main(canvas, {
  width: 1920,
  height: 1080,
  renderMode: "raytrace", // default
});

Initialize the Renderer

await renderer.initializeAsync();

This performs one-time setup for the renderer (e.g. GPU device creation for WebGPU) and initializes default resources needed for rendering.

Parse a Spec

Here is a minimal spec — a sphere on a white ground plane:

const specJSON = {
  "width": 640,
  "height": 360,
  "marks": [
    {
      "type": "rect",
      "geometry": "sphere",
      "material": "glossy",
      "encode": {
        "enter": {
          "xc": {"value": 320},
          "yc": {"value": 180},
          "width": {"value": 360}
        }
      }
    },
    {
      "type": "rect",
      "encode": {
        "enter": {
          "xc": {"value": 320},
          "width": {"value": 1280},
          "depth": {"value": 1280},
          "fill": {"value": "white"}
        }
      }
    }
  ]
};
Listing 2. A minimal spec.

Parse the specJSON and create a scene:

const scene = await Spec.Plot.createSceneAsync(specJSON);

Load the Scene

renderer.loadScene(scene);

Loading a scene resets the renderer's visual collections and populates them from the scene.

Render Loop

const maxSamples = 1000;
let previousTime = performance.now();
function tick(currentTime) {
  const elapsed = currentTime - previousTime;
  previousTime = currentTime;
  renderer.updateAsync(elapsed);
  renderer.renderAsync(elapsed);
  if (renderer.frameCount < maxSamples) {
    requestAnimationFrame(tick);
  }
}
requestAnimationFrame(tick);

Each frame calls updateAsync (updates state) and renderAsync (dispatches a render pass) on the renderer.

The ray-tracing renderer accumulates samples over multiple frames, so you typically run the loop until renderer.frameCount reaches a desired quality level (maxSamples in this example).