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.

Before starting, 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

<!DOCTYPE html>
<html>
<head>
    <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>
</head>
<body>
    <!-- Add a canvas -->
    <canvas id="canvas" width="1280" height="720"></canvas>

    <script type="module">
        // Module imports
        import * as Core from 'core';
        import * as Spec from 'spec';
        import * as WebGPURenderer from 'webgpuraytrace';

        // Create the renderer
        const canvas = document.getElementById('canvas');
        const renderer = new WebGPURenderer.Main(canvas);

        // Initialize the renderer
        await renderer.initializeAsync();

        // Parse a spec and create a scene
        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"}
                        }
                    }
                }
            ]
        };
        const scene = await Spec.Plot.createSceneAsync(specJSON);

        // Load the scene
        renderer.loadScene(scene);

        // Run a 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);
    </script>
</body>
</html>
Rendered result — a sphere on a white ground plane
Rendered result — a sphere on a white ground plane.

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 reads the canvas 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"}
                }
            }
        }
    ]
};

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).