Surface
Chart3D includes a feature for rendering high resolution 3D surfaces.
This can mean up to millions or even billions of data points displayed at high speed and colored according to user configured color rules.

This section is about so called "static surfaces", for scrolling and time heatmaps see Scrolling surface](/6.1.2/features/d3/scrolling-surface).
With LightningChart, Surface charts are extremely similar to Heatmaps. As such, we recommend you to also check out the Heatmap guide](/6.1.2/features/xy/heatmap).
// Creation of surface series
const surfaceSeries = chart
.addSurfaceGridSeries({
columns: 3,
rows: 4,
dataOrder: 'columns',
})
.setStart({ x: 0, z: 0 })
.setStep({ x: 1, z: 1 })
Surface height map
Surface series consume height data as uniform number matrixes. You can imagine this as a table of numbers, where each cell is an Y coordinate:
| column 0 | column 1 | column 2 | |
|---|---|---|---|
| row 0 | 0 | 4 | 8 |
| row 1 | 1 | 5 | 9 |
| row 2 | 2 | 6 | 10 |
| row 3 | 3 | 7 | 11 |
surfaceSeries.invalidateHeightMap([
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11],
])
"uniform" means that when you attach this data table to a pair of X and Z axes, the X and Z step between each column and row is always the same.
Color by height
The most common way of coloring a surface is to color each location along the surface by its Y coordinate.
surfaceSeries.setFillStyle(new PalettedFill({
lookUpProperty: 'y',
lut: new LUT({
interpolate: true,
steps: [
{ value: 0, color: ColorCSS('red') },
{ value: 100, color: ColorCSS('green') },
]
})
}))
const legend = chart.addLegendBox().add(chart)
Color by separate data set
Surfaces can also be colored by a separate data set from the height map. As in, each coordinate in the surface can have separate Y coordinate as well as other number Value which is used for coloring.
surfaceSeries.setFillStyle(new PalettedFill({
lookUpProperty: 'value',
lut: new LUT({
interpolate: true,
steps: [
{ value: 0, color: ColorRGBA(255, 0, 0) },
{ value: 10, color: ColorRGBA(0, 0, 255) },
],
}),
}))
// NOTE: Intensity values do not affect surface geometry, but only coloring!
surfaceSeries.invalidateIntensityValues([
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11],
])
const legend = chart.addLegendBox().add(chart)
Percentage color lookup
LUT has also a percentageValues option, which you can use if you want to use % values of the present min-max lookup value range, rather than specific value steps:
new LUT({
percentageValues: true,
interpolate: true,
steps: [
// 0 = 0 %, 1 = 100%
{ value: 0, color: ColorCSS('red') },
{ value: 1, color: ColorCSS('green') },
]
})
For more details about style API, please see Styles, colors and fonts](/6.1.2/more-guides/styles-colors-and-fonts).
Intensity interpolation
// Enable intensity interpolation
surfaceSeries.setIntensityInterpolation('bilinear')
// Disable intensity interpolation
surfaceSeries.setIntensityInterpolation(undefined)
Disable wireframe
surfaceSeries.setWireframeStyle(emptyLine)