bhylu.com

Advertisements

AI D3.js

Bind data to SVG marks and explicit scales.

How it works

D3 provides tools for data joins, scales and axes rather than one fixed chart widget. That flexibility also leaves you responsible for meaningful encodings and accessibility. The pinned D3 7.9.0 lab joins four synthetic records to circles. The chart is cleared before redrawing so repeated runs do not duplicate marks.

Run the code

This complete HTML document is a separate learning lab. Download download-labs.mjs into an empty folder and run node download-labs.mjs. For an unpublished local preview use node download-labs.mjs http://127.0.0.1:8000/examples/ai/. It saves hash-checked sources, pinned vendor assets and licence notices into a new bhylu-ai-lab folder and refuses an existing destination. Review the files, enter that folder, run node serve.mjs and open http://127.0.0.1:8138/d3js.html. Select Run explicitly. The server is read-only, binds loopback and stops with Ctrl+C. It changes no PHP, Apache or database settings.

The reviewed browser baselines are TensorFlow.js 4.22.0, tfjs-vis 1.5.1, Brain.js 2.0.0-beta.24 (prerelease), Chart.js 4.5.1, D3 7.9.0 and Plotly.js 3.1.0. Only the libraries listed by this document load into the separate learning page. The plain JavaScript/Python playground does not supply them. TensorFlow.js labs select the CPU backend; no GPU support or speed claim follows.

Download the complete d3js.html

Example

d3js.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Bhylu AI - D3.js</title>
    <style>
      body {
        font: 16px/1.6 system-ui;
        max-width: 760px;
        margin: 32px auto;
        padding: 0 20px;
        color: #0f3464;
        background: #fff;
      }
      button {
        font: inherit;
        padding: 8px 20px;
        background: #0078d4;
        color: white;
        border: 0;
        border-radius: 6px;
      }
      pre {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
        background: #f3f7fc;
        padding: 16px;
      }
      canvas,
      svg,
      #chart {
        max-width: 100%;
      }
    </style>
  </head>
  <body>
    <h1>D3.js</h1>
    <p>Bind data to SVG marks and explicit scales.</p>
    <p>
      This separate synthetic-data lab runs only when you select Run. It is not a model
      service or a production benchmark.
    </p>
    <p>Horizontal: trays. Vertical: minutes. Four synthetic sessions.</p>
    <svg
      id="chart"
      width="480"
      height="320"
      role="img"
      aria-label="Synthetic tray and minute scatter plot"
    ></svg
    ><button id="run" type="button">Run example</button>
    <pre id="output" aria-live="polite"></pre>
    <script src="vendor/d3.min.js"></script>
    <script>
      const runButton = document.getElementById("run");
      const output = document.getElementById("output");
      const print = (value) => {
        output.textContent += String(value) + "\n";
      };
      window.aiDone = false;
      window.aiFailure = null;
      runButton.addEventListener("click", async () => {
        runButton.disabled = true;
        output.textContent = "";
        window.aiDone = false;
        window.aiFailure = null;
        try {
          const points = [
            { trays: 1, minutes: 7 },
            { trays: 2, minutes: 9 },
            { trays: 3, minutes: 12 },
            { trays: 4, minutes: 13 },
          ];
          const svg = d3.select("#chart");
          svg.selectAll("*").remove();
          const x = d3.scaleLinear().domain([0, 5]).range([50, 450]);
          const y = d3.scaleLinear().domain([0, 16]).range([270, 20]);
          svg
            .append("g")
            .attr("transform", "translate(0,270)")
            .call(d3.axisBottom(x).ticks(5));
          svg
            .append("g")
            .attr("transform", "translate(50,0)")
            .call(d3.axisLeft(y).ticks(4));
          svg
            .selectAll("circle")
            .data(points)
            .join("circle")
            .attr("cx", (d) => x(d.trays))
            .attr("cy", (d) => y(d.minutes))
            .attr("r", 5)
            .attr("fill", "#0078d4");
          print("bound observations: " + svg.selectAll("circle").size());
          window.aiArtifact = { kind: "d3", points };
        } catch (error) {
          window.aiFailure = String(error);
          print("Error: " + error.message);
        } finally {
          window.aiDone = true;
          runButton.disabled = false;
        }
      });
    </script>
  </body>
</html>

Focus on the JavaScript operations

This excerpt omits document markup, local library loading and the explicit Run control. Use the full source to run it.

Operation excerpt

const points = [{trays:1,minutes:7},{trays:2,minutes:9},{trays:3,minutes:12},{trays:4,minutes:13}];
const svg = d3.select('#chart');svg.selectAll('*').remove();
const x = d3.scaleLinear().domain([0,5]).range([50,450]);
const y = d3.scaleLinear().domain([0,16]).range([270,20]);
svg.append('g').attr('transform','translate(0,270)').call(d3.axisBottom(x).ticks(5));
svg.append('g').attr('transform','translate(50,0)').call(d3.axisLeft(y).ticks(4));
svg.selectAll('circle').data(points).join('circle').attr('cx',d=>x(d.trays)).attr('cy',d=>y(d.minutes)).attr('r',5).attr('fill','#0078d4');
print('bound observations: ' + svg.selectAll('circle').size());
window.aiArtifact = {kind:'d3',points};

Expected result

bound observations: 4

The data join creates one mark per row and the scales share explicit domains. The visible caption supplies axis meanings. The browser review checks the actual circle count and positions; a text message alone would not prove the chart was drawn.

Technical references

Advertisements