2D/3D Data Visualization with Ray Tracing in JavaScript – MorphCharts

Category: Chart & Graph , Javascript | February 21, 2026
Authormicrosoft
Last UpdateFebruary 21, 2026
LicenseMIT
Tags
Views70 views
2D/3D Data Visualization with Ray Tracing in JavaScript – MorphCharts

MorphCharts is an open-source JavaScript visualization library that creates rich 2D and 3D data visualizations using ray tracing and procedural geometry. Developed by Microsoft.

The library takes a dual approach to chart creation. You can define visualizations using a JSON specification based on an extended subset of the VEGA grammar, or you can build them programmatically using the Core library with a Renderer.

This means you can prototype quickly with JSON specs or integrate deeply into your application’s architecture with code-based implementations.

Features:

  • Ray-traced rendering pipeline: Renders charts using path tracing with global illumination, supporting diffuse, metal, glass, glossy, and emissive materials for photorealistic visualizations.
  • Multiple geometric primitives: Supports rectangles, cuboids, spheres, cylinders, hex prisms, ring segments, and torus segments for diverse visualization types.
  • Flexible data sources: Load data inline through JSON definitions or link to external CSV files via URLs, with local file support in the web client.
  • WebGPU acceleration: Uses GPU rendering through WebGPU for fast ray tracing and multiple render passes, including color, normal maps, depth maps, and edge detection.
  • Arbitrary resolution output: Generate visualizations at any resolution, including 4K single images or larger outputs through tiled rendering that can be stitched together.
  • Material and texture support: Apply solid colors or textures to any geometric primitive with full material property control for realistic surface appearances.

Installation

1. Clone the repository and install dependencies:

git clone https://github.com/microsoft/morphcharts.git
cd morphcharts
npm install

2. Build and start the client. The build process compiles all components and starts a local web server. The start_client command automatically opens client.html in your default browser, where you can immediately start creating visualizations through the web interface.

npm run build_client
npm run start_client

3. Create Visualizations with JSON Specifications. This approach extends VEGA grammar with 3D support. Here’s a 3D bar chart example:

{
  "title": "3D Stacked Bar Chart",
  "description": "A 3D stacked bar chart",
  "width": 640,
  "height": 360,
    
  "camera": {"position": [0, 0.6, 1.2]},
    
  "data": [ 
    {
      "name": "table",
      "values": [
        {"category": "A", "subcategory": "One", "amount": 42},
        {"category": "B", "subcategory": "One", "amount": 87},
        {"category": "C", "subcategory": "One", "amount": 15},
        {"category": "D", "subcategory": "One", "amount": 63},
        {"category": "E", "subcategory": "One", "amount": 29},
        {"category": "F", "subcategory": "One", "amount": 91},
        {"category": "G", "subcategory": "One", "amount": 8},
        {"category": "H", "subcategory": "One", "amount": 76},
        {"category": "I", "subcategory": "One", "amount": 54},
        {"category": "J", "subcategory": "One", "amount": 33},
        {"category": "A", "subcategory": "Two", "amount": 67},
        {"category": "B", "subcategory": "Two", "amount": 23},
        {"category": "C", "subcategory": "Two", "amount": 94},
        {"category": "D", "subcategory": "Two", "amount": 38},
        {"category": "E", "subcategory": "Two", "amount": 81},
        {"category": "F", "subcategory": "Two", "amount": 12},
        {"category": "G", "subcategory": "Two", "amount": 56},
        {"category": "H", "subcategory": "Two", "amount": 33},
        {"category": "I", "subcategory": "Two", "amount": 70},
        {"category": "J", "subcategory": "Two", "amount": 19},
        {"category": "A", "subcategory": "Three", "amount": 25},
        {"category": "B", "subcategory": "Three", "amount": 59},
        {"category": "C", "subcategory": "Three", "amount": 11},
        {"category": "D", "subcategory": "Three", "amount": 72},
        {"category": "E", "subcategory": "Three", "amount": 44},
        {"category": "F", "subcategory": "Three", "amount": 60},
        {"category": "G", "subcategory": "Three", "amount": 39},
        {"category": "H", "subcategory": "Three", "amount": 90},
        {"category": "I", "subcategory": "Three", "amount": 18},
        {"category": "J", "subcategory": "Three", "amount": 77}
      ],
      "transform": [
        {
          "type": "stack",
          "groupby": ["category"],
          "sort": {"field": "subcategory"},
          "field": "amount",
          "as": ["y0", "y1"]
        }
      ]
    },
    {
      "name": "aggregate",
      "source": "table",
      "transform": [
        {
          "type": "aggregate",
          "groupby": ["category"],
          "ops": ["sum"],
          "fields": ["amount"],
          "as": ["total"]
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "xscale", 
      "type": "band", 
      "_domain": {"data": "table", "field": "category"},
      "domain": {
        "data": "aggregate",
        "field": "category",
        "sort": {"field": "total", "op": "max", "order": "ascending"}
      },
      "range": "width",
      "padding": 0.25
    }, 
    {
      "name": "yscale",
      "type": "linear",
      "domain": {"data": "table", "field": "y1"},
      "nice": true,
      "range": "height"
    },
    {
      "name": "color",
      "type": "ordinal",
      "range": "category",
      "domain": {"data": "table", "field": "subcategory"}
    }
  ],
  "axes": [
    {
      "orient": "bottom",
      "scale": "xscale",
      "labelBaseline": "top",
      "labelOffsetY": 0.1,
      "labelOffsetZ": 32,
      "labelAngleX": 90,
      "title": "Category",
      "titleOffsetZ": 64,
      "titleOffsetY": 0.1,
      "titleAngleX": 90,
      "domain": false
    },
    {
      "orient": "left",
      "orientZ": "back",
      "scale": "yscale",
      "grid": true,
      "gridWidth": 0.1,
      "tickCount": 10,
      "labelAlign": "right",
      "labelOffsetX": -2,
      "labelOffsetY": 4,
      "title": "Amount",
      "titleOffsetX": -32,
      "titleAngle": -90
    }
  ],
 
  "marks": [
    {
      "type": "rect",
      "geometry": "xzrect",
      "material": "glossy",
      "encode": {
        "enter": {
          "xc": {"signal": "width/2"},
          "width": {"signal": "width*2"},
          "depth": {"signal": "width*2"},
          "fuzz": {"value": 0.1},
          "fill": {"value": "white"}
        }
      }
    },
    {
      "type": "rect",
      "geometry": "cylindersdf",
      "material": "glossy",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "xscale", "field": "category"},
          "width": {"scale": "xscale", "band": 1},
          "y": {"scale": "yscale", "field": "y0"},
          "y2": {"scale": "yscale", "field": "y1"},
          "rounding": {"scale": "xscale", "band": 0.025},
          "fill": {"scale": "color", "field": "subcategory"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "x": {"scale": "xscale", "field": "category", "band": 0.5},
          "y": {"scale": "yscale", "signal": "(datum.y0+datum.y1)/2"},
          "z": {"scale": "xscale", "band": 0.5},
          "fill": {"value": "white"},
          "text": {"field": "amount"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "aggregate"},
      "encode": {
        "enter": {
          "align": {"value": "center"},
          "baseline": {"value": "bottom"},
          "x": {"scale": "xscale", "field": "category", "band": 0.5},
          "y": {"scale": "yscale", "field": "total"},
          "stroke": {"value": "white"},
          "strokeWidth": {"value": 0.5},
          "text": {"field": "total"}
        }
      }
    }
  ]
}

Changelog:

02/21/2026

  • Update

You Might Be Interested In:


Leave a Reply