Scales
Scales map data values to visual values — turning a number like 42 into a pixel position, a color, or a band width. They are the bridge between your data domain and the visual range on screen. Ripl ships with a full family of scale types covering continuous, categorical, ordinal, logarithmic, quantile, and time-based mappings.
Every scale is a callable function: pass a domain value in, get a range value out. Scales also expose inverse (reverse mapping), ticks (nice axis values), includes (domain membership), and the original domain/range arrays.
NOTE
For the full API, see the Scales API Reference.
Demo
Use the controls below to explore different scale types. The scale maps a domain value (bottom axis) to a range value (left axis).
Continuous
The most common scale. Maps a numeric domain linearly to a numeric range.
import {
scaleContinuous,
} from '@ripl/web';
const x = scaleContinuous([0, 100], [0, 800]);
x(0); // 0
x(50); // 400
x(100); // 800
x.inverse(400); // 50
x.ticks(5); // [0, 25, 50, 75, 100]Options: clamp (constrain output to range), padToTicks (extend domain to nice tick boundaries), and nice (expand the domain to round, tick-aligned boundaries at construction — true targets ~10 ticks, or pass a number). nice is a construction-time option by design, so scales stay plain callable objects with no chained .nice() method:
const y = scaleContinuous([3, 97], [400, 0], { nice: true });
y.domain; // [0, 100] — snapped to round boundariesBand
Divides a continuous range into evenly spaced bands for categorical data. Exposes bandwidth and step.
import {
scaleBand,
} from '@ripl/web';
const x = scaleBand(['Jan', 'Feb', 'Mar'], [0, 300], {
innerPadding: 0.1,
outerPadding: 0.05,
});
x('Jan'); // band start position
x.bandwidth; // width of each band
x.step; // distance between band startsOptions: innerPadding, outerPadding, alignment, round.
Point
The categorical analogue of a continuous axis — positions discrete values at evenly spaced points rather than bands (no bandwidth). With zero padding the first and last values sit exactly on the range endpoints. Exposes step, and inverse returns the nearest domain value.
import {
scalePoint,
} from '@ripl/web';
const x = scalePoint(['Mon', 'Tue', 'Wed', 'Thu'], [0, 300], {
padding: 0.5,
});
x('Mon'); // first point position
x.step; // distance between adjacent points
x.inverse(105); // nearest domain value, e.g. 'Tue'Options: padding (space before the first and after the last point, as a fraction of the step) and alignment (0–1).
Discrete
Maps discrete domain values to evenly spaced positions in a numeric range.
import {
scaleDiscrete,
} from '@ripl/web';
const color = scaleDiscrete(['low', 'mid', 'high'], [0, 100]);
color('low'); // 0
color('mid'); // 50
color('high'); // 100Ordinal
Maps each distinct domain value to a value from range of any type, cycling when there are more categories than range values. Unknown values encountered later are assigned the next range slot — so a chart can colour series without pre-declaring every category. Its most common use is categorical colour.
import {
scaleOrdinal,
} from '@ripl/web';
const color = scaleOrdinal(
['apples', 'oranges', 'pears'],
['#3a86ff', '#ff006e', '#ffbe0b']
);
color('apples'); // '#3a86ff'
color('pears'); // '#ffbe0b'
color('grapes'); // '#3a86ff' — cycles back to the first range valueDiverging
Like continuous, but splits at a midpoint — useful for scales that diverge around zero (e.g. temperature anomalies, profit/loss).
import {
scaleDiverging,
} from '@ripl/web';
const x = scaleDiverging([-100, 100], [0, 800], {
midpoint: 0,
});
x(-100); // 0
x(0); // 400
x(100); // 800Logarithmic
Maps values using a log transformation. Useful for data spanning several orders of magnitude.
import {
scaleLog,
scaleLogarithmic,
} from '@ripl/web';
const x = scaleLogarithmic([1, 1000], [0, 600], { base: 10 });
x(1); // 0
x(10); // 200
x(100); // 400
x(1000); // 600
// scaleLog is a shortcut for base-10
const y = scaleLog([1, 1000], [0, 600]);Power
Maps values using an exponential transformation. exponent: 2 gives a quadratic curve, exponent: 0.5 gives a square root curve.
import {
scalePower,
scaleSqrt,
} from '@ripl/web';
const x = scalePower([0, 100], [0, 400], { exponent: 2 });
// scaleSqrt is a shortcut for exponent 0.5
const y = scaleSqrt([0, 100], [0, 400]);Quantile
Divides a sorted numeric domain into quantiles, each mapped to a discrete range value.
import {
scaleQuantile,
} from '@ripl/web';
const color = scaleQuantile(
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
['low', 'medium', 'high']
);
color(15); // 'low'
color(55); // 'medium'
color(95); // 'high'Quantize
Divides a continuous domain into uniform segments mapped to discrete range values.
import {
scaleQuantize,
} from '@ripl/web';
const rating = scaleQuantize([0, 100], ['poor', 'fair', 'good', 'excellent']);
rating(20); // 'poor'
rating(40); // 'fair'
rating(60); // 'good'
rating(90); // 'excellent'Threshold
Maps values to range values based on threshold breakpoints.
import {
scaleThreshold,
} from '@ripl/web';
const grade = scaleThreshold([60, 70, 80, 90], ['F', 'D', 'C', 'B', 'A']);
grade(55); // 'F'
grade(65); // 'D'
grade(75); // 'C'
grade(85); // 'B'
grade(95); // 'A'Time
Maps Date objects to a numeric range using linear interpolation of timestamps.
import {
scaleTime,
} from '@ripl/web';
const x = scaleTime(
[new Date('2024-01-01'), new Date('2024-12-31')],
[0, 800]
);
x(new Date('2024-07-01')); // ~400
x.inverse(400); // ≈ Date('2024-07-01')
x.ticks(6); // 6 calendar-aligned datesticks is calendar-aware: it picks the interval (seconds up to years) whose spacing is closest to the requested count, and month/year steps use calendar arithmetic so ticks land on real month and year boundaries rather than fixed millisecond offsets.
Common Scale Properties
Every scale function exposes:
| Property | Type | Description |
|---|---|---|
domain | TDomain[] | The input domain |
range | TRange[] | The output range |
inverse(value) | (TRange) → TDomain | Reverse mapping |
ticks(count?) | (number?) → TDomain[] | Generate nice tick values |
includes(value) | (TDomain) → boolean | Test domain membership |