Number Line Zoomer

An interactive number line that zooms around a chosen center, exposing the ULP (unit-in-the-last-place) grid for IEEE 754 floats. Each zoom step halves the visible range, surfacing the spacing between adjacent representable values at float32 or float64 precision. Sibling to FloatBitViewer — together they teach why 0.5 + 1e-9 rounds back to 0.5 in low-precision training and why subnormals exist near zero.

Number line zoomer · float32Number line zoomer: float32, center 0, range 0.007813, ULP 1.40e-45, 5.58e+42 representable values in window.
float32 · zoom 2^8center 0 range 0.007813 ulp 1.40e-45
-0.0039060.00390605.58e+42 representable values (too dense to draw)
2^8
Customize
Window
0.00
2^8
Display
float32

Installation

npx shadcn@latest add https://craftbits.dev/r/number-line-zoomer.json

Usage

import { NumberLineZoomer } from "@craft-bits/core";
 
<NumberLineZoomer defaultCenter={0} defaultZoomLevel={8} precision="float32" />

Drive center and zoom externally:

<NumberLineZoomer
  center={center}
  zoomLevel={zoom}
  precision="float32"
  onCenterChange={setCenter}
  onZoomLevelChange={setZoom}
/>

Hide the ULP ticks for a dense composition:

<NumberLineZoomer
  defaultCenter={2 ** 20}
  defaultZoomLevel={0}
  precision="float32"
  showUlps={false}
/>

Anatomy

  1. Two state pairs, both Radix. center / defaultCenter plus zoomLevel / defaultZoomLevel. Pass the controlled prop and an on*Change callback, or the default* prop alone — the component picks the right mode automatically.
  2. Each zoom level halves the range. Zoom 0 spans [-1, 1] around the center; zoom 8 spans roughly [-0.004, 0.004]; zoom 20 spans roughly [-1e-6, 1e-6]. The center is the fixed point of the zoom — pan by moving center, not by changing the bounds.
  3. Math.fround is the truth for float32. Every center is snapped to the nearest float32 (or left untouched for float64) before rendering, so the highlighted middle always sits on a representable value rather than between two ULPs.
  4. ULP grid via the IEEE 754 formula. For a normal float x at precision p, the ULP is two raised to the power of floor(log2(absolute x)) minus the mantissa bit count. Ticks anchor on the center and march outward by that ULP. Near zero (and in the subnormal range) the formula collapses to the smallest representable subnormal, so the grid stays well-defined even when the center is exactly zero.
  5. Dense-band fallback. When the number of ticks in the window exceeds 200 — common for float64 at moderate zoom — drawing them individually would crush the renderer. Above that threshold the component swaps to a translucent band with 80 illustrative strokes and an exact count in the readout, so the visual still reads as "too dense to draw".
  6. SPRINGS.smooth for the visible window. The center cross-hair animates between zoom and center changes; prefers-reduced-motion: reduce collapses both to an instant swap.

Props

PropTypeDefaultDescription
centernumberControlled center value. Pair with onCenterChange.
defaultCenternumber0Uncontrolled initial center.
onCenterChange(center: number) => voidFires whenever the center changes.
zoomLevelnumberControlled zoom level in 0 to 20. Each step halves the visible range.
defaultZoomLevelnumber0Uncontrolled initial zoom level.
onZoomLevelChange(zoomLevel: number) => voidFires whenever the zoom level changes.
precision"float32" | "float64""float32"Which IEEE 754 precision to sample representable values from.
showUlpsbooleantrueWhether to render the ULP ticks. When false, only the axis line, center marker, and readout are drawn.
transitionTransitionSPRINGS.smoothSpring used for the center marker between zoom or center changes.
classNamestringMerged onto the root via cn().

Accessibility

  • The figure is role="figure" with a labelled title that names the precision and zoom level.
  • An aria-live="polite" summary announces the precision, center, range, ULP, and approximate tick count whenever the window changes.
  • The zoom slider uses LabeledSlider underneath — rendering as a native range input with Arrow / Home / End keyboard support and a labelled value readout.
  • Color is never the only signal: the textual header above the chart spells out the center, range, and ULP in mono.
  • prefers-reduced-motion: reduce snaps the center marker and zoom transitions with no spring.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/nn/NumberLineZoomer.tsx). The source bundled a lesson-specific format picker (FP8 E4M3, FP8 E5M2, FP16), interval picker over fixed power-of-two intervals, phase-tracked narration, and the project's ChallengeBtn plus SvgLabel chrome. The library version is a pure number-line zoom primitive — controlled and uncontrolled center and zoomLevel pairs, IEEE 754 float32 / float64 sampling via Math.fround and the ULP formula, a LabeledSlider for zoom, a dense-band fallback for sub-threshold tick densities, and cb-accent / cb-border-strong semantic tokens in place of the inline palette.