Line Plot with Markers — MUI X Charts

A line plot with visible markers at each data point, combining line and scatter plot features. This is particularly useful for sparse data where individual observations are significant.

Line Plot with Markers rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// line-markers: Line Plot with Markers
// Library: muix 7.29.1 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-09-05
import { LineChart } from "@mui/x-charts/LineChart";
import { ChartsReferenceLine } from "@mui/x-charts/ChartsReferenceLine";
import { ChartsText } from "@mui/x-charts/ChartsText";
import {
  useXScale,
  useYScale,
  useDrawingArea,
  getValueToPositionMapper,
} from "@mui/x-charts/hooks";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ----------------------------------------
// Torque-wrench QC readings at 14 sequential fastener checkpoints on two
// assembly stations — sparse enough that each individual reading matters,
// which is exactly why the markers (not just the trend line) carry meaning.
const checkpoints = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const stationA = [
  42.6, 42.3, 42.7, 42.4, 42.8, 42.2, 42.5, 42.6, 42.3, 42.7, 42.4, 42.5, 42.6,
  42.3,
];
const stationB = [
  41.9, 42.0, 41.7, 42.1, 41.8, 41.6, 41.9, 41.5, 41.8, 41.7, 41.9, 41.6, 41.8,
  41.7,
];
const TARGET_TORQUE = 42.5;

const TITLE =
  "Fastener Torque QC · line-markers · javascript · muix · anyplot.ai";

// Station A: solid filled circles — primary series, drawn above its line.
function CircleMarks({ color }: { color: string }) {
  const xScale = useXScale("checkpoint-axis");
  const yScale = useYScale("torque-axis");
  const toX = getValueToPositionMapper(xScale);
  const r = 10;
  return (
    <g>
      {checkpoints.map((x, i) => (
        <circle
          key={x}
          cx={toX(x)}
          cy={yScale(stationA[i])}
          r={r}
          fill={color}
          stroke={t.pageBg}
          strokeWidth={2}
        />
      ))}
    </g>
  );
}

// Station B: hollow diamonds — visually distinct shape + fill for the second series.
function DiamondMarks({ color }: { color: string }) {
  const xScale = useXScale("checkpoint-axis");
  const yScale = useYScale("torque-axis");
  const toX = getValueToPositionMapper(xScale);
  const r = 11;
  return (
    <g>
      {checkpoints.map((x, i) => {
        const cx = toX(x);
        const cy = yScale(stationB[i]);
        const points = `${cx},${cy - r} ${cx + r},${cy} ${cx},${cy + r} ${cx - r},${cy}`;
        return (
          <polygon
            key={x}
            points={points}
            fill={t.pageBg}
            stroke={color}
            strokeWidth={2.5}
          />
        );
      })}
    </g>
  );
}

// Hand-rolled y-axis title: MUI X's built-in `yAxis.label` offsets itself from
// a hardcoded tickFontSize guess rather than the tick labels' real measured
// width, so a 4-digit "42.6"-style tick collides with it. A fixed-position
// label sidesteps that collision.
function YAxisTitle({ text }: { text: string }) {
  const { top, height } = useDrawingArea();
  return (
    <ChartsText
      x={22}
      y={top + height / 2}
      text={text}
      fill={t.ink}
      style={{
        fontSize: 15,
        angle: -90,
        textAnchor: "middle",
        dominantBaseline: "auto",
      }}
    />
  );
}

// --- Chart (default-exported component — the harness mounts it) ------------
export default function Chart() {
  const { width, height } = window.ANYPLOT_SIZE;
  const TITLE_H = 60;
  const titleSize =
    TITLE.length > 67 ? Math.max(16, Math.round((22 * 67) / TITLE.length)) : 22;

  return (
    <Box
      sx={{
        width,
        height,
        bgcolor: t.pageBg,
        display: "flex",
        flexDirection: "column",
        boxSizing: "border-box",
      }}
    >
      <Box
        sx={{
          height: TITLE_H,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <Typography sx={{ color: t.ink, fontSize: titleSize, fontWeight: 600 }}>
          {TITLE}
        </Typography>
      </Box>

      <LineChart
        width={width}
        height={height - TITLE_H}
        skipAnimation
        xAxis={[
          {
            id: "checkpoint-axis",
            scaleType: "point",
            data: checkpoints,
            label: "QC Checkpoint (sequential reading)",
            tickLabelStyle: { fontSize: 13, fill: t.inkSoft },
            labelStyle: { fontSize: 15, fill: t.ink },
          },
        ]}
        yAxis={[
          {
            id: "torque-axis",
            min: 41.0,
            max: 43.2,
            valueFormatter: (v: number) => v.toFixed(1),
            tickLabelStyle: { fontSize: 13, fill: t.inkSoft },
            labelStyle: { fontSize: 15, fill: t.ink },
          },
        ]}
        series={[
          {
            id: "stationA",
            data: stationA,
            label: "Station A",
            color: t.palette[0],
            curve: "linear",
            showMark: false,
            xAxisId: "checkpoint-axis",
            yAxisId: "torque-axis",
          },
          {
            id: "stationB",
            data: stationB,
            label: "Station B",
            color: t.palette[1],
            curve: "linear",
            showMark: false,
            xAxisId: "checkpoint-axis",
            yAxisId: "torque-axis",
          },
        ]}
        grid={{ horizontal: true }}
        margin={{ top: 24, right: 50, bottom: 108, left: 90 }}
        sx={{
          "& .MuiLineElement-series-stationA": { strokeWidth: 3 },
          "& .MuiLineElement-series-stationB": {
            strokeWidth: 2.25,
            strokeDasharray: "9 6",
          },
          "& .MuiChartsAxis-line": { stroke: t.inkSoft, strokeOpacity: 0.4 },
          "& .MuiChartsAxis-tick": { stroke: t.inkSoft, strokeOpacity: 0.4 },
          "& .MuiChartsGrid-line": { stroke: t.grid },
        }}
        slotProps={{
          legend: {
            position: { vertical: "bottom", horizontal: "middle" },
            direction: "row",
            itemMarkWidth: 20,
            itemMarkHeight: 3,
            markGap: 8,
            itemGap: 32,
            labelStyle: { fontSize: 14, fill: t.inkSoft },
          },
        }}
      >
        <YAxisTitle text="Torque (N·m)" />
        <ChartsReferenceLine
          y={TARGET_TORQUE}
          label={`Target ${TARGET_TORQUE.toFixed(1)} N·m`}
          labelAlign="end"
          lineStyle={{
            stroke: t.inkSoft,
            strokeDasharray: "5 4",
            strokeWidth: 1.5,
            opacity: 0.8,
          }}
          labelStyle={{ fontSize: 12, fill: t.inkSoft }}
        />
        <CircleMarks color={t.palette[0]} />
        <DiamondMarks color={t.palette[1]} />
      </LineChart>
    </Box>
  );
}

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/line-markers/muix/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "line-markers",
  "language": "javascript",
  "library": "muix",
  "page": "https://anyplot.ai/line-markers/javascript/muix",
  "hub": "https://anyplot.ai/line-markers",
  "code_json": "https://api.anyplot.ai/specs/line-markers/muix/code",
  "spec_json": "https://api.anyplot.ai/specs/line-markers",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/line-markers/javascript/muix/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/line-markers/javascript/muix/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/line-markers/javascript/muix/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/line-markers/javascript/muix/plot-dark.html",
  "quality_score": 91.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Line Plot with Markers on anyplot.ai.

Other implementations