Sparkline Chart — Free HTML CSS JS Canvas Snippet

Sparkline Chart · Charts · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Canvas 2D bezier curves: midpoint control points for smooth lines
Coordinate normalisation: (value-min)/range maps any data to canvas dimensions
Y-axis inversion: h - padding - scaledValue (Canvas Y increases downward)
Linear gradient fill: opaque top → transparent bottom via createLinearGradient
End-point dot: ctx.arc on last data point marks current value
4 metric cards: revenue/users/bounce/conversion with colour-matched sparklines
padding variable prevents clipping at canvas edges
No external library — pure Canvas 2D API

About this UI Snippet

Sparkline Chart — Canvas Bezier Curve, Gradient Fill, End Dot & Normalised Coordinates

Screenshot of the Sparkline Chart snippet rendered live

A full chart needs axes, gridlines, a legend, and room to breathe. A sparkline needs none of that — just enough ink to answer "is this going up or down?" in the time it takes an eye to pass over a number. Edward Tufte coined the term for exactly this kind of "intense, simple, word-sized graphic," and it's now the default companion to any KPI on a dashboard: a revenue figure means little until a tiny rising line beside it says *and it's been climbing all month*. This snippet draws four such metric cards directly to <canvas> — smooth curves instead of jagged polylines, a soft gradient fill beneath the line, a dot marking the latest value, and a coordinate system that adapts itself to whatever numbers you hand it.

Mapping arbitrary numbers onto a fixed-size canvas

Revenue might range from 27 to 48; active users from 1,150 to 1,847. Both need to fill the same 100×40 canvas without the chart author hand-tuning a scale for each metric. The fix is normalisation: (value - min) / range converts any number in the dataset into a position between 0 and 1 — purely *where it sits relative to the dataset's own minimum and maximum* — and multiplying that fraction by the canvas's available height converts it into pixels. The final twist, canvasHeight - padding - scaledValue, flips the result, because canvas coordinates grow *downward* from the top-left corner while every chart convention on Earth expects "higher value" to mean "higher on the screen."

Smooth curves from a single line of geometry

ctx.lineTo would connect the data points with sharp, jagged corners — technically accurate, visually noisy. Instead, each segment uses ctx.bezierCurveTo with control points placed at the horizontal midpoint between consecutive points: const mx = (prev.x + next.x) / 2. Anchoring both control points to that shared midpoint — one paired with the previous point's height, one with the next's — produces a curve that eases smoothly out of one point and into the next, with no separate smoothing pass or external charting math required. It's the same "let geometry do the work" instinct behind the gliding indicator in the Scroll-Spy Navigation snippet.

A gradient that fades the story toward the baseline

The filled area beneath each line uses createLinearGradient running from an opaque tint at the top to fully transparent at the bottom — drawn by tracing the curve, dropping straight down to the canvas floor, and closing the path back to the start before calling ctx.fill(). The fade keeps the eye anchored on the *line*, which carries the actual information, while the colour wash beneath it adds just enough visual weight to read as "area under a trend" rather than a bare line floating in space.

Why canvas, not SVG, for a wall of small charts

A dashboard might show twenty or fifty of these at once. Each SVG sparkline would be its own subtree of DOM nodes for the browser to parse, lay out, and keep in memory; a canvas sparkline is a handful of draw calls into a single bitmap that simply *is* what it looks like — no nodes, no reflow, trivially cheap to redraw on a live data tick. For a handful of hero charts, SVG's crispness and CSS-styleability often win; for a grid of glanceable trend lines like the Stats Card or Line Chart Widget might use, canvas is the pragmatic choice.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the coordinate normalization or the bezier smoothing on your own. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why drawSparkline computes (value - min) / range before scaling to pixels, and why the y coordinate then gets subtracted from canvasHeight rather than used directly. The same assistant can help optimize it, for instance checking whether recomputing the same min/max and gradient on every single redraw call matters if a dashboard redraws 20 sparklines on every live data tick, or whether caching those values per dataset would help. It is just as useful for extending the sparklines: ask it to animate the line drawing in progressively left-to-right on first load, add a hover tooltip showing the exact value at the nearest point using mouse position mapped back through the same coordinate math, or make the fill gradient's opacity reflect whether the trend is up or down. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a grid of metric cards with canvas "sparkline" mini-charts in plain HTML, CSS, and JavaScript using only the Canvas 2D API — no charting library.

Requirements:
- Several metric cards, each showing a label, a large current value, a colored percentage-change indicator, and a small fixed-size canvas element for its trend line.
- A single reusable draw function that takes a canvas element and an array of numeric data points, computes the dataset's own minimum and maximum, and maps every value to a canvas y-coordinate using linear normalization: (value - min) / (max - min || 1), scaled to the canvas's available height after subtracting padding, and inverted by subtracting from canvas height so higher values appear higher on screen (since canvas y grows downward).
- X coordinates must be evenly distributed across the canvas width based on each point's index in the array, independent of the y-coordinate math.
- Connect the points with smooth curves, not straight line segments: for each pair of consecutive points, use a bezier curve whose two control points are both anchored at the horizontal midpoint between the two points, so the curve eases naturally in and out of every point with no separate smoothing algorithm.
- Beneath the line, fill the area down to the canvas floor with a vertical linear gradient that is opaque near the line and fully transparent by the bottom, so the trend has visual weight without competing with the line itself.
- Draw a small filled circle at the last data point to mark the current/latest value, and give each metric card's sparkline its own distinct line and fill color tied to whether the metric is generally good-when-up or good-when-down (e.g. green for rising revenue, red for a rising bounce rate).
- The draw function must work unmodified for datasets of very different scales (e.g. one series ranging 27 to 48, another ranging 1150 to 1847) without any per-metric manual scale configuration.

Step by step

How to Use

  1. 1
    View the four metric cards with their sparklinesEach card shows a label, value, percentage change, and a mini sparkline chart. The last data point has an endpoint dot. Green for growth metrics, red for bounce rate (where lower is better).
  2. 2
    Replace the DATA arrays with your own metricsUpdate the DATA object: each key is a canvas ID suffix (sp-X), the value is an array of numbers. Use the last 7, 14, or 30 days of data. The sparkline automatically normalises to fit any range.
  3. 3
    Change sparkline coloursUpdate COLORS: each key needs a line colour (hex) and fill colour (rgba with low opacity). Match the colour to the metric's semantic meaning — green for growth, red for declining metrics, blue for neutral.
  4. 4
    Add more metric cardsDuplicate a .metric-card div and add matching entries to DATA and COLORS. Call drawSparkline("sp-newkey", "newkey") at the bottom. The 2-column grid automatically accommodates more cards.
  5. 5
    Animate the sparkline on loadFor an animated draw effect, modify drawSparkline to draw progressively: use requestAnimationFrame and draw only the first N points per frame, incrementing N until all points are drawn. This creates a left-to-right reveal animation.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component using useRef for canvases and useEffect for drawing, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

Dashboard KPI metric cards with trend indicators
The classic sparkline use case — a number with a tiny trend chart. Revenue, active users, conversion rate, error count, latency — any metric that changes over time benefits from a sparkline showing the trend shape at a glance without reading the full chart.
Analytics overview and real-time metric monitoring
Real-time dashboards show live metrics updating every second. Canvas sparklines redraw efficiently — call drawSparkline() with the updated data array and the canvas clears and redraws instantly without any DOM overhead.
Inline trend indicators in table cells and lists
Data tables with a sparkline column show the trend history for each row. Stock tickers, server health monitors, and sales team leaderboards use inline sparklines in table cells. Canvas sparklines are efficient enough to render 50+ simultaneously.
Performance monitoring and uptime dashboards
Site reliability and DevOps dashboards show response time, error rate, and throughput sparklines per service. The gradient fill communicates the trend severity — flat green for stable, rising red for degrading.
Study Canvas 2D bezier curve smoothing and coordinate normalisation
The sparkline demonstrates the midpoint bezier curve technique for smooth chart lines without a smoothing algorithm. The coordinate normalisation formula works for any chart type — bar charts, area charts, and line charts all use the same (value - min) / range × pixelRange calculation.
Lightweight alternative to Chart.js for simple sparklines
Chart.js adds 60KB+ for sparklines this snippet renders in 40 lines of Canvas code. For dashboards with 10-20 sparklines, the zero-dependency Canvas approach eliminates library overhead entirely while providing identical visual output.

Got questions?

Frequently Asked Questions

Two calculations: X = padding + (index / (dataLength - 1)) × availableWidth. Y = canvasHeight - padding - ((value - min) / range) × availableHeight. The X formula distributes points evenly across the canvas width. The Y formula: (value - min) / range normalises the value to 0-to-1, then multiplying by availableHeight scales to pixels. Subtracting from canvasHeight - padding inverts the Y axis (canvas Y increases downward, but charts conventionally show higher values higher up).

For each pair of adjacent points (prev, next), the midpoint mx = (prev.x + next.x) / 2 is computed. bezierCurveTo uses (mx, prev.y) as the first control point and (mx, next.y) as the second. The first control point extends horizontally from the previous point, and the second extends horizontally to the next point. This creates a smooth curve that transitions horizontally between each data point.

Call drawSparkline(canvasId, dataKey) whenever the data updates. In the DATA object, push new values and shift old ones to maintain a fixed length: DATA.revenue.push(newValue); DATA.revenue.shift(). Then redraw: drawSparkline("sp-revenue", "revenue"). For a 1-second update: setInterval(() => { fetchMetric().then(v => { DATA.revenue.push(v); DATA.revenue.shift(); drawSparkline("sp-revenue", "revenue"); }); }, 1000).

Click "JSX" to download. Use useRef(null) for each canvas element: const revenueRef = useRef(null). The drawSparkline function takes the canvas element directly instead of looking up by ID. Call it in useEffect: useEffect(() => { drawSparkline(revenueRef.current, "revenue", data.revenue, COLORS.revenue); }, [data]). When data changes, the useEffect dependency fires and the sparkline redraws automatically.