You Might Also Like
Radar Chart HTML CSS JS — SVG Spider Chart
Radar Chart · Charts · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
How to Build an SVG Radar Chart with Polar-to-Cartesian Math

A radar chart — also called a spider or web chart — plots several metrics around a circle, with each axis radiating from the center and the data joined into a polygon. It is the go-to visualization for comparing multi-dimensional profiles like player stats, product scores or skill ratings. This implementation is built with inline SVG and a little trigonometry, supports two overlaid datasets, animates the fill on load, and lets you drag vertices to edit values live. No charting library is used. Here is the full breakdown.
Placing the axes with polar coordinates
The chart has N axes spread evenly around the center, one per metric, separated by 360 / N degrees. The core of the whole chart is the polar-to-Cartesian conversion. For a given axis index i and a radius r (how far out along that axis), the angle is i * (2*PI / N) measured from straight up, and the point is computed as:
x = cx + r * sin(angle) y = cy - r * cos(angle)
Using sin for x and -cos for y (with the minus because SVG's y-axis points down) places the first axis at the top and walks clockwise. A small helper function takes an axis index and a 0-to-1 value and returns the screen point, scaling the value by the chart's maximum radius. Every line, label and data vertex in the chart is positioned through this one function.
The concentric ring grid
The background grid is a set of concentric polygons rather than circles, so the gridlines mirror the data polygon's shape. For each ring level (say 20%, 40%, 60%, 80%, 100%) the code generates a points string by calling the polar helper for every axis at that fixed radius and joining the coordinates with spaces, then renders an SVG <polygon> with a faint stroke and no fill. Straight axis spokes are drawn as <line> elements from the center to each outermost vertex, and axis labels are positioned just beyond the outer ring, with their text-anchor adjusted based on which side of the chart they fall so labels never overlap the shape.
Building the data polygon
Each dataset is an array of values, one per axis, normalized to 0–1. The data polygon's points attribute is built exactly like a grid ring, but using each axis's actual data value as its radius instead of a fixed level. The resulting <polygon> is given a semi-transparent fill and a solid colored stroke. Small <circle> markers are drawn at each vertex so individual values are easy to read and grab.
Overlaying two datasets
Two datasets are rendered as two separate polygons with different colors and translucent fills layered on the same grid, making side-by-side comparison immediate — for example two players' attribute profiles. Because both use the same polar helper and coordinate system, they align perfectly. A legend maps each color to its label.
Animating the fill-in
On load the polygons animate in using SVG stroke techniques. The outline is drawn with a large stroke-dasharray set to the path length and an initial stroke-dashoffset equal to that length, which hides the stroke; transitioning the offset to zero makes the outline appear to draw itself around the shape. The fill opacity is simultaneously transitioned from zero, so the polygon both traces and fades in. This is the standard SVG line-drawing animation applied to a closed polygon.
Draggable vertices for live editing
The chart is interactive: each data vertex marker responds to pointer events. On press, the code records which dataset and axis the grabbed marker belongs to and sets a dragging reference. On move, it converts the pointer position back from Cartesian into a value along that axis — projecting the pointer's offset from center onto the axis direction and dividing by the max radius — clamps it to 0–1, updates the dataset, and rebuilds the polygon and markers. Releasing clears the drag. Because the rebuild reruns the same polar math, dragging a vertex smoothly reshapes the polygon in real time, effectively turning the chart into an input control. Both mouse and touch end events clear the drag state so it works on phones.
Tooltips and labels
Hovering or focusing a vertex can surface the exact value, implemented either through native SVG <title> elements for built-in tooltips or a positioned label. Axis labels and value rings give the chart context without clutter.
Why SVG over canvas here
SVG is ideal for a radar chart because each element — polygon, line, circle, label — is a real DOM node you can style with CSS, animate with transitions, and attach events to individually for dragging and tooltips. The whole chart is just trigonometry feeding coordinate strings into declarative SVG, which makes it crisp at any resolution, accessible, and easy to extend with more axes or datasets by changing the data arrays.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not need to work through the trigonometry alone to understand this chart. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the pt() function uses sin for x and negative cos for y instead of the more common cos-for-x convention, and how that choice determines which axis sits at the top of the chart. The same assistant can help optimize it — ask whether rebuilding the entire svg.innerHTML on every build() call is necessary, or whether dragging a vertex could update just that one polygon's points attribute and its dot without touching the rings and axis labels at all. It's also useful for extending the chart: ask it to support a third overlaid dataset, snap dragged values to whole numbers with a visible readout, or add pinch-zoom for the radius on touch devices. 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:
Build an SVG radar (spider) chart in plain HTML, CSS, and JavaScript using inline SVG created with createElementNS — no charting library, no canvas.
Requirements:
- Compute every point on the chart with one shared polar-to-Cartesian helper function that takes an axis index and a 0-100 value, converts the axis index to an angle assuming N evenly-spaced axes starting at the top (12 o'clock) and going clockwise, and returns an {x, y} pair scaled by a fixed maximum radius from a fixed center point.
- Draw a background grid of several concentric polygons (not circles) at fixed percentage levels (e.g. 20, 40, 60, 80, 100), each generated by calling the same polar helper for every axis at that fixed radius level and joining the results into an SVG polygon points string.
- Draw straight axis spoke lines from the center to each axis's outermost point, and axis text labels positioned just beyond the outer ring.
- Render at least two overlaid datasets as separate semi-transparent-fill, solid-stroke polygons sharing the same grid and polar helper, each with its own color, plus small circle markers at every data vertex.
- Animate each dataset polygon's outline drawing itself in on load using stroke-dasharray set to the polygon's total length and stroke-dashoffset animated from that same length down to zero via a CSS transition triggered on the next animation frame.
- Make every data vertex circle draggable with both mouse and touch events: on drag, convert the pointer position back into local chart coordinates, project it onto that vertex's axis direction to get a new 0-100 value, clamp it, update the underlying data array, and rebuild only that dataset's polygon points and the dragged vertex's own circle position, using the exact same polar helper function used for the initial render.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1View the profileSee each metric plotted on its own axis, joined into a polygon over the ring grid.
- 2Compare datasetsRead the two translucent overlays and legend to compare two profiles at a glance.
- 3Watch it animateOn load the polygon outline draws itself and the fill fades in via SVG stroke animation.
- 4Drag a vertexGrab any vertex marker and drag along its axis to change that value live.
- 5Read exact valuesHover or focus a vertex to surface its precise value through a tooltip.
- 6Edit the dataChange the dataset arrays or axis labels in the JS to chart your own metrics.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
That places the first axis straight up and walks clockwise, which is the conventional radar layout. The minus on cos accounts for SVG y-coordinates increasing downward, so positive values go up the screen.
Concentric polygons share the same vertices as the data shape, so each ring is a scaled copy of the chart outline. This makes it easy to judge a value against the grid and looks cleaner than circular rings behind a polygon.
The pointer position is projected onto the grabbed axis direction and divided by the maximum radius to get a 0-to-1 value, which is clamped and written back to the dataset. The polygon is then rebuilt with the same polar math.
The polygon stroke uses stroke-dasharray equal to its length with an initial dashoffset that hides it. Transitioning the dashoffset to zero makes the outline trace itself, while the fill opacity fades in at the same time.
SVG elements are real DOM nodes, so each vertex can have its own event listeners for dragging and tooltips, be styled with CSS, and animated with transitions. It also stays crisp at any resolution without manual redrawing.
Yes. Use the JSX, Vue, Angular, or Tailwind export buttons on this page. In React, compute the polygon points from your data array with the same polar-coordinate math during render, and pass datasets as props — the SVG re-renders declaratively when values change.