Build an HTML5 Spreadsheet-Like Graph Plotter with JavaScript

Build a Graph Plotter in HTML5!
On 5 min, 38 sec read

Have you ever wanted to create a simple graphing tool using just HTML5 and JavaScript? In this tutorial, I’ll show you how to build a spreadsheet-style data input table that plots dynamic bar and line graphs using the <canvas> element — no external libraries required.

This project is perfect for JavaScript beginners, especially if you enjoy learning through hands-on examples.

🎯 What You’ll Learn

  • How to use <input type="number"> in a spreadsheet-style table
  • Automatically add new rows when inputs are filled
  • Draw bar and line graphs using the HTML5 <canvas>
  • Add interactivity with JavaScript
  • Toggle between graph types using a dropdown

📑 Project Code Overview

We’ll create:

  1. A simple table with two columns for X and Y values
  2. Auto-expanding rows when both X and Y are filled
  3. A dropdown to choose between Line or Bar graph
  4. Canvas rendering logic to draw and update the graph

💻 Technologies Used

  • HTML5 for structure and canvas
  • CSS3 for minimal styling
  • Vanilla JavaScript for dynamic interaction



&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot; /&gt;
  &lt;title&gt;Line/Bar Graph Plotter&lt;/title&gt;
  &lt;style&gt;
    body {
      font-family: sans-serif;
      padding: 20px;
      background: #f0f0f0;
    }

    table {
      width: 300px;
      margin-bottom: 10px;
      border-collapse: collapse;
    }

    th, td {
      padding: 6px;
      border: 1px solid #ccc;
      text-align: center;
    }

    input[type=&quot;number&quot;] {
      width: 80px;
      padding: 4px;
      text-align: right;
    }

    select, button {
      padding: 8px 12px;
      margin: 10px 0;
      font-size: 14px;
    }

    canvas {
      border: 1px solid #333;
      background: #fff;
      display: block;
      margin-top: 20px;
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

  &lt;h2&gt;Spreadsheet Graph Plotter&lt;/h2&gt;

  &lt;label&gt;Choose Graph Type:&lt;/label&gt;
  &lt;select id=&quot;graphType&quot;&gt;
    &lt;option value=&quot;line&quot;&gt;Line Graph&lt;/option&gt;
    &lt;option value=&quot;bar&quot;&gt;Bar Graph&lt;/option&gt;
  &lt;/select&gt;

  &lt;table id=&quot;dataTable&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;&lt;th&gt;X&lt;/th&gt;&lt;th&gt;Y&lt;/th&gt;&lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;input type=&quot;number&quot; step=&quot;any&quot; /&gt;&lt;/td&gt;
        &lt;td&gt;&lt;input type=&quot;number&quot; step=&quot;any&quot; /&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;button onclick=&quot;plotGraph()&quot;&gt;Plot Graph&lt;/button&gt;

  &lt;canvas id=&quot;graphCanvas&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;&lt;/canvas&gt;

  &lt;script&gt;
    const table = document.getElementById(&#039;dataTable&#039;).getElementsByTagName(&#039;tbody&#039;)[0];

    // Add new row if last one is filled
    table.addEventListener(&#039;input&#039;, () =&gt; {
      const rows = table.rows;
      const lastRow = rows[rows.length - 1];
      const xInput = lastRow.cells[0].querySelector(&#039;input&#039;);
      const yInput = lastRow.cells[1].querySelector(&#039;input&#039;);

      if (xInput.value &amp;&amp; yInput.value) {
        addRow();
      }
    });

    function addRow() {
      const newRow = table.insertRow();
      const xCell = newRow.insertCell(0);
      const yCell = newRow.insertCell(1);

      const xInput = document.createElement(&#039;input&#039;);
      xInput.type = &#039;number&#039;;
      xInput.step = &#039;any&#039;;

      const yInput = document.createElement(&#039;input&#039;);
      yInput.type = &#039;number&#039;;
      yInput.step = &#039;any&#039;;

      xCell.appendChild(xInput);
      yCell.appendChild(yInput);
    }

function plotGraph() {
  const graphType = document.getElementById(&quot;graphType&quot;).value;
  const canvas = document.getElementById(&quot;graphCanvas&quot;);
  const ctx = canvas.getContext(&quot;2d&quot;);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  let points = [];

  const rows = table.rows;
  for (let i = 0; i &lt; rows.length; i++) {
    const x = parseFloat(rows[i].cells[0].querySelector(&#039;input&#039;).value);
    const y = parseFloat(rows[i].cells[1].querySelector(&#039;input&#039;).value);
    if (!isNaN(x) &amp;&amp; !isNaN(y)) {
      points.push({ x, y });
    }
  }

  if (points.length &lt; 2) {
    alert(&quot;Please enter at least two valid data points.&quot;);
    return;
  }

  if (graphType === &#039;line&#039;) {
    points.sort((a, b) =&gt; a.x - b.x);
  }

  const padding = 40;
  const graphWidth = canvas.width - 2 * padding;
  const graphHeight = canvas.height - 2 * padding;
  const xVals = points.map(p =&gt; p.x);
  const yVals = points.map(p =&gt; p.y);

  const xMin = Math.min(...xVals);
  const xMax = Math.max(...xVals);
  const yMin = Math.min(0, ...yVals);
  const yMax = Math.max(...yVals);

  const xScale = graphWidth / (xMax - xMin || 1);
  const yScale = graphHeight / (yMax - yMin || 1);

  // Axes
  ctx.strokeStyle = &quot;#999&quot;;
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(padding, canvas.height - padding);
  ctx.lineTo(canvas.width - padding, canvas.height - padding); // X-axis
  ctx.moveTo(padding, padding);
  ctx.lineTo(padding, canvas.height - padding); // Y-axis
  ctx.stroke();

  if (graphType === &#039;line&#039;) {
    // ✅ Draw a single path through all sorted points
    ctx.strokeStyle = &quot;#007acc&quot;;
    ctx.lineWidth = 2;
    ctx.beginPath();

    points.forEach((point, i) =&gt; {
      const xCoord = padding + (point.x - xMin) * xScale;
      const yCoord = canvas.height - padding - (point.y - yMin) * yScale;

      if (i === 0) {
        ctx.moveTo(xCoord, yCoord);
      } else {
        ctx.lineTo(xCoord, yCoord);
      }
    });

    ctx.stroke(); // ⬅️ Only stroke once, after path is fully defined

    // ✅ Draw data points on top
    points.forEach(point =&gt; {
      const xCoord = padding + (point.x - xMin) * xScale;
      const yCoord = canvas.height - padding - (point.y - yMin) * yScale;
      ctx.fillStyle = &quot;#ff3333&quot;;
      ctx.beginPath();
      ctx.arc(xCoord, yCoord, 4, 0, 2 * Math.PI);
      ctx.fill();
    });
  }

  if (graphType === &#039;bar&#039;) {
    // Draw bars centered on each x
    const barWidth = Math.min(40, graphWidth / xVals.length - 10);

    points.forEach((point) =&gt; {
      const xCoord = padding + (point.x - xMin) * xScale - barWidth / 2;
      const yCoord = canvas.height - padding - (point.y - yMin) * yScale;
      const barHeight = (point.y - yMin) * yScale;

      ctx.fillStyle = &quot;#28a745&quot;;
      ctx.fillRect(xCoord, yCoord, barWidth, barHeight);
    });
  }
}

&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

Interactive Demo

Here’s a live example of the Spreadsheet Graph in action:

HTML5 Spreadsheet Graph Demo
HTML5 Line Graph
Web Browser Displaying HTML5 Spreadsheet Line Graph

HTML5 Bar Graph
Web Browser Displaying HTML5 Spreadsheet Bar Graph

HTML5 Spreadsheet Graph Video

📚 Want to Learn JavaScript More Deeply?

Check out my beginner-friendly book:
📖 Learning JavaScript – A Beginner’s Guide

Whether you’re starting fresh or revisiting JavaScript, this book is a solid companion.

Also, explore my full course here:
🎓 Learning JavaScript – Online Course

👨‍🎓 Need Help or 1-on-1 Tutoring?

I’m available for personalized programming tutorials — including HTML5, CSS, JavaScript, and more.
📧 Contact me here to book a session.

🔁 What’s Next?

In future tutorials, I’ll cover:

  • Adding axis labels and gridlines
  • Exporting graphs as images
  • Saving input data to local storage or files

💬 Questions?

Have any questions or feedback? Leave a comment below or reach out through the contact form.

Happy coding!

🚀 Recommended Resources


Disclosure: Some of the links above are referral links. I may earn a commission if you make a purchase at no extra cost to you.

About Edward

Edward is a software engineer, author, and designer dedicated to providing the actionable blueprints and real-world tools needed to navigate a shifting economic landscape.

With a provocative focus on the evolution of technology—boldly declaring that “programming is dead”—Edward’s latest work, The Recession Business Blueprint, serves as a strategic guide for modern entrepreneurship. His bibliography also includes Mastering Blender Python API and The Algorithmic Serpent.

Beyond the page, Edward produces open-source tool review videos and provides practical resources for the “build it yourself” movement.

📚 Explore His Books – Visit the Book Shop to grab your copies today.

💼 Need Support? – Learn more about Services and the ways to benefit from his expertise.

🔨 Build it Yourself – Download Free Plans for Backyard Structures, Small Living, and Woodworking.