06Object 06 · Displays & signs
Odometer
A mechanical drum counter. A digit moves only while the one to its right carries, so 1,099 to 1,100 rolls three drums over together. Not a number that fades into another.
- Version
- 0.1.0
- Size
- 42.3 KB gzipped
- Access
- Free · source included
- Needs
- odometer.mjs + odometer.css
- Tests
- 24 tests · 12 states
Use it
Show a number that matters as a machine that counted it: a founder price, seats remaining, downloads, a live tally, a donation total. The number stays real text for screen readers and search; the drums are what the eye believes.
Copy the odometer folder from its package next to your page, link its stylesheet and construct it on an element. Every option has a default; the lab’s Code tab writes the same example at any settings, and for React, Vue, Svelte or a custom element (Using a framework).
<link rel="stylesheet" href="odometer/odometer.css">
<div id="odometer" style="width: min(92vw, 960px); height: min(56vh, 420px); display: grid; place-items: center"></div>
<script type="module">
import Odometer from './odometer/odometer.mjs';
const odometer = new Odometer(document.getElementById('odometer'), {
value: 1099,
decimals: 0,
prefix: '',
});
// odometer.update({ … }); change any option in place
// odometer.destroy(); and take it away again
</script>Options
| Option | Type | Default | What it does |
|---|---|---|---|
| value | number, -999999999 to 999999999999 | 1099 | Where the drums start. The buttons under the preview roll it from there. |
| decimals | number, 0 to 3 | 0 | Drums after the decimal point. |
| prefix | text | '' | Printed on the plate before the window: a currency sign. |
| suffix | text | '' | Printed on the plate after the window: a unit, or "seats left". |
| locale | en-US · en-GB · de-DE · fr-FR · de-CH · en-IN · ar-EG | 'en-US' | Group and decimal marks, grouping pattern and digit glyphs. |
| grouping | boolean | true | Fixed plates between the drums: 1,099 rather than 1099. |
| digits | number, 1 to 12 | 6 | Whole-number drums kept even when the value is shorter, so a growing number never shifts the layout. |
| zeros | boolean | true | Print 001,099 like a car odometer. Off, the leading drums print a blank where their zero would be. |
| finish | ink · bone · steel · brass | 'ink' | Bone figures on ink drums, ink on bone, white on steel, or brass. |
| red | none · last · decimals | 'none' | The classic odometer tenths: the last drum, or every decimal drum, in signal red. |
| bezel | slot · cells · bare | 'slot' | One slot across the drums, a window per drum like a tally counter, or bare drums to sit in a line of text. |
| stiffness | number, 40 to 400 | 140 | How hard the value is pulled to a new number. |
| damping | number, 0.4 to 1.4 | 0.85 | Below 1 the drums overshoot and settle back; 1 and above they arrive without passing. |
| maxSpin | number, 6 to 60 | 24 | Past this a drum is drawn spinning at the cap, as a blur, instead of strobing. |
| count | number, -20 to 20 | 0 | A live tally: add this much every second, in steps of the last drum. Negative counts down; 0 is off. |
| wear | number, 0 to 1 | 0.35 | Drums a little off their detents, figures a little off register. |
| label | text | '' | Cut into the plate under the window and filled with paint, ruled out to the window’s edges. |
| light | number, 0 to 345 | 315 | 0 is from above, clockwise. |
| size | number, 0 to 160 | 0 | 0 fills the frame it is put in and follows it. Any other value is the numeral size, shrunk only when the frame is narrower. |
| sound | boolean | false | A soft synthesised tick per step of the last drum. Starts only after you touch the page. |
Methods
new Odometer(element, options)- Builds it inside the element with these options.
value- The value the drums are going to (or showing, once landed).
statesetValue(v, { instant = false } = {})- Roll to a new value.
count(rate = 1)- A live tally: add
rate(value per second, negative counts down) in whole steps of the last drum, until count(0). update(opts = {})- Change options in place.
destroy()- Stops it and cleans up after it.
States
- At restThe drums sit on the value, each a hair off true with wear; the loop is asleep and costs nothing.
- Filling its frameWith size "fit" (the default) the counter takes the largest numeral size that fits its host, measured with the counter out of it, and follows the host as it resizes; a fixed size shrinks only to fit.
- Face loadingThe bundled Big Shoulders face is block-displayed: the drums show no figures for the few milliseconds before it arrives, then the figures are optically re-centred on the loaded face, never a fallback that jumps.
- RollingA new value is a spring target: the last drum turns, and each drum above moves only while the one to its right passes 9 → 0, so carries roll together at the end of the turn.
- SpinningA jump past the spin cap draws the fast drums turning at the cap, smeared along the turn, while the slow drums visibly roll; every drum lands exactly.
- Overshoot and settleWith damping below 1 the value passes its target and the drums wind back; at 1 and above they arrive without passing.
- Countingcount(rate) adds a steady tally in steps of the last drum; a slow tally sleeps between its steps and wakes for the next; the live text stops announcing and is kept current once a second.
- GrowingReserved drums show blanks for leading zeros, so a growing number fills in without moving; past the reserve a drum is added at the start of the roll.
- NegativeA sign plate flips into the window and the drums show the magnitude.
- LandedOnce the value has landed and every smear has faded, the visually hidden text changes and is announced politely, once, never per frame.
- Off screen or hiddenThe loop pauses; a returning tab cannot launch the spring with a long frame.
- Reduced motionThe value changes instantly with no spin and no frame requested; counting still counts, by jumps.
How it works
Made of: Carry chain, Value spring, Spin cap motion blur, View fixed cylinder light, Intl number parts, Landed only live region, Sleeping loop, Fit to host, Lamp lit chamfers, Sleeping tally.
Read more: How the Odometer carries · Odometer, odometer.js, NumberFlow.
Original. Taken from the mechanism of real drum counters (car odometers, tally counters, petrol-pump registers), not from a catalogue reference, so references is honestly empty. DOM text on CSS 3D drums. The numerals are Big Shoulders Bold (SIL OFL 1.1, a Latin subset converted to WOFF2), shipped beside the stylesheet with its licence text, so the drums print the same figures on every platform; the old platform fallbacks (Bahnschrift, DIN) remain in the stack only in case the face cannot load.
Blocks built with it
- 06.1Pricing DrumsA price printed on brass drums.
- 06.2Goal TallyA fundraiser’s total on big printed drums.
- 06.3Trip ComputerAn instrument cluster as a stats band.
- 06.4Specimen CaseA bento of glass-topped compartments in a machined case, each holding one real Tactile object running small (the Odometer, the Flip-Dot, the Detent Dial, the Vessel) with an engraved label card under the glass.