
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







