Skip to content

Add scatterternarygl trace - #8083

Open
Lexachoc wants to merge 12 commits into
plotly:mainfrom
Lexachoc:feature-scatterternarygl
Open

Lexachoc wants to merge 12 commits into
plotly:mainfrom
Lexachoc:feature-scatterternarygl

Conversation

@Lexachoc

@Lexachoc Lexachoc commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #7058

Ternary diagrams are widely used in materials science, for example for glass compositions (shown in #7058) and multicomponent phase diagrams. Platforms such as the Materials Project use interactive Plotly-based phase diagram for multicomponent materials systems.

This PR adds a new scatterternarygl trace type for rendering ternary scatter plots with WebGL.

It reuses the existing scattergl rendering logic and adds ternary-specific coordinate handling, clipping, hover/select support, strict bundle support, schema/types, regl precompiled commands, tests (to be added), and image mocks.

The goal is to provide a WebGL-based alternative to scatterternary, especially for larger datasets (thousands) showing in #7058.

Ternary diagrams are widely used in materials science, for example for glass compositions (as shown in #7058) and multicomponent phase diagrams.

Platforms such as the Materials Project use Plotly-based phase diagrams for multicomponent materials systems, while materials data platforms such as SciGlass Next also work with large glass composition datasets where ternary visualization is very useful.

This PR adds a new scatterternarygl trace type for rendering ternary scatter plots with WebGL.

It reuses the existing scattergl rendering logic and adds ternary-specific coordinate handling, clipping, hover/select support, strict bundle support, schema/types, regl precompiled commands, tests (to be added), and image mocks.

The goal is to provide a WebGL-based alternative to scatterternary, especially for larger datasets with thousands of points, as shown in #7058.


Current limitation

There is no hoveron attribute for scattergl. Since scatterternarygl reuses much of the scattergl implementation, it will not support hoveron either. I have no intention of supporting it specifically in this PR, as this should be addressed upstream.

@Lexachoc
Lexachoc force-pushed the feature-scatterternarygl branch from 6f072c6 to 1d66cc2 Compare September 25, 2026 23:39
@Lexachoc

Copy link
Copy Markdown
Contributor Author

I’ll add the jasmine tests after the initial review, so I can incorporate any feedback or requested changes first. If there are any specific tests you’d like to see covered, please let me know.


One known issue is some visible aliasing/jagged edges in the WebGL rendering. I’ll add a comparison screenshot below to show the difference more clearly. Feedback on whether this should be addressed in this PR would be very helpful.

(Same data from the example page: Soil Types Ternary Plot)

image

@Lexachoc

Copy link
Copy Markdown
Contributor Author

It would be helpful if #8041 could be reviewed first, so this PR can also include the ternary spikeline support from that work.
I’ve tested the two changes together locally, and the initial result looks good so far (if I haven't missed anything).

@Lexachoc

Lexachoc commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

UPDATE

The spikelines feature for ternary plots (PR #8041) has now been merged.

Below is an example demonstrating scatterternarygl with spikelines using 1M points.

Reproduce with devtools

Open:

http://localhost:3000/devtools/test_dashboard/index.html

Then run in the console:

function gaussianRand() {
    var rand = 0;
    for (var i = 0; i < 6; i += 1) {
        rand += Math.random();
    }
    return (rand / 6) - 0.5;
}

var A = [],
    B = [],
    C = [],
    n = 1000000,
    i;

for (i = 0; i < n; i += 1) {
    var a = gaussianRand() + 0.5;
    var b = gaussianRand() + 0.5;
    var c = gaussianRand() + 0.5;
    var sum = a + b + c;

    A.push(a);
    B.push(b);
    C.push(c);
}

var data = [{
    type: 'scatterternarygl',
    mode: 'markers',
    a: A,
    b: B,
    c: C,
    marker: {
        line: {
            width: 1,
            color: '#404040'
        }
    }
}];

var layout = {
    title: {text: 'scatterternarygl - 1M points with spikelines'},
    ternary: {
        aaxis: {
            spikecolor: 'oklch(65% 0.25 25)', // any color
            spikesnap: 'cursor', // 'data' | 'cursor' | 'hovered data'
            spikedash: '5px,10px,2px,2px', // "solid", "dot", "dash", "longdash", "dashdot", or "longdashdot") or a dash length list in px (eg "5px,10px,2px,2px")
            spikemode: 'toaxis', // 'toaxis' | 'across' | 'marker'
            spikethickness: 1, // any number
        },
        baxis: {
            spikecolor: '#008000',
            spikesnap: 'cursor',
            spikedash: 'solid',
            spikemode: 'toaxis',
            spikethickness: 2,
        },
        caxis: {
            spikecolor: '', // default to marker color
            spikesnap: 'data', // default
            spikedash: 'dash', // default
            spikemode: 'toaxis', // default
            spikethickness: 3, // default
        }
    },
    hovermode: 'closest',
};

Plotly.newPlot(gd, data, layout);

Move the mouse over the ternary plot to see the spikelines and corresponding axis value labels.

Screenshot

image

@Lexachoc

Lexachoc commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Test: Hover performance with 100,000 points

Since scatterternarygl reuses the spatial tree and hover search logic from scattergl, it inherits the same performance limitation described in #8134.

When using 100,000 points arranged along a horizontal or vertical line, hovering becomes noticeably laggy. Reducing the number of points to 99,999 makes hovering much smoother.

export const TOO_MANY_POINTS = 1e5;

Interestingly, with 100,000 randomly distributed points, hovering remains smooth.

Reproduce with devtools

Open:

http://localhost:3000/devtools/test_dashboard/index.html

Then run in the console:

var n = 100000 - 0 ; // try - 1 to feel the difference
var pattern = 'horizontal'; // 'horizontal' | 'vertical' | 'random'

// NOTE:
// when using 100K points with random pattern, hovering is smooth

var A = [],
    B = [],
    C = [];

for (var i = 0; i < n; i++) {
    var t = i / (n - 1);
    var a, b, c;

    if (pattern === 'horizontal') {
        a = 0.5;
        b = 0.5 * t;
        c = 0.5 - b;
    } else if (pattern === 'vertical') {
        a = t;
        b = (1 - t) / 2;
        c = b;
    } else {
        var r1 = Math.sqrt(Math.random());
        var r2 = Math.random();

        a = 1 - r1;
        b = r1 * (1 - r2);
        c = r1 * r2;
    }

    A.push(a);
    B.push(b);
    C.push(c);
}

var data = [{
    type: 'scatterternarygl',
    mode: 'markers',
    a: A,
    b: B,
    c: C,
}];

var layout = {
    title: {text: pattern + ' - ' + n + ' points'},
};

Plotly.newPlot(gd, data, layout).then(function() {
    var stash = gd.calcdata[0][0].t;

    console.log({
        points: n,
        pattern: pattern,
        hasTree: !!stash.tree,
        hasIds: !!stash.ids
    });
});

Screenshots

image image

Note

This is an upstream performance issue with scattergl, so I just ignore this in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add scatterternarygl trace for ternary plot

2 participants