Imbalance Scales
A static balance-scale visualization for binary class imbalance. Two pans hang from a horizontal beam pivoting at a central fulcrum; the beam tilts toward whichever class has more samples. Use it to motivate class weighting, oversampling, and balanced metrics (F1, balanced accuracy) on skewed datasets.
Imbalance scales: 1,000 positive vs 100 negative samples. Imbalance ratio 9.1%.
Customize
Counts
1000
100
Display
Installation
npx shadcn@latest add https://craftbits.dev/r/imbalance-scales.jsonUsage
import { ImbalanceScales } from "@craft-bits/core";
<ImbalanceScales positiveCount={1000} negativeCount={100} />Hide the raw pos : neg caption while keeping the imbalance percentage:
<ImbalanceScales
positiveCount={1000}
negativeCount={100}
showRatio={false}
/>Show only the scale with no captions at all:
<ImbalanceScales
positiveCount={1000}
negativeCount={100}
showRatio={false}
showImbalanceRatio={false}
/>Understanding the component
- Two pans, one beam. The left pan is the positive class (
cb-accenttone); the right pan is the negative class (cb-fg-mutedtone). Both hang from chain lines anchored at the ends of a horizontal beam, with a fulcrum triangle at the centre. - Tilt is proportional, not absolute. Beam rotation is
atan2(negative − positive, max(positive, negative))clamped to ±22°. Usingatan2over the heavier pan keeps the visual readable: a 1000 : 1 split tilts almost exactly the same as a 10 : 1 split (both pin to the clamp), while finer ratios in the 1 : 1 to 5 : 1 range get the full sweep. - Pan contents stay upright. The pans translate with the rotating beam, but their inner content (sample-stack rectangle, count, class label) counter-rotates by
−tiltso numbers never read sideways at extreme imbalances. - One spring drives everything. Beam tilt animates on
SPRINGS.smooth. Pan content inherits via the counter-rotation. Reduced-motion users get a{ duration: 0 }transition — the scale snaps to its target tilt. - Imbalance ratio is
min / (min + max). A balanced split reads50.0%; a 1000 : 100 split reads9.1%— the lower the percentage, the more skewed the dataset.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
positiveCount | number | — | Sample count for the positive class. Negative values are clamped to 0. |
negativeCount | number | — | Sample count for the negative class. Negative values are clamped to 0. |
showRatio | boolean | true | Render the raw pos : neg caption underneath the scale. |
showImbalanceRatio | boolean | true | Render the min / (min + max) percentage caption. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- The root
<div>isrole="figure"with a visually hidden summary of the two counts and the imbalance ratio. - The SVG carries
role="img"and is labelled by the same hidden summary. - All decorative geometry is marked
aria-hidden. - Motion respects
prefers-reduced-motion: reduce— beam tilt collapses to an instant update.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/nn/ImbalanceScales.tsx). The source was a full lesson primitive with a drag-the-class-weight slider, ×99 balance celebration, phase narration, gradient-contribution bars, and a live equation readout. The library extract is the bare imbalance visual — pass two counts, get a tilted scale.