<head>
	<!-- Load plotly.js into the DOM -->
	<script src='https://cdn.plot.ly/plotly-3.0.0.min.js'></script>
	<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
</head>

<body>
	<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
let url = 'https://gist.githubusercontent.com/davenquinn/988167471993bc2ece29/raw/f38d9cb3dd86e315e237fde5d65e185c39c931c2/data.json';

d3.json(url, function(err, rawData) {
    if(err) throw err;

    plot(rawData);
});

function plot(rawData) {
    let data = Object.keys(rawData).map(function(k) {
        let pts = rawData[k];

        return {
            type: 'scatterternary',
            mode: 'lines',
            name: k,
            hoverinfo: 'none',
            a: pts.map(function(d) { return d.clay }),
            b: pts.map(function(d) { return d.sand }),
            c: pts.map(function(d) { return d.silt }),
            line: { color: 'gray' }
        };
    });

    let layout = {
      ternary: {
        sum: 100,
        aaxis: makeAxis('Clay', "green"),
        baxis: makeAxis('Sand', "blue"),
        caxis: makeAxis('Silt', "red")
      },
      showlegend: false,
      hoverdistance: 500,
      shapes: [{
        type: "line",
        xref: 'domain',
        yref: 'domain',
        x0: 0,
        x1: 0,
        y0: 0,
        y1: 0,
        line: {
          color: 'green',
          width: 1,
        }
      },{
        type: "line",
        xref: 'domain',
        yref: 'domain',
        x0: 0,
        x1: 0,
        y0: 0,
        y1: 0,
        line: {
          color: 'blue',
          width: 1,
        }          
      },{
        type: "line",
          xref: 'domain',
          yref: 'domain',
          x0: 0,
          x1: 0,
          y0: 0,
          y1: 0,
          line: {
            color: 'red',
            width: 1,
          }        
      }],
      annotations: [{
        xref: "domain",
        yref: "domain",
        xanchor: "right",
        yanchor: "center",
        ax: -6,
        ay: 0,
        arrowwidth: 3,
				arrowcolor: "green",
        borderpad: 5,
        visible: false,
        align: 'right',
        bgcolor: 'green',
        font: {
          color: "white"
        }
      },{
        xref: "domain",
        yref: "domain",
        xanchor: "middle",
        yanchor: "top",
        ax: 0,
        ay: 6,
        arrowwidth: 3,
				arrowcolor: "blue",
        borderpad: 5,
        visible: false,
        align: 'center',
        bgcolor: 'blue',
        font: {
          color: "white"
        }
      },{
        xref: "domain",
        yref: "domain",
        xanchor: "left",
        yanchor: "bottom",
        ax: 0,
        ay: 0,
        arrowwidth: 0,
				arrowcolor: "red",
        borderpad: 5,
        visible: false,
        align: 'left',
        bgcolor: 'red',
        font: {
          color: "white"
        }
      }]
    }
  
    let config = {
      responsive: true
    }
    
    let layer = document.getElementById("myDiv")

    Plotly.newPlot(layer, data, layout, config).then(_ => {
      Plotly.relayout(layer, {autosize: true})
      
      layer
        .on("plotly_relayout", e => {
          if(e.autosize) {            
            Plotly.update(layer, {}, {
              margin: {
                l: 0,
                t: 0,
                r: 0,
                b: 0
              }
            })
            
            const ternary = document.querySelector('.ternary .toplevel.plotbg')
            const { height: th, width: tw } = ternary.getBBox()
            const { height: lh, width: lw } = layer.getBoundingClientRect()
            
            let mv = (lh - th) / 2
            let mh = (lw - tw) / 2
            
            mh += mv < 50 ? 58 : 0
            mv += mv < 50 ? 50 : 0
            
            mv += mh < 50 ? 50 : 0
            mh += mh < 50 ? 58 : 0
                               
            Plotly.update(layer, {}, {
              margin: {
                l: mh,
                t: mv,
                r: mh,
                b: mv
              }
            })
          }
        })
        .on("plotly_click", e => {
          const xaxi = e.points[0].xaxis
          const left = layer._fullLayout.margin.l
          const x = xaxi.r2fraction(xaxi.p2r(e.event.offsetX - left))

          const yaxi = e.points[0].yaxis
          const top = layer._fullLayout.margin.t
          const y = yaxi.r2fraction(yaxi.p2r(e.event.offsetY - top))
          
          const base = layer._fullLayout.ternary.sum
          
          const { a, b, c } = xy2abc(x, y, base)
          const { A, B, C } = xyEdges(a, b, c, base)

          Plotly.relayout(layer, {
            "annotations[0].x": A.x,
            "annotations[0].y": A.y,
            "annotations[0].text": `${parseInt(a)}%`,
            "annotations[0].visible": true,
            "annotations[1].x": B.x,
            "annotations[1].y": B.y,
            "annotations[1].text": `${parseInt(b)}%`,
            "annotations[1].visible": true,
            "annotations[2].x": C.x,
            "annotations[2].y": C.y,
            "annotations[2].text": `${parseInt(c)}%`,
            "annotations[2].visible": true,
            "shapes[0].x0": x,
            "shapes[0].x1": A.x,
            "shapes[0].y0": y,
            "shapes[0].y1": A.y,
            "shapes[1].x0": x,
            "shapes[1].x1": B.x,
            "shapes[1].y0": y,
            "shapes[1].y1": B.y,
            "shapes[2].x0": x,
            "shapes[2].x1": C.x,
            "shapes[2].y0": y,
            "shapes[2].y1": C.y
          })
        })
    })
}

function makeAxis(text, color) {
  return {
    title: {
      text
    },
    color,
    ticksuffix: '%',
    min: 0.01,
    linewidth: 2,
    ticks: 'outside',
    ticklen: 8,
    showgrid: true,
  }
}

function xy2abc(x, y, base) {
    let a = base * y
    let b = base * (1 - x - y / 2)
    let c = base - a - b

    return { a, b, c }
}

function xyEdges(a, b, c, base) {
  let A = {
    x: a / 2 / base,
    y: a / base
  }
  let B = {
    x: (base - b) / base,
    y: 0
  }
  let C = {
    x: (c + (a + b) / 2) / base,
    y: (base - c) / base
  }
  
  return { A, B, C }
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.