diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/CMakeLists.txt b/Examples/Emscripten/Cxx/ConeMultiBackend/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..905b5a590029b0f9eff5b022fe3ddb10b3131e4e
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/CMakeLists.txt
@@ -0,0 +1,180 @@
+cmake_minimum_required(VERSION 3.13)
+project(ConeMultiBackend)
+
+# -----------------------------------------------------------------------------
+# EMSCRIPTEN only
+# -----------------------------------------------------------------------------
+
+if (NOT EMSCRIPTEN)
+  message("Skipping example: This needs to run inside an Emscripten build environment")
+  return ()
+endif ()
+
+# -----------------------------------------------------------------------------
+# Handle VTK dependency
+# -----------------------------------------------------------------------------
+
+find_package(VTK
+  COMPONENTS
+    FiltersSources      # VTK pipeline
+    InteractionStyle    # Mouse handling
+    RenderingOpenGL2    # For Rendering with OpenGL
+    RenderingWebGPU     # For Rendering with WebGPU
+    RenderingUI         # For SDL2 Window
+)
+
+if (NOT VTK_FOUND)
+  message("Skipping example: ${VTK_NOT_FOUND_MESSAGE}")
+  return ()
+endif ()
+
+# -----------------------------------------------------------------------------
+# Compile example code
+# -----------------------------------------------------------------------------
+
+add_executable(ConeMultiBackend ConeMultiBackend.cxx)
+
+target_link_libraries(ConeMultiBackend
+  PRIVATE
+    VTK::FiltersSources
+    VTK::InteractionStyle
+    VTK::RenderingOpenGL2
+    VTK::RenderingWebGPU
+    VTK::RenderingUI
+)
+
+# -----------------------------------------------------------------------------
+# WebAssembly build options
+# -----------------------------------------------------------------------------
+set(emscripten_link_options)
+set(emscripten_compile_options)
+
+list(APPEND emscripten_link_options
+  "-sWASM=1"
+  "-sMODULARIZE=1"
+  "-sALLOW_MEMORY_GROWTH=1"
+  "-sEXPORT_NAME=createConeMultiBackendModule"
+  "-sEXPORTED_RUNTIME_METHODS=['ENV']" # ENV holds the environment variables accessible by C getenv
+)
+
+set(emscripten_debug_options)
+set(DEBUGINFO "PROFILE" CACHE STRING "Type of debug info")
+set_property(CACHE DEBUGINFO PROPERTY
+  STRINGS
+    NONE              # -g0
+    READABLE_JS       # -g1
+    PROFILE           # -g2
+    DEBUG_NATIVE      # -g3
+)
+
+if(DEBUGINFO STREQUAL "NONE")
+  list(APPEND emscripten_debug_options
+    "-g0"
+  )
+elseif(DEBUGINFO STREQUAL "READABLE_JS")
+  list(APPEND emscripten_debug_options
+    "-g1"
+  )
+  list(APPEND emscripten_link_options
+    "-sDEMANGLE_SUPPORT=1"
+  )
+elseif(DEBUGINFO STREQUAL "PROFILE")
+  list(APPEND emscripten_debug_options
+    "-g2"
+  )
+  list(APPEND emscripten_link_options
+    "-sDEMANGLE_SUPPORT=1"
+  )
+elseif(DEBUGINFO STREQUAL "DEBUG_NATIVE")
+  list(APPEND emscripten_debug_options
+    "-g3"
+  )
+  list(APPEND emscripten_link_options
+    "-sASSERTIONS=1"
+    "-sDEMANGLE_SUPPORT=1"
+  )
+endif()
+
+# -----------------------------------------------------------------------------
+# Build options
+# -----------------------------------------------------------------------------
+set(emscripten_optimizations)
+set(OPTIMIZE "SMALLEST_WITH_CLOSURE" CACHE STRING "Emscripten optimization")
+set_property(CACHE OPTIMIZE PROPERTY
+  STRINGS
+    NO_OPTIMIZATION       # -O0
+    LITTLE                # -O1
+    MORE                  # -O2
+    BEST                  # -O3
+    SMALL                 # -Os
+    SMALLEST              # -Oz
+    SMALLEST_WITH_CLOSURE # -Oz --closure 1
+)
+
+if(OPTIMIZE STREQUAL "NO_OPTIMIZATION")
+  list(APPEND emscripten_optimizations
+    "-O0"
+  )
+elseif(OPTIMIZE STREQUAL "LITTLE")
+  list(APPEND emscripten_optimizations
+    "-O1"
+  )
+elseif(OPTIMIZE STREQUAL "MORE")
+  list(APPEND emscripten_optimizations
+    "-O2"
+  )
+elseif(OPTIMIZE STREQUAL "BEST")
+  list(APPEND emscripten_optimizations
+    "-O3"
+  )
+elseif(OPTIMIZE STREQUAL "SMALL")
+  list(APPEND emscripten_optimizations
+    "-Os"
+  )
+elseif(OPTIMIZE STREQUAL "SMALLEST")
+  list(APPEND emscripten_optimizations
+    "-Oz"
+  )
+elseif(OPTIMIZE STREQUAL "SMALLEST_WITH_CLOSURE")
+  list(APPEND emscripten_optimizations
+    "-Oz"
+  )
+  list(APPEND emscripten_link_options
+    "--closure 1"
+  )
+endif()
+
+target_compile_options(ConeMultiBackend
+  PUBLIC
+    ${emscripten_compile_options}
+    ${emscripten_optimizations}
+    ${emscripten_debug_options}
+)
+
+target_link_options(ConeMultiBackend
+  PUBLIC
+    ${emscripten_link_options}
+    ${emscripten_optimizations}
+    ${emscripten_debug_options}
+)
+
+# -----------------------------------------------------------------------------
+# VTK modules initialization
+# -----------------------------------------------------------------------------
+
+vtk_module_autoinit(
+  TARGETS  ConeMultiBackend
+  MODULES  ${VTK_LIBRARIES}
+)
+
+# -----------------------------------------------------------------------------
+# Copy HTML to build directory
+# -----------------------------------------------------------------------------
+
+add_custom_command(
+  TARGET ConeMultiBackend
+  COMMAND
+    ${CMAKE_COMMAND} -E copy_directory
+      "${CMAKE_CURRENT_SOURCE_DIR}/web"
+      "${CMAKE_CURRENT_BINARY_DIR}"
+)
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/ConeMultiBackend.cxx b/Examples/Emscripten/Cxx/ConeMultiBackend/ConeMultiBackend.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2e979c07e559b57f47c390655f92d187ecbea223
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/ConeMultiBackend.cxx
@@ -0,0 +1,113 @@
+/*=========================================================================
+  Program:   Visualization Toolkit
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+=========================================================================*/
+
+#include "vtkActor.h"
+#include "vtkCellData.h"
+#include "vtkConeSource.h"
+#include "vtkInteractorStyleTrackballCamera.h"
+#include "vtkMinimalStandardRandomSequence.h"
+#include "vtkNew.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkRenderer.h"
+
+//------------------------------------------------------------------------------
+// Main
+//------------------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+  // Create a renderer, render window, and interactor
+  vtkNew<vtkRenderer> renderer;
+  vtkNew<vtkRenderWindow> renderWindow;
+  renderWindow->SetMultiSamples(0);
+  renderWindow->AddRenderer(renderer);
+
+  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
+  renderWindowInteractor->SetRenderWindow(renderWindow);
+
+  vtkNew<vtkInteractorStyleTrackballCamera> style;
+  renderWindowInteractor->SetInteractorStyle(style);
+  style->SetDefaultRenderer(renderer);
+
+  vtkNew<vtkMinimalStandardRandomSequence> seq;
+
+  double spacingX = 2.0, spacingY = 2.0, spacingZ = 2.0;
+
+  const int nx = std::atoi(argv[1]);
+  const int ny = std::atoi(argv[2]);
+  const int nz = std::atoi(argv[3]);
+  const int mapperIsStatic = std::atoi(argv[4]);
+
+  double x = 0.0, y = 0.0, z = 0.0;
+  for (int k = 0; k < nz; ++k)
+  {
+    for (int j = 0; j < ny; ++j)
+    {
+      for (int i = 0; i < nx; ++i)
+      {
+        vtkNew<vtkConeSource> coneSrc;
+        coneSrc->SetResolution(10);
+        // position the cone
+        coneSrc->SetCenter(x, y, z);
+
+        coneSrc->Update();
+        vtkPolyData* cone = coneSrc->GetOutput();
+
+        // generate random colors for each face of the cone.
+        vtkNew<vtkUnsignedCharArray> colors;
+        colors->SetNumberOfComponents(4);
+        seq->SetSeed(k * ny * nx + j * nx + i);
+        for (vtkIdType cellId = 0; cellId < cone->GetNumberOfPolys(); ++cellId)
+        {
+          double red = seq->GetNextRangeValue(0, 255.);
+          double green = seq->GetNextRangeValue(0, 255.);
+          double blue = seq->GetNextRangeValue(0, 255.);
+          colors->InsertNextTuple4(red, green, blue, 255);
+        }
+        cone->GetCellData()->SetScalars(colors);
+
+        vtkNew<vtkPolyDataMapper> mapper;
+        mapper->SetInputData(cone);
+        mapper->Update();
+        mapper->SetStatic(mapperIsStatic);
+
+        vtkNew<vtkActor> actor;
+        actor->SetMapper(mapper);
+        actor->GetProperty()->SetEdgeVisibility(1);
+        actor->GetProperty()->SetEdgeColor(1.0, 1.0, 1.0);
+        mapper->Update();
+        actor->SetOrigin(x, y, z);
+        actor->RotateZ(i * j);
+        renderer->AddActor(actor);
+
+        x += spacingX;
+      }
+      x = 0.0;
+      y += spacingY;
+    }
+    y = 0.0;
+    z += spacingZ;
+  }
+  std::cout << "Created " << nx * ny * nz << " cones" << std::endl;
+
+  // Start rendering app
+  renderer->SetBackground(0.2, 0.3, 0.4);
+  renderWindow->SetSize(300, 300);
+  renderWindow->Render();
+
+  // Start event loop
+  renderWindowInteractor->Start();
+
+  return 0;
+}
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/README.md b/Examples/Emscripten/Cxx/ConeMultiBackend/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..eccba2845a6d258fd56d3c21b6bcd14bc576474c
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/README.md
@@ -0,0 +1,48 @@
+# WebAssembly Cone Example
+
+This example aims to provide a base example on how to write a VTK viewer for
+WebAssembly with both OpenGL and WebGPU backends. It shows how an application can
+use a single wasm binary to serve using both the backends. In this example, the UI is very minimal.
+
+The additional steps required are:
+
+1. Update `CMakeLists.txt` so that the `VTK::RenderingWebGPU` component is requested when finding VTK.
+2. Link your targets with `RenderingWebGPU`. There is no problem linking to both `RenderingWebGPU` and `RenderingOpenGL2`.
+3. Preload environment variable with `VTK_GRAPHICS_BACKEND` set to 'WEBGPU'.
+
+## Compiling example against VTK
+
+We assume inside the `work/` directory to find the source of VTK under `src/`
+and its build tree under `build-vtk-wasm`.
+
+If VTK is not built yet, please follow the guide `../README.md`.
+
+Let's create the build directory for our example
+
+```
+mkdir -p work/build
+```
+
+Start docker inside that working directory
+
+```
+docker run --rm --entrypoint /bin/bash -v $PWD:/work -p 8000:8000 -it dockcross/web-wasm:20230222-162287d
+
+cd /work/build
+
+emcmake cmake \
+  -G Ninja \
+  -DVTK_DIR=/work/build-vtk-wasm \
+  /work/src/Examples/Emscripten/Cxx/Cone
+
+cmake --build .
+```
+
+## Serve and test generated code
+
+```
+cd work/build
+python3 -m http.server 8000
+```
+
+Open your browser to http://localhost:8000 and click on a backend.
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_base.js b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_base.js
new file mode 100644
index 0000000000000000000000000000000000000000..fa80cf383cf81b6c643a80a9da57f130e21fc0ac
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_base.js
@@ -0,0 +1,34 @@
+export var options = {
+    nx: '10',
+    ny: '10',
+    nz: '100',
+    mapperIsStatic: '1'
+};
+
+export const BaseConfig = {
+    'canvas': (function () {
+        var canvas = document.getElementById('canvas');
+        return canvas;
+    })(),
+    'print': (function () {
+        return function (text) {
+            text = Array.prototype.slice.call(arguments).join(' ');
+            console.info(text);
+        };
+    })(),
+    'printErr': function (text) {
+        text = Array.prototype.slice.call(arguments).join(' ');
+        console.error(text);
+    }
+};
+
+export function initCanvas(canvas) {
+    // sends a resize event so that the render window fills up browser tab dimensions.
+    setTimeout(() => {
+        window.dispatchEvent(new Event('resize'));
+    }, 0);
+    // focus on the canvas to grab keyboard inputs.
+    canvas.setAttribute('tabindex', '0');
+    // grab focus when the render window region receives mouse clicks.
+    canvas.addEventListener('click', () => canvas.focus());
+}
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgl.js b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgl.js
new file mode 100644
index 0000000000000000000000000000000000000000..170b2ea90d1c350fc7af1db644f210f7e92e3d4f
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgl.js
@@ -0,0 +1,30 @@
+import { BaseConfig, options, initCanvas } from "./config_base.js";
+
+function makeWebGLConfig() {
+    var cfg = BaseConfig;
+    cfg.canvas = (function () {
+        var canvas = document.getElementById('canvas');
+        canvas.addEventListener(
+            "webglcontextlost",
+            function (e) {
+                console.error('WebGL context lost. You will need to reload the page.');
+                e.preventDefault();
+            },
+            false
+        );
+        return canvas;
+    })()
+    cfg.arguments = [options.nx, options.ny, options.nz, options.mapperIsStatic];
+    return cfg;
+}
+
+var canvas = document.createElement('canvas');
+canvas.setAttribute('id', 'canvas');
+canvas.setAttribute('class', 'canvas');
+document.body.appendChild(canvas)
+
+let cfg = makeWebGLConfig();
+
+let Runtime = await createConeMultiBackendModule(cfg);
+console.log(`WASM runtime initialized with arguments ${Runtime.arguments}`);
+initCanvas(canvas);
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgpu.js b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgpu.js
new file mode 100644
index 0000000000000000000000000000000000000000..69e76f50dd3260a046fb91ef8b0287c2e01b1c85
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/config_webgpu.js
@@ -0,0 +1,44 @@
+import { BaseConfig, options, initCanvas } from "./config_base.js";
+
+function makeWebGPUConfig() {
+    var cfg = BaseConfig;
+    cfg.canvas = (function () {
+        var canvas = document.getElementById('canvas');
+        return canvas;
+    })()
+    cfg.preRun = [function (module) {
+        // select WEBGPU backend
+        module.ENV.VTK_GRAPHICS_BACKEND = 'WEBGPU';
+    }];
+    cfg.arguments = [options.nx, options.ny, options.nz, options.mapperIsStatic];
+    return cfg;
+}
+
+if (navigator.gpu === undefined) {
+    var webgpuSupportEl = document.createElement('h1');
+    webgpuSupportEl.innerText = "Your browser does not support webgpu!";
+    document.body.appendChild(webgpuSupportEl)
+    var explainEl = document.createElement('p');
+    explainEl.innerText = "On supported browsers, maybe serve from localhost instead of ip?";
+    document.body.appendChild(explainEl)
+} else {
+    var canvas = document.createElement('canvas');
+    canvas.setAttribute('id', 'canvas');
+    canvas.setAttribute('class', 'canvas');
+    document.body.appendChild(canvas)
+
+    let cfg = makeWebGPUConfig();
+
+    let adapter = await navigator.gpu.requestAdapter();
+    console.log("Found an adapter");
+    let device = await adapter.requestDevice();
+    console.log("Obtained a device");
+
+    // Set the device from JS. This can be done in C++ as well.
+    // See https://github.com/kainino0x/webgpu-cross-platform-demo/blob/main/main.cpp#L51
+    cfg.preinitializedWebGPUDevice = device;
+
+    let Runtime = await createConeMultiBackendModule(cfg);
+    console.log(`WASM runtime initialized with arguments ${Runtime.arguments}`);
+    initCanvas(canvas);
+}
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/index.html b/Examples/Emscripten/Cxx/ConeMultiBackend/web/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..0f459a355b197e24480261cea3d541b39ccec310
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/index.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+
+<head>
+    <meta charset='utf-8' />
+    <link rel="stylesheet" href="style.css">
+</head>
+
+<body>
+    <script type='module' src='./config_base.js'></script>
+    <div class="container">
+        <div class="center">
+            <a href="webgl.html">VTK OpenGL (WebGL)</a>
+            |
+            <a href="webgpu.html">VTK WebGPU</a>
+        </div>
+    </div>
+</body>
+
+</html>
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/style.css b/Examples/Emscripten/Cxx/ConeMultiBackend/web/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..45eb881480e3fb3f58803b6162440fb5627c9918
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/style.css
@@ -0,0 +1,22 @@
+.canvas {
+    left: 0;
+    top: 0;
+    position: absolute;
+    display: block;
+}
+
+.container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: 100vh;
+}
+
+.center {
+    margin: 0;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    -ms-transform: translate(-50%, -50%);
+    transform: translate(-50%, -50%);
+}
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgl.html b/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgl.html
new file mode 100644
index 0000000000000000000000000000000000000000..7fb99da8940d8647e2ce97c4b2a37570e6ecd659
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgl.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+
+<head>
+  <meta charset='utf-8' />
+  <link rel="stylesheet" href="style.css">
+</head>
+
+<body>
+  <script type='text/javascript' src='./ConeMultiBackend.js'></script>
+  <script type='module' src='./config_webgl.js'></script>
+</body>
+
+</html>
diff --git a/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgpu.html b/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgpu.html
new file mode 100644
index 0000000000000000000000000000000000000000..66c26f6e9b1b8c999e9de3303bea361ab7813577
--- /dev/null
+++ b/Examples/Emscripten/Cxx/ConeMultiBackend/web/webgpu.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+
+<head>
+  <meta charset='utf-8' />
+  <link rel="stylesheet" href="style.css">
+</head>
+
+<body>
+  <script type='text/javascript' src='./ConeMultiBackend.js'></script>
+  <script type='module' src='./config_base.js'></script>
+  <script type='module' src='./config_webgpu.js'></script>
+</body>
+
+</html>