Inverted Scaling Viz

A scaling-law canvas with room for inverse scaling laws. One or more curves of metric vs model size are drawn on a shared log-x chart so the usual upward curve (standard scaling) and a downward curve (inverse scaling — the surprising case where bigger models do worse) sit side by side.

The classic teaching pair is a textbook capability that improves smoothly with parameter count, alongside a task like TruthfulQA where larger models confidently reproduce common misconceptions more often.

Accuracy vs Model size (params) — inverse scaling plot.Inverse scaling plot, Accuracy vs Model size (params). Standard task: up from 0.180 at 10M to 0.850 at 1T; Inverse-scaling task: down from 0.600 at 10M to 0.180 at 1T.
Customize
Inverse curve
1.00
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/inverted-scaling-viz.json

Usage

import { InvertedScalingViz } from "@craft-bits/core";
 
<InvertedScalingViz
  curves={[
    {
      id: "standard",
      label: "Standard task",
      tone: "accent",
      points: [
        { size: 1e7, metric: 0.18 },
        { size: 1e9, metric: 0.52 },
        { size: 1e12, metric: 0.85 },
      ],
    },
    {
      id: "inverse",
      label: "Inverse-scaling task",
      tone: "warning",
      points: [
        { size: 1e7, metric: 0.62 },
        { size: 1e9, metric: 0.46 },
        { size: 1e12, metric: 0.17 },
      ],
    },
  ]}
/>

Pin the highlight declaratively and lose the legend interaction:

<InvertedScalingViz curves={curves} highlightId="inverse" />

Drive the highlight from outside:

<InvertedScalingViz
  curves={curves}
  currentCurveId={selected}
  onCurrentCurveChange={setSelected}
/>

Swap to a linear x-axis when the sizes are small and the log isn't pulling its weight:

<InvertedScalingViz curves={curves} xScale="linear" />

Anatomy

  1. Multi-line chart, log-x by default. Scaling-law plots conventionally use a log x-axis so powers-of-ten parameter counts space out evenly. The y-axis stays linear — accuracy, loss, and calibration error all read better unwarped.
  2. Tone signals the regime. Each curve carries a tone (accent / warning / success). The canonical teaching pairing is standard scaling in cb-accent and inverse scaling in cb-warning; success is held in reserve for the "fixed" curve in a before-and-after comparison.
  3. One highlight, the rest dim. highlightId (or the internal legend chip) emphasises one curve with a thicker stroke and larger dots and softens the others to ~45%. The chip strip is a role="radiogroup"; clicking toggles the highlight.
  4. Auto-fit domain. The visible range is the bounding box of every point across every curve, padded multiplicatively under log and additively under linear so the first and last dots sit comfortably inside the plot.
  5. Trend glyph in the legend. Each chip ends in , , or based on the first-to-last delta of its curve — a colour-blind-safe shape signal for which curves are scaling and which are inverse-scaling.
  6. Reduced motion. prefers-reduced-motion: reduce collapses every spring to an instant swap; the chart still highlights, fades, and refits.

Props

PropTypeDefaultDescription
curvesreadonly InvertedScalingVizCurve[]requiredCurves to plot. Empty curves are skipped.
xLabelstring"Model size (params)"X-axis title.
yLabelstring"Accuracy"Y-axis title.
xScale'linear' | 'log''log'X-axis scale — y is always linear.
highlightIdstringOne curve to emphasise. Wins over currentCurveId.
currentCurveIdstring | nullControlled legend selection. Pair with onCurrentCurveChange.
defaultCurrentCurveIdstringUncontrolled initial selection.
onCurrentCurveChange(id: string | null) => voidFires when a legend chip toggles the highlight.
transitionTransitionSPRINGS.smoothSpring for curve and dot transitions.
classNamestringMerged onto the root via cn().

The InvertedScalingVizCurve shape: { id: string; label: string; points: { size: number; metric: number }[]; tone?: 'accent' | 'warning' | 'success' }.

Accessibility

  • The figure is role="figure" with a hidden summary describing each curve's trend and endpoints — screen readers hear the story whenever the data changes.
  • Each dot carries an SVG <title> exposing curve label, metric, and size in the native tooltip.
  • The legend is a role="radiogroup" of role="radio" buttons — Tab walks the chips, Space and Enter toggle the highlight, and :focus-visible paints the accent ring.
  • Curves are differentiated by tone and by their trend glyph (↗ / ↘ / →) in the legend — the inverse-scaling story survives without colour.
  • Motion respects prefers-reduced-motion: reduce.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/nn/InvertedScalingViz.tsx). The source was a single-purpose dropout teaching widget — a clickable neuron row, a scaling toggle, inline animate() calls on circle and glow refs, a four-phase narration state machine, ChallengeBtn chrome, and raw --color-accent-* / --color-fail-* / --color-ink-* vars. Repurposed as the canvas the concept needs: a multi-curve metric-vs-size plot built on motion.path plus SPRINGS.smooth, semantic cb-* tokens, controlled and uncontrolled legend selection, and a colour-blind-safe trend glyph in every legend chip.