Skip to main content
Version: 6.1.2

Scrolling surface

A variant of Surface, this feature is suitable for streaming applications where data is measured and visualized in real-time.

Chart with scrolling surface series Chart with scrolling surface series

Scrolling surfaces work same as Static surfaces](/6.1.2/features/d3/surface) for the most part. The main differences are creation syntax and data input.

// Creation of scrolling surface series
const surfaceSeries = chart.addSurfaceScrollingGridSeries({
// Scroll along X axis
scrollDimension: 'columns',
// Each sample has 5 values
rows: 5,
// Number of samples to keep in memory
columns: 100,
})
.setStart({ x: 0, z: 0 })
.setStep({ x: 1, z: 1 })

Data input

A sample is added to a scrolling surface with addValues method:

surfaceSeries.addValues({
yValues: [0, 1, 2, 3, 4]
})

Alternatively, invalidateValues can also be used. This lets you control which sample index you are defining.

Color by separate data set

See static surface guide on topic here](/6.1.2/features/d3/surface). If you want to use the same approach with a scrolling surface series, then supply intensity values like this:

surfaceSeries.addValues({
yValues: [0, 1, 2, 3, 4],
intensityValues: [100, 110, 90, 80, 20]
})

Using timestamps

Most use cases of scrolling surfaces involve timestamped data. In these cases, samples realistically don't always arrive with the same time step. Generally this is worked around by configuring a "minimum perceived time step" for the surface:

// Minimum time step that can be displayed by the surface
// Smaller value means more precision but more RAM and GPU memory usage.
const surfaceMinTimeStepMs = 1 // 1 millisecond
surfaceSeries.setStep({ x: surfaceMinTimeStepMs, z: 1 })

Then, input data is mapped from timestamps to "surface sample index", and inserted to surface using invalidateValues method. This also works even if data doesn't come in correct time order.

let tFirstSample: number | undefined
const handleIncomingData = (timestamp, sample) => {
if (!tFirstSample) {
tFirstSample = timestamp
// Using date origin is required to display time series data in range of hours, minutes, seconds or lower
chart.axisX.setTickStrategy(AxisTickStrategies.DateTime, (strategy) =>
strategy.setDateOrigin(new Date(tFirstSample)),
)
}
// Calculate sample index from timestamp to place sample in correct location in surface.
const iSample = Math.round((timestamp - tFirstSample) / surfaceMinTimeStepMs)
surfaceSeries.invalidateIntensityValues({
iSample,
values: [sample],
})
}

The series automatically fills any gaps between samples by repeating the previous sample value. So, actually 1 sample can occupy several data slots in the series.

info

If the axis doesn't let you zoom in far enough, consider enabling "high precision" axis](/6.1.2/features/axis/#zoom-ability)

3D color shading

This section works the same as for Line, to avoid duplication of guides, please refer to the section under Line](/6.1.2/features/d3/line)

Depth testing

This section works the same as for Line, to avoid duplication of guides, please refer to the section under Line](/6.1.2/features/d3/line)

Interactions with data points

This section works the same as for Line, to avoid duplication of guides, please refer to the section under Line](/6.1.2/features/d3/line)