Put a measured latency on a chart without changing what it means.
An engineer is adding a five-minute latency chart to a service dashboard. The vertical plot area runs from 220 px at the bottom to 20 px at the top. Its chosen domain is 100–900 ms. A measured request took 500 ms. Where should its mark appear?
This mapping changes coordinates, not the request. The latency is still 500 ms; the chart position is 120 px from the top of the drawing coordinate system used here. Keeping those quantities separate helps prevent a visualization detail from being mistaken for telemetry.
- Measured value
- 500 ms for one request in the illustrative window.
- Input domain
- 100 ms at one anchor; 900 ms at the other.
- Output range
- 220 px at the bottom; 20 px at the top.
- Question
- What coordinate represents 500 ms, and what should happen beyond the domain?
Normalize first; then place that fraction in the output range.
Let x be the measured value, x₀ and x₁ the input
anchors, and y₀ and y₁ the output anchors. First calculate the fraction
of the way from the first input anchor to the second:
t = (x − x₀) ÷ (x₁ − x₀)
Then apply that same fraction to the output interval:
y = y₀ + t × (y₁ − y₀)
For the chart, t = (500 ms − 100 ms) ÷ (900 ms − 100 ms) = 400 ms ÷ 800 ms = 0.5. Milliseconds cancel, so t is dimensionless. Then y = 220 px + 0.5 × (20 px − 220 px) = 120 px. The output difference is
negative because screen coordinates increase downward: larger latency moves upward toward
the top anchor.
| Input latency | Normalized position | Chart coordinate |
|---|---|---|
| 100 ms | 0 | 220 px |
| 500 ms | 0.5 | 120 px |
| 900 ms | 1 | 20 px |
Interpolation stays between anchors; extrapolation continues past them.
When x lies between x₀ and x₁, t lies
between 0 and 1. That is interpolation. A value outside the interval makes t less than 0 or greater than 1; applying the same equation then extrapolates beyond the anchors.
Neither operation changes the original measurement.
For 1,100 ms, t = (1,100 − 100) ÷ 800 = 1.25. Extrapolation gives 220 + 1.25 × (20 − 220) = −30 px. A negative chart coordinate is outside the
plot. Clamping the input to 900 ms instead gives 20 px, but the chart should still show an
over-range cue or let the reader inspect the original 1,100 ms.
The two input anchors must differ; otherwise the normalized fraction divides by zero. The mapping also assumes you want a linear relationship between these endpoints. A chart may use a logarithmic scale or a piecewise scale instead, especially when values span orders of magnitude. Choosing a mapping is a product and measurement decision, not something the formula decides for you.
A coordinate can be correct while the visualization still misleads.
The input domain is chosen by the chart, not discovered by interpolation. A fixed 100–900 ms domain makes two releases visually comparable, but hides detail near 100 ms and caps visible values above 900 ms. An automatically changing domain uses more of the plot but can make identical latencies appear at different heights between views.
Diagnose the display before interpreting a surprising chart: check the selected domain, whether the axis is linear, whether outliers were clipped, and whether the plotted statistic is raw, aggregated, or sampled. The interpolation formula only places the value it receives. It cannot tell you whether the input is representative or whether the axis supports the conclusion.
Watch an interval mapping move, clamp, or extend beyond the chart.
The chart uses a fixed domain of 100–900 ms and maps it to 220–20 px. Change the measured latency or turn clamping off. The raw value remains visible in the result, even when the mark is held at a chart boundary.
Raw measurement: 500 ms
Normalized fraction: 0.500
Mapped chart coordinate: 120.0 px
Inside domain: the value is interpolated between the anchors.
Make endpoints, range direction, and boundary behavior part of the function contract.
The TypeScript and Go functions calculate a normalized fraction, map it into the output interval, and allow callers to select clamping or extrapolation. Both reject non-finite values and a zero-width input interval. They intentionally allow a reversed output interval, which is how the chart maps increasing latency toward smaller y coordinates.
Trace the fraction first, then follow how the implementation applies bounds.
export type ScaleOptions = {
clamp?: boolean;
};
/** Map a value between two intervals. The output interval may run backwards, as chart y-axes do. */
export function mapLinear(
value: number,
inputMin: number,
inputMax: number,
outputMin: number,
outputMax: number,
options: ScaleOptions = {}
): number {
const values = [value, inputMin, inputMax, outputMin, outputMax];
if (!values.every(Number.isFinite)) throw new RangeError('All values must be finite.');
if (inputMin === inputMax) throw new RangeError('The input interval must have nonzero width.');
const boundedValue =
options.clamp === false
? value
: Math.min(Math.max(value, Math.min(inputMin, inputMax)), Math.max(inputMin, inputMax));
const fraction = (boundedValue - inputMin) / (inputMax - inputMin);
return outputMin + fraction * (outputMax - outputMin);
}
package scale
import (
"errors"
"math"
)
// MapLinear maps a value between two intervals. The output interval may run
// backwards, as a chart's vertical pixel axis commonly does.
func MapLinear(value, inputMin, inputMax, outputMin, outputMax float64, clamp bool) (float64, error) {
values := []float64{value, inputMin, inputMax, outputMin, outputMax}
for _, number := range values {
if math.IsNaN(number) || math.IsInf(number, 0) {
return 0, errors.New("all values must be finite")
}
}
if inputMin == inputMax {
return 0, errors.New("input interval must have nonzero width")
}
boundedValue := value
if clamp {
lower := math.Min(inputMin, inputMax)
upper := math.Max(inputMin, inputMax)
boundedValue = math.Min(math.Max(value, lower), upper)
}
fraction := (boundedValue - inputMin) / (inputMax - inputMin)
return outputMin + fraction*(outputMax-outputMin), nil
}
Transfer the method to queue depth, then test what happens past the scale.
- Input interval
- 0–2,000 queued items.
- Output interval
- 0–320 px, increasing from left to right.
- Observed value
- 750 queued items; then consider a spike to 2,400 items.
- Your task
- Calculate both coordinates with clamping on and off. What should the dashboard communicate about the spike?
For 750 items, t = 750 ÷ 2,000 = 0.375, so the coordinate is 0 + 0.375 × 320 = 120 px. For 2,400 items, the fraction is 1.2 and
extrapolation reaches 384 px, outside the plot. Clamping places the mark at
320 px, but the dashboard should preserve the raw count and signal that it exceeded the
scale.
Now change the output interval to 320 px at the left and 0 px at the right. The normalized fraction remains 0.375, but the coordinate becomes 200 px. The data value did not change; only the visual direction did.