---
title: "EDM115 - Project EDM115&#x2F;light-odometer"
canonical_url: "https://edm115.github.io/website/website/projects/light-odometer"
last_updated: "2026-08-20T22:18:03.747Z"
meta:
  description: "For personal use only. Use mtmarco87/tm-odometer instead"
  "og:description": "For personal use only. Use mtmarco87/tm-odometer instead"
  "og:title": "EDM115 - Project EDM115/light-odometer"
---

Home

# light-odometer

![NPM Version](https://img.shields.io/npm/v/light-odometer)![NPM Downloads](https://img.shields.io/npm/dt/light-odometer) [![More info](https://img.shields.io/badge/npmx-More_info-orange?logo=npm)](https://npmx.dev/light-odometer)

This project is made only for personal use, as a small and lightweight build of [tm-odometer](https://github.com/mtmarco87/tm-odometer), which is itself a fork of [HubSpot’s odometer](https://github.com/HubSpot/odometer).  
 ⚠️ Do not use this, use [@mtmarco87](https://github.com/mtmarco87)’s package instead.  
 Huge props to him for this TypeScript refactor !  
  

No theme is shipped here, no docs either.

## What’s changed ?

For a quick overview in a real-world usage, see https://github.com/EDM115/website/blob/master/app/components/ui/Odometer.vue

### Breaking changes

- Renamed from `TmOdometer` to `LightOdometer`
- Removal of all built artifacts, themes, demo, screenshots and CoffeeScript code
- Switch from Rollup to tsdown (Rolldown)
- Remove compatibility layers (Internet Explorer, jQuery)
- Keep only the ESM build and switch to a default export

### New features

- SSR friendly (you can import it top-level without blowing up) and use safe fallbacks for browser-land functions
- Each instance can now have an `id`
- Ability to customize the frameratets

  ```
  const odo = new LightOdometer({ ..., framerate: 20 })
  // default is 30, going above isn't recommended
  // use countFramerate if you use the `count` mode instead of `slide`
  ```
- Ability to render once and destroy the instancets

  ```
  const odo = new LightOdometer({ ..., value: 0 })
  odo.animateOnceAndDisconnect(12345)
  ```
- Expose `duration` as a CSS property to sync up JS and CSS animationsts

  ```
  const odo = new LightOdometer({ ..., duration: 5000 })
  console.log(getComputedStyle(odo.el).getPropertyValue("--odometer-duration")) // "5000ms"
  ```
- Get and mutate instance and global optionsts

  ```
  const odo = new LightOdometer({ ... })
  console.log(odo.getOptions().duration)
  odo.setOptions({ duration: 2000 })

  console.log(LightOdometer.getGlobalOptions.selector)
  LightOdometer.setGlobalOptions({ selector: ".my-odometer" })
  ```
- Add per-instance subscriptable animation start/end events, they give back the instance id, el, instance, value, oldValue, optionsts

  ```
  const odo = new LightOdometer({ ... })
  console.log(odo.isAnimating)

  function onStart(e: Event) {
    const { detail } = e as CustomEvent<LightOdometerEventDetail>
    // detail.id, detail.value, detail.instance.isAnimating, ...
    console.log(`started animating to ${detail.value}`)
  }

  function onDone(e: Event) {
    const { detail } = e as CustomEvent<LightOdometerEventDetail>
    console.log(`finished animating to ${detail.value}`)
  }

  odo.on("odometerstart", onStart)
  odo.on("odometerdone", onDone)

  // later
  odo.off("odometerstart", onStart)
  odo.off("odometerdone", onDone)
  ```
- Print the instance as a JSON-friendly stringts

  ```
  const odo = new LightOdometer({ ... })
  console.log(odo.toString())
  ```json

  ```
  {
    "id": 2,
    "value": 157,
    "options": {
      "id": 2,
      "value": 0,
      "animation": "slide",
      "duration": 8000,
      "format": "( ddd)",
      "framerate": 20
    },
    "globalOptions": {},
    "watchMutations": false,
    "transitionEndBound": true,
    "destroyed": false,
    "format": {
      "repeating": " ",
      "precision": 0
    },
    "isAnimating": false
  }
  ```

### Improvements

- `value` is now stripped from spaces
- Multiple performance improvements
- Use overall more optimized methods
- Stabilize and simplify the interfaces

### Other changes

- No more const functions
- Better TS config and stricter types (no more `any` nor `as`)
- Use classes private elements
- Added linting and formatting (OxLint + ESLint Stylistic)
- Target ES2023 instead of ES2015