In SVG every mark is a live DOM node, and building one costs a node, its
attributes and an appendChild. At scale that per-element cost,
not the arithmetic, is what makes a chart slow. Flip the switches and watch
the element count and the clock move together. The gaps switch moves markers
too: a point next to a null gets its own dot so a one-point segment stays
visible, which is why a chart with markers off can still build thousands of
them.
Press Run the matrix to render the current point count four times over and compare.
Same data, same colours, same marker size. One path is rasterized as a
single region, so where markers overlap they lose their individual
outlines: all the fills paint, then all the strokes, and the seams between
neighbours disappear. Dense clusters read flatter. Above a thousand points
in a chart this wide markers always overlap, so there is no point count at
which the merge is free, which is exactly why
markers.largeDatasetThreshold ships off.
A null breaks the line, and a break in SVG is just another subpath. A
gapped series is now one path element carrying one M per
segment, where it used to be one element per segment. Automatic, no
config, and pixel for pixel identical.
Markers, not nulls, dominate a big render, and a null-heavy series draws
them whether you asked or not: every isolated point gets a dot so a
one-point segment stays visible.
markers.largeDatasetThreshold merges both kinds where they
are already non-interactive, for the overlap detail above.
Past a few tens of thousands of points, stop drawing them all:
chart.dataReducer downsamples to the pixels you actually
have, and chart.renderer: 'auto' paints dense series into a
canvas instead of the DOM.