Get StartedComponentsStylingThemingMath & Science
MCP

Science

Plot 2D

Draws a 2D scene with plain SVG: lines, points, vectors and text, with an optional play bar. It does no math and has no dependencies; it only draws the data it is given.

A scene is a plain object: bounds (the visible math window), duration in seconds (0 for a static plot) and a pure frame(t) function that returns the items to draw. Hand-write one, or take it from @indoku/science (for example projectileScene() or waveScene()); the shape is the same.

Markdown

Import

tsx
import { MathRenderer, Plot2D } from "@indoku/ui"import type { Scene2DLike } from "@indoku/ui"​const renderLatex = (latex: string) => <MathRenderer latex={latex} />

Anatomy

<Plot2D scene={{ kind: "2d", bounds, duration, loop?, latex?, frame(t) }} />​frame(t) returns Item2D[]:  { t: "line",   pts: [x, y][], color?, width?, dash? }  { t: "point",  x, y, r?, color?, label? }  { t: "vector", x, y, dx, dy, color?, label? }  { t: "text",   x, y, text, color? }​colors: "accent" | "muted" | "ok" | "warn" | "danger" | any CSS color

Examples

Animated scene

An animated scene plays on its own and shows a play bar with a scrubber and speed buttons. Animation starts paused when the user prefers reduced motion.

P
0.00 / 2.00 s

Controlled time

Pass t to drive the plot from your own state (a slider, a lesson step, scroll position). The play bar is hidden.

P

Vectors, dashes and formulas

Vectors point in data coordinates. renderLatex receives each string in scene.latex. Pass a renderLatex built on MathRenderer to typeset them with KaTeX.

v
R = 40\,\mathrm{m}H_{max} = 12.3\,\mathrm{m}
0.00 / 2.50 s

Static scene

duration: 0 makes a static plot with no play bar. Turn the grid off with grid={false}.

x₁x₂vertex

Props

Plot2D

PropTypeDefaultDescription
scene*Scene2DLike–The scene to draw: bounds, duration and a pure frame(t) function.
tnumber–Controlled time in seconds. Omit to let the component animate itself.
controlsbooleantrueShow the play bar for animated scenes (only when t is not set).
aspectnumber16 / 9Width divided by height of the plot area.
gridbooleantrueDraw the background grid.
renderLatex(latex: string) => ReactNode–Renders each string of scene.latex below the plot. Use (latex) => <MathRenderer latex={latex} />, or your own renderer. Omit to hide them.

* required. Style props and HTML attributes are accepted as well.

Accessibility

  • The SVG has role="img" and uses scene.title as its label.
  • The play bar buttons and the time slider are real form controls with labels, so they work with the keyboard.
  • Autoplay is off when prefers-reduced-motion is set; the user can still press Play or drag the scrubber.

Notes

  • Keep frame(t) pure and cheap: it runs on every animation frame. Build large constant data (like a full trajectory) outside frame, or once per scene.
  • Keep the scene object stable (module constant or useMemo). A new object resets the animation clock.
  • Axes are not equal-scaled. Set aspect to match your bounds when angles matter (a projectile, a circle).

Created by @indokudev