Car Gauge API

Pure JS Canvas Gauge Library

Quick Start

A pure JavaScript canvas gauge library with spring-damper needle physics. No dependencies, no build step required for usage.

// HTML: <div id="my-gauge" style="width:200px;height:200px"></div>
import { Gauge } from './src/index.js';

const gauge = new Gauge(document.getElementById('my-gauge'), {
  min: 0, max: 100, label: 'SPEED', units: 'MPH'
});
gauge.setValue(60);

Constructor

new Gauge(element, config)

  • element — An HTMLElement. The gauge appends a <canvas> child inside it.
  • config — A preset name string (e.g. 'speed', 'rpm') or a config object merged with defaults.
// Using a preset name
const rpm = new Gauge(el1, 'rpm');

// Using a custom config object
const custom = new Gauge(el2, {
  min: 0, max: 200,
  label: 'KPH', units: 'km/h',
  majorTicks: 11, minorTicks: 2
});

Methods

setValue(value, options?)

Set the gauge value. Animates by default with spring-damper physics. Pass { immediate: true } to snap instantly. Dispatches gauge:valuechange.

sweep()

Runs a self-test sweep animation: needle goes to max then returns. Dispatches gauge:sweepcomplete when done.

destroy()

Disconnects the resize observer, cancels animation, and removes the canvas element. Call this before removing the gauge from the DOM.

gauge.setValue(75);                   // animated
gauge.setValue(75, { immediate: true }); // instant snap
gauge.sweep();                        // self-test
gauge.destroy();                      // cleanup

Properties

value (getter/setter)

Get or set the current target value. Setting calls setValue() with animation.

vibration (setter)

Enable or disable needle vibration effect (subtle jitter, useful for RPM gauges).

gauge.value = 50;        // same as gauge.setValue(50)
console.log(gauge.value); // 50
gauge.vibration = true;   // enable needle jitter

Events

Events are dispatched on the container element passed to the constructor.

  • gauge:ready — Fired after construction and initial render.
  • gauge:valuechange — Fired on setValue(). Detail: { value }.
  • gauge:sweepcomplete — Fired when a sweep() animation finishes.

Configuration Reference

All configuration keys with their types and defaults. Pass these in the config object to the constructor.

KeyTypeDefaultDescription
minnumber0Minimum scale value
maxnumber100Maximum scale value
unitsstring''Unit text shown below label
labelstring''Main label text on gauge face
majorTicksnumber5Number of major tick marks
minorTicksnumber4Minor ticks between each major
startAnglenumber-225Start angle in degrees (from 12 o'clock)
endAnglenumber45End angle in degrees
stiffnessnumber120Spring stiffness (higher = faster)
dampingnumber18Damping coefficient (higher = less bounce)
redlineStartnumber-Value where redline zone begins
dangerStartnumber-Value where danger zone begins
showOdometerbooleanfalseShow odometer display
customLabelsstring[]-Custom labels for each major tick
faceStylestring'light''light' or 'dark' face background
colorsobject{}Color overrides (see Colors section)
zonesarray[]Warning/danger zone arcs
textsarray[]Arbitrary text placements on face
showDigitalValuebooleanfalseShow simple digital numeric readout (legacy; prefer digitalDisplay)
digitalDisplayobjectnullAdvanced digital readout with position, font size, units display (see Modern Features)
labelFontSizenumber1Label font size multiplier (auto-shrinks for long labels)
needleGlowbooleanfalseAdds a colored glow effect behind the needle
activeTicksobjectnullIlluminate ticks up to current value (see Modern Features)
innerRingobjectnullDecorative ring inside the gauge face (see Modern Features)
microTicksobjectnullDense minor tick marks for high-resolution scales (see Modern Features)
progressArcobjectnullColored arc that fills proportional to current value (see Modern Features)
ringsarray[]External segmented ring indicators (see Modern Features)
complicationsarray[]Sub-gauges embedded on the dial face (see Modern Features)
onDrawfunctionnullCustom draw callback (ctx, state) => {} called each frame after needle

Here is a "kitchen sink" gauge demonstrating many config keys:

new Gauge(el, {
  min: 0, max: 120,
  label: 'POWER', units: 'kW',
  majorTicks: 7, minorTicks: 3,
  faceStyle: 'dark',
  showDigitalValue: true,
  labelFontSize: 1.2,
  zones: [
    { start: 80, end: 100, color: 'rgba(255,165,0,0.15)' },
    { start: 100, end: 120, color: 'rgba(204,32,32,0.15)' }
  ],
  texts: [{ text: 'EV', x: 0, y: -0.25 }]
});

Colors & Theming

The colors object overrides individual gauge colors. All keys are optional; unset keys use smart defaults based on faceStyle.

KeyAffectsLight DefaultDark Default
faceFace background gradient#FEFEFE#1A1A1A
needleNeedle gradient base#CC1010#CC1010
ticksMajor tick marks#1A1A1A#E0E0E0
minorTicksMinor tick marks#404040#888888
numbersNumber text#1A1A1A#D0D0D0
labelLabel text#2A2A2A#E0E0E0
unitsUnits text#555555#AAAAAA
redlineRedline/danger zone#CC2020#FF4040

Three gauges: default light, custom colors, and dark face.

Modern Features

These advanced features enable contemporary instrument cluster designs with glowing needles, illuminated ticks, segmented rings, digital readouts, and embedded sub-gauges. See the modern preset for a full working example.

needleGlow

When true, adds a soft colored glow behind the needle using the needle's color. Simple boolean toggle.

digitalDisplay

Advanced digital readout replacing the simple showDigitalValue boolean. Provides full control over position, size, colors, and units display.

KeyTypeDescription
showbooleanEnable the digital display
ynumberVertical position (-1 to 1, 0 = center)
fontSizenumberFont size multiplier (e.g. 2 for large)
colorstringText color (hex or CSS color)
backgroundbooleanShow a dark background behind the value
showUnitsbooleanDisplay units text alongside the value
unitsColorstringColor for the units text
unitsFontSizenumberFont size multiplier for units text

activeTicks

Illuminates tick marks from minimum up to the current needle position, creating a "fill" effect.

KeyTypeDescription
colorstringColor for illuminated ticks (e.g. '#FFAA00')

innerRing

Draws a decorative circular ring inside the gauge face, useful for framing a digital readout.

KeyTypeDescription
radiusnumberRing radius as fraction of gauge radius (0.1 – 0.8)
widthnumberLine width in pixels (1 – 6)
colorstringRing color

microTicks

Draws a dense set of fine tick marks for high-resolution scales (e.g. one per km/h on a speedometer).

KeyTypeDescription
countnumberTotal number of micro ticks around the arc (10 – 500)
colorstringTick color

progressArc

A colored arc that fills proportional to the current gauge value, drawn along the tick arc.

KeyTypeDescription
widthnumberArc width as fraction of gauge radius (e.g. 0.04)
offsetnumberRadial offset from center (0.5 – 1.0)
backgroundstringBackground color for the unfilled portion
glowbooleanAdd glow effect to the filled arc
gradientarrayColor stops: [{ at: 0, color: '#00ff00' }, { at: 1, color: '#ff0000' }]

rings

External segmented ring indicators, often used for RPM/rev bands outside the main bezel. Each ring is an object in the array:

KeyTypeDescription
min / maxnumberValue range for the ring
widthnumberRing width as fraction of gauge radius
offsetnumberRadial offset (1.0 = at bezel edge, >1.0 = outside)
startAngle / endAnglenumberAngular span in degrees
segmentsnumberNumber of discrete blocks (0 = continuous)
backgroundstringColor for unfilled segments
gradientarrayColor stops: [{ at: 0, color: '#0f0' }, ...]
flashobjectFlash effect: { above: 7000, color: 'rgba(255,0,0,0.5)', rate: 4 }

complications

Embedded sub-gauges on the dial face (fuel gauge, oil temp, etc.). Each complication is an object in the array:

KeyTypeDescription
typestringComplication type ('arc')
x / ynumberPosition on dial face (-1 to 1)
radiusnumberSize reference for tick/marker width
min / maxnumberValue range
startAngle / endAnglenumberAngular span in degrees
arcOffsetnumberRadial position of the arc
arcWidthnumberArc line width
zonesarrayColor zones: [{ start: 0, end: 0.15, color: '#FF3333' }]
fillobjectFill arc: { color, widthMultiplier, glow }
tickMarksnumberNumber of tick marks on the arc
markerbooleanShow a position marker on the arc
markerColorstringMarker color
labelSidestring'inside' or 'outside' for label placement
labelFontSizenumberFont size for labels
labelsarrayLabel positions: [{ text: 'E', position: 0 }, { text: 'F', position: 1 }]

onDraw

Custom draw callback invoked each animation frame after the needle is drawn. Receives the canvas 2D context and a state object with { value, angle, centerX, centerY, radius }.

Here is the built-in modern preset demonstrating these features together:

new Gauge(el, {
  min: 0, max: 320,
  units: 'km/h',
  faceStyle: 'dark',
  needleGlow: true,
  colors: { needle: '#FF8800', ticks: '#CCCCCC' },
  digitalDisplay: {
    show: true, fontSize: 2,
    color: '#FFFFFF',
    showUnits: true, unitsColor: '#999999'
  },
  activeTicks: { color: '#FFAA00' },
  innerRing: { radius: 0.38, color: '#333333', width: 2 },
  microTicks: { count: 320, color: '#444444' },
  rings: [{
    min: 0, max: 8000, width: 0.04, offset: 1.08,
    segments: 30,
    gradient: [
      { at: 0, color: '#2255FF' },
      { at: 1, color: '#FF2200' }
    ]
  }],
  complications: [{
    type: 'arc',
    startAngle: 216, endAngle: 144,
    arcOffset: 0.82, arcWidth: 0.012,
    fill: { color: '#00CCBB', glow: true },
    labels: [
      { text: 'E', position: 0 },
      { text: 'F', position: 1 }
    ]
  }]
});

Presets Reference

Built-in presets for common automotive gauges. Pass the preset name as the config argument.

Container Requirements

The container element must have explicit dimensions. The gauge uses the smaller of width/height to maintain a square aspect ratio. Use aspect-ratio: 1 for best results.

<!-- Recommended: square container -->
<div style="width: 200px; aspect-ratio: 1"></div>

<!-- Also works: explicit width + height -->
<div style="width: 300px; height: 300px"></div>

<!-- Responsive: percentage width -->
<div style="width: 50%; aspect-ratio: 1"></div>
150 x 150
250 x 150 (uses min)
100 x 100

Advanced

NeedlePhysics

The needle uses a second-order spring-damper system. The equation of motion:

// acceleration = stiffness * (target - angle) - damping * velocity
// Semi-implicit Euler integration at 120Hz fixed timestep
  • stiffness — Higher values make the needle respond faster.
  • damping — Higher values reduce oscillation/overshoot.

Constructor: new NeedlePhysics(config). Key methods: setTarget(angle, immediate), update(timestamp), isSettled(threshold).

GaugeRenderer

Handles all canvas drawing. Static layers (bezel, face, ticks, numbers) are pre-rendered to an OffscreenCanvas. Per-frame layers (needle, center cap, digital value) are drawn each animation frame.

Layer pipeline: Outer Shadow → Bezel → Inner Lip → Face → Texts → Zones → Ticks → Numbers → Label → Odometer → Glass Highlight.

Compare stiffness/damping: "loose" (stiffness 40, damping 8) vs "tight" (stiffness 300, damping 30).

LOOSE (40/8)
TIGHT (300/30)