Activity Rings Progress Chart — MUI X Charts

An activity rings chart displays goal completion for several metrics at once using concentric radial progress arcs, in the style popularized by fitness trackers. Each ring is a partial arc with rounded end caps that starts at the top (12 o'clock) and sweeps clockwise in proportion to how close the metric is to its goal, with a distinct color per metric and a faint background track showing the full circle. It is instantly recognizable and ideal for at-a-glance comparison of progress across a small set of related goals, filling the general radial-progress gap that needle gauges (`gauge-basic`) and polar bars (`polar-bar`) do not cover.

Activity Rings Progress Chart rendered with MUI X Charts

JavaScript source (MUI X Charts)

// anyplot.ai
// gauge-activity-rings: Activity Rings Progress Chart
// Library: muix 7.29.1 | JavaScript 22.22.3
// Quality: 89/100 | Created: 2026-06-14
//# anyplot-orientation: square
// anyplot.ai
// gauge-activity-rings: Activity Rings Progress Chart
// Library: MUI X Charts | React | Node 22
// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.
// Quality: pending | Created: 2026-06-14

import {
  GaugeContainer,
  GaugeValueArc,
  GaugeReferenceArc,
} from "@mui/x-charts/Gauge";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";

const t = window.ANYPLOT_TOKENS;
const W = window.ANYPLOT_SIZE.width;  // 1200 CSS px (square mount)
const H = window.ANYPLOT_SIZE.height; // 1200 CSS px

// Higher opacity in dark mode so tracks remain visible against near-black
const trackOpacity = window.ANYPLOT_THEME === "dark" ? 0.28 : 0.18;

// Daily fitness activity — Move / Exercise / Stand rings (spec example data)
const rings = [
  { label: "Move",     value: 420, goal: 600, unit: "kcal", color: t.palette[0] },
  { label: "Exercise", value: 25,  goal: 30,  unit: "min",  color: t.palette[1] },
  { label: "Stand",    value: 9,   goal: 12,  unit: "hr",   color: t.palette[2] },
];

// Concentric radii as % of maxRadius (≈ min(W,H)/2 = 600 px for full-circle gauge)
// Outer → inner: 12% wide rings, 4% gaps between them
const RING_RADII = [
  { inner: "60%", outer: "72%" }, // outer  (Move)
  { inner: "44%", outer: "56%" }, // middle (Exercise)
  { inner: "28%", outer: "40%" }, // inner  (Stand)
];

// Identify the lagging ring (lowest percentage) for visual emphasis
const ringPcts = rings.map((r) => Math.round((r.value / r.goal) * 100));
const laggingIndex = ringPcts.indexOf(Math.min(...ringPcts));

export default function Chart() {
  return (
    <Box sx={{ position: "relative", width: W, height: H, bgcolor: t.pageBg }}>
      {/* Three concentric gauge rings stacked via absolute positioning */}
      {rings.map((ring, i) => {
        const pct = Math.min((ring.value / ring.goal) * 100, 100);
        return (
          <Box key={ring.label} sx={{ position: "absolute", inset: 0 }}>
            <GaugeContainer
              width={W}
              height={H}
              value={pct}
              valueMin={0}
              valueMax={100}
              startAngle={0}
              endAngle={360}
              innerRadius={RING_RADII[i].inner}
              outerRadius={RING_RADII[i].outer}
              cornerRadius="50%"
              skipAnimation
            >
              {/* Full-circle background track */}
              <GaugeReferenceArc style={{ fill: ring.color, opacity: trackOpacity }} />
              {/* Colored progress arc */}
              <GaugeValueArc style={{ fill: ring.color }} />
            </GaugeContainer>
          </Box>
        );
      })}

      {/* Title */}
      <Box
        sx={{
          position: "absolute",
          top: 28,
          left: 0,
          right: 0,
          textAlign: "center",
          pointerEvents: "none",
        }}
      >
        <Typography
          sx={{ color: t.inkSoft, fontSize: 22, fontWeight: 400, letterSpacing: "0.01em" }}
        >
          gauge-activity-rings · javascript · muix · anyplot.ai
        </Typography>
      </Box>

      {/* Center label — sits inside the innermost ring (< 28% radius = 168 px) */}
      <Box
        sx={{
          position: "absolute",
          top: "50%",
          left: "50%",
          transform: "translate(-50%, -50%)",
          textAlign: "center",
          pointerEvents: "none",
        }}
      >
        <Typography
          sx={{ color: t.ink, fontSize: 34, fontWeight: 700, lineHeight: 1.15 }}
        >
          Daily
        </Typography>
        <Typography
          sx={{ color: t.ink, fontSize: 34, fontWeight: 700, lineHeight: 1.15 }}
        >
          Activity
        </Typography>
        <Typography sx={{ color: t.inkSoft, fontSize: 16, mt: 0.75 }}>
          Jun 14, 2026
        </Typography>
      </Box>

      {/* Bottom legend: percentage + name + value/goal */}
      <Box
        sx={{
          position: "absolute",
          bottom: 52,
          left: 0,
          right: 0,
          display: "flex",
          justifyContent: "center",
          gap: "64px",
          pointerEvents: "none",
        }}
      >
        {rings.map((ring, i) => {
          const pct = ringPcts[i];
          const isLagging = i === laggingIndex;
          return (
            <Box key={ring.label} sx={{ textAlign: "center" }}>
              <Typography
                sx={{ color: ring.color, fontSize: 30, fontWeight: 800, lineHeight: 1 }}
              >
                {pct}%
              </Typography>
              <Typography
                sx={{ color: t.ink, fontSize: 18, fontWeight: 600, mt: 0.5 }}
              >
                {ring.label}
              </Typography>
              <Typography sx={{ color: t.inkSoft, fontSize: 14 }}>
                {ring.value} / {ring.goal} {ring.unit}
              </Typography>
              {isLagging && (
                <Typography sx={{ color: t.amber, fontSize: 13, fontWeight: 700, mt: 0.5 }}>
                  ↓ focus area
                </Typography>
              )}
            </Box>
          );
        })}
      </Box>
    </Box>
  );
}

Part of Activity Rings Progress Chart on anyplot.ai.

Other implementations