/

LinesGL Chart

GPU-accelerated 2D multi-line chart with thick triangle-strip rendering for crisp lines at any zoom level. Handle large datasets with per-series styling.

Quick Start

import { LinesGL } from "@chartts/gl"
 
const chart = LinesGL("#chart", {
  data: {
    series: [
      {
        name: "Revenue",
        x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        y: [42, 58, 71, 64, 82, 96, 88, 105, 112, 98],
        color: "#6c9eff",
      },
      {
        name: "Costs",
        x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        y: [28, 32, 38, 35, 42, 48, 44, 52, 55, 50],
        color: "#f472b6",
      },
    ],
  },
  lineWidth: 3,
})

That renders two GPU-accelerated line series with thick, crisp lines. Each line segment is rendered as a triangle strip rather than a thin GL line, which means the line width stays consistent at any zoom level and on any display. Hover detection snaps to the nearest data point across all series.

When to Use LinesGL Charts

LinesGL is the high-performance WebGL alternative to SVG-based line charts. It excels at rendering multiple series with many data points where DOM-based approaches would lag.

Use a LinesGL chart when:

  • You have multiple series with thousands of points each
  • Smooth 60fps pan and zoom is important
  • You need consistent thick lines that render crisply on all devices
  • The dataset is too large for SVG/Canvas 2D line rendering

Don't use a LinesGL chart when:

  • You have fewer than a few hundred points (SVG line charts are simpler)
  • You need CSS styling, Tailwind classes, or DOM event handlers on lines
  • Server-side rendering is required (WebGL needs a browser)
  • You want area fills, dots, or curve interpolation (use the SVG line chart)

Props Reference

PropTypeDefaultDescription
dataGLChartDatarequiredChart data with series array of GLSeries2D objects
lineWidthnumber2Line thickness in pixels
theme'dark' | 'light' | GLTheme'dark'Color theme for background and palette
animatebooleantrueEnable fade-in animation on mount
tooltipbooleantrueShow tooltip on hover with series name and value

Thick Line Triangles

Traditional WebGL line rendering with GL_LINES produces hairline-thin lines (1px max on many GPUs). LinesGL solves this by building each line segment as a pair of triangles (a quad) with screen-space width. The vertex shader computes perpendicular offsets from the line direction, creating a ribbon that maintains its width regardless of the data scale.

// Thin lines for dense overlapping series
LinesGL("#chart", {
  data: denseData,
  lineWidth: 1,
})
 
// Thick lines for emphasis
LinesGL("#chart", {
  data: highlightData,
  lineWidth: 5,
})

The line width is specified in CSS pixels and does not scale with data coordinates. Lines at 3px width look the same whether you are viewing 100 points or 100,000 points.


Per-Series Styling

Each series can specify its own color. When no color is provided, series are assigned colors from the theme palette in order.

LinesGL("#chart", {
  data: {
    series: [
      {
        name: "CPU",
        x: timestamps,
        y: cpuValues,
        color: "#6c9eff",
      },
      {
        name: "Memory",
        x: timestamps,
        y: memValues,
        color: "#5eead4",
      },
      {
        name: "Disk I/O",
        x: timestamps,
        y: diskValues,
        color: "#fbbf24",
      },
    ],
  },
  lineWidth: 2,
})

The theme provides 8 default colors that cycle for additional series beyond 8.


Large Dataset Handling

LinesGL automatically computes data bounds with a 40px margin, maps all points to screen coordinates, and builds the triangle strip geometry in a single pass. This geometry is uploaded to the GPU as a single vertex buffer, making the draw call count independent of the number of points.

// 10 series with 10,000 points each
const series = Array.from({ length: 10 }, (_, si) => ({
  name: `Sensor ${si + 1}`,
  x: Array.from({ length: 10000 }, (_, i) => i),
  y: Array.from({ length: 10000 }, (_, i) =>
    Math.sin(i * 0.01 + si) * 50 + si * 20
  ),
}))
 
LinesGL("#chart", {
  data: { series },
  lineWidth: 1.5,
})

Accessibility

  • Tooltip displays series name and exact y-value on hover
  • Each series has a distinct color for visual separation
  • Lines maintain consistent width across all zoom levels for readability
  • Dark and light themes provide appropriate contrast for line visibility

Real-World Examples

System metrics dashboard

const hours = Array.from({ length: 1440 }, (_, i) => i)
 
LinesGL("#metrics", {
  data: {
    series: [
      {
        name: "CPU %",
        x: hours,
        y: hours.map((h) =>
          40 + Math.sin(h * 0.05) * 20 + Math.random() * 10
        ),
        color: "#6c9eff",
      },
      {
        name: "Memory %",
        x: hours,
        y: hours.map((h) =>
          55 + Math.sin(h * 0.03) * 15 + Math.random() * 5
        ),
        color: "#5eead4",
      },
      {
        name: "Network MB/s",
        x: hours,
        y: hours.map((h) =>
          20 + Math.sin(h * 0.08) * 18 + Math.random() * 8
        ),
        color: "#fbbf24",
      },
    ],
  },
  lineWidth: 2,
  theme: "dark",
})

Stock price comparison

LinesGL("#stocks", {
  data: {
    series: [
      {
        name: "AAPL",
        x: tradingDays,
        y: aaplPrices,
        color: "#6c9eff",
      },
      {
        name: "GOOGL",
        x: tradingDays,
        y: googlPrices,
        color: "#f472b6",
      },
      {
        name: "MSFT",
        x: tradingDays,
        y: msftPrices,
        color: "#5eead4",
      },
    ],
  },
  lineWidth: 2,
  theme: "light",
})

IoT sensor array

const points = 5000
const time = Array.from({ length: points }, (_, i) => i * 0.1)
 
LinesGL("#iot", {
  data: {
    series: Array.from({ length: 8 }, (_, si) => ({
      name: `Sensor ${si + 1}`,
      x: time,
      y: time.map((t) =>
        Math.sin(t * (0.5 + si * 0.1)) * (20 + si * 5) + 50 + si * 10
      ),
    })),
  },
  lineWidth: 1.5,
})

Other Charts