Ellipse Axis Explorer

A square SVG plane that teaches the focal definition of an ellipse. The user drags two sliders — semi-major axis a and semi-minor axis b — and the ellipse, its axes, the two foci, and the eccentricity readout all update live.

Ellipse with semi-major axis 1.50 and semi-minor axis 1.00. Eccentricity 0.75.
1.50
1.00
e = c / a = 1.12 / 1.50 = 0.75
Customize
Axes
1.50
1.00
Overlays

Installation

npx shadcn@latest add https://craftbits.dev/r/ellipse-axis-explorer.json

Usage

import { EllipseAxisExplorer } from "@craft-bits/core";
 
<EllipseAxisExplorer defaultA={1.5} defaultB={1.0} />

Drive both axes as controlled values:

const [a, setA] = useState(1.5);
const [b, setB] = useState(1.0);
 
<EllipseAxisExplorer a={a} onAChange={setA} b={b} onBChange={setB} />

Hide the foci to focus on the axes alone:

<EllipseAxisExplorer defaultA={1.5} defaultB={1.0} showFoci={false} />

Hide the eccentricity readout when you only want the visualization:

<EllipseAxisExplorer
  defaultA={2}
  defaultB={1}
  showEccentricity={false}
/>

Understanding the component

  1. Parametric ellipse polyline. The curve is sampled at 96 points along (a cos t, b sin t) for t in zero to two pi and rendered as a motion.polyline so the points attribute can spring between snapshots without React re-rendering on every frame.
  2. Foci on the major axis. The focal distance is sqrt(a squared minus b squared). When a is the larger axis the foci sit on the x axis; when the user pushes b past a the construction flips and the foci move to the y axis.
  3. Eccentricity as a ratio. Eccentricity equals c divided by the larger semi-axis — zero for a circle, approaching one as the ellipse stretches thin. The readout surfaces the full expression so the relationship between c, the major axis, and e stays visible.
  4. Auto-fit viewport. The math-space half-extent is 12 percent larger than the bigger of the two slider maxes, so the ellipse and foci stay comfortably inside the frame across the entire slider range without popping.
  5. Two strokes, two roles. A faint accent fill at 8 percent opacity sits behind a solid accent outline so the ellipse reads as both a shape and a curve — the fill establishes the body, the outline carries the precision.
  6. Built on LabeledSlider. Both sliders are instances of the library's LabeledSlider — installing this component via shadcn pulls labeled-slider in as a transitive dependency, free a11y included.
  7. Reduced motion. When prefers-reduced-motion: reduce is set the ellipse, axes, and foci snap to their new positions with no spring.

Props

PropTypeDefaultDescription
anumberControlled semi-major axis. Pair with onAChange.
defaultAnumber1.5Uncontrolled initial semi-major axis.
onAChange(value: number) => voidFires when a changes.
bnumberControlled semi-minor axis. Pair with onBChange.
defaultBnumber1.0Uncontrolled initial semi-minor axis.
onBChange(value: number) => voidFires when b changes.
aRangereadonly [number, number][0.5, 2.5]Slider range for a.
bRangereadonly [number, number][0.3, 2.5]Slider range for b.
showAxesbooleantrueDraw the dashed semi-axis lines.
showFocibooleantrueRender the two focal points.
showEccentricitybooleantrueRender the eccentricity readout.
sizenumber320SVG side length in pixels (the plane is square).
transitionTransitionSPRINGS.smoothSpring for ellipse / axis transitions.
classNamestringMerged onto the root via cn().

Accessibility

  • The root renders with role="figure" and aria-labelledby pointing at a visually hidden aria-live="polite" summary — assistive tech announces the new axes and eccentricity as the user drags the sliders.
  • The SVG itself is aria-hidden; the visible sliders carry their own labels via LabeledSlider, so keyboard users can adjust each axis with arrow keys and the focus ring lands on a native input.
  • The eccentricity readout uses tabular numerals so the columns stay aligned as values change.
  • Animation respects prefers-reduced-motion: reduce — every spring collapses to an instant swap.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/math/EllipseAxisExplorer.tsx). The source was a linear-algebra widget that morphed a unit circle into an ellipse and overlaid a narration state machine for "scalar multiple of identity" / "ill-conditioned" / "singular." The library extract drops the eigenvalue framing and rebuilds the component around the focal definition — semi-major and semi-minor axes, foci at distance c from the center, and eccentricity e = c / a — so it teaches conic sections instead of linear maps.