npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

activities-heatmap

v0.3.2

Published

A library to draw sport activities heatmap on a tile layer.

Readme

activities-heatmap

Description

This library generates a heatmap based on activity data, highlighting routes and paths. The more frequently a path is used, the brighter it appears on the map. The library can be used to implement a tile layer server that renders heatmap tiles efficiently.

Background

There are already tools and libraries to draw a heatmap of activities, for example strava-local-heatmap-tool. But the rendering is done on the client side, which can be inefficient for large dataset. As of November 2024, I have about 1800 activities representing 1.5 million vertices. Downloading and parsing the data on the client side would be prohibitive. That is why I developed that library for use on the server side with a Node.js application.

Installation

npm install activities-heatmap

Usage

The following code renders the heatmap for the tile x, y, z:

      import { HeatmapProducer } from 'activities-heatmap';

      const producer = new HeatmapProducer(datasource);
      const bitmap = await producer.BitmapForTile({x: x, y: y, z: z});

z is the zoom level, and x and y are tile coordinates, according to XYZ standard implemented in OpenLayers, Leaflet, Mapbox, etc.

Providing the data

The dataSource argument of HeatmapForTile represents an object that provides access to activity data, and can retrieve paths within a given bounding box. Two activities sources are provided:

  • StravaLocalZip: reads activities from a local zip bulk export from strava.
  • PostgisDB: reads activities from a postgis database.

You can implement your own data source by implementing the ActivitiesSource interface.

Filtering

data sources can optionally support filtering, to display the heatmap for a subset of the activities only. By providing an activitiesFilter argument to HeatmapForTile, it will pass than filter to the data source. Filter options will be specific to each data source.

Both StravaLocalZip and PostgisDB support filtering by date, and by sport type. If you implement your own datasource, you can decide which filters to implement.

      const bitmap = await producer.BitmapForTile({
                                          x: x, y: y, z: z,
                                          activitiesFilter: {startDate: '2020-01-01', endDate: '2020-12-31'}
                                         });

Rendering options

You can customize the heatmap appeearance by passing a renderingOptions argument to HeatmapForTile. The rendering options are:

  • valueForMaxColor: Specifies the threshold for the brightest color. If a path has been taken more than this number of times, it is rendered with the brightest color. (Default is 25).
  • lineWidth: Defines the width of the lines in the heatmap. (Default is 2).
  • gradientColors: An array of colors to use as linear gradients when picking a color for the path.
    • Default is [ [0x4B, 0x00, 0x82, 130], [0xB2, 0x22, 0x22, 155], [0xFF, 0x00, 0x00, 180], [0xff, 0x45, 0x00, 205], [0xFF, 0x69, 0x00, 230], [0xFF, 0xFF, 0xE0, 255] ].
    • For example, if the path has been taken more than valueForMaxColor, it will be rendered with the color [0xFF, 0xFF, 0xE0, 255] (aka rgba(255, 255, 224, 1)). If it has been taken between 80% and 100% of that value, it will be rendered with a color between [0xFF, 0x69, 0x00, 230] and [0xFF, 0xFF, 0xE0, 255] (aka rgba(255, 105, 0, 0.9) and rgba(255, 255, 224, 1)). And so on.
    • By passing a different array of colors, you can change the rendering colors of the heatmap.
      const bitmap = await producer.BitmapForTile({
                                          x: x, y: y, z: z,
                                          renderingOptions: {valueForMaxColor: 50, lineWidth: 3}
                                         });

Implementation

Pixel drawing

The heatmap is created by drawing activity paths on a bitmap. Each time a path goes trough a pixel, the count is incremented for that pixel. After all paths have been processed for, each pixel is colored according to the count.

I use anti-aliasing to avoid the heatmap paths from being blurry. It means that I cannot use existing libraries like node-canvas or skia-canvas for line rendering, and had to implement the line drawing algorithm inside the activities-heatmap. With anti-aliasing and a configurable linewidth, it requires to draw a polygon for each path. Fortunately, there are good resources to implement that algorithm.

Indeed, each color channel contains only 256 values. So if I want to use 16 values for antialiasing, I would be able to draw only 16 (256/16) paths before reaching the maximum value. With the default value, the maximum color is applied for 25 paths. So in theory, I could have used 8 (256 / 32) levels of antialiasing. But I want to provide the ability to specify a value of more than 32.

Web workers

Rendering paths is computationally intensive, and is done on the main thread. Unfortunately, moving the computation to a web worker would not improve the situation, since the serialization/deserialization of the paths would take longer than the computation itself.

Demo

The website at https://heatmap.renevier.net/ is a simple application that uses this library. It displays a heatmap of the author's list of Strava activities from 2018 to 2024. The code source is available at https://github.com/arenevier/heatmap-demo

For reference:

strava blog about the implementation of the global heatmap: https://medium.com/strava-engineering/the-global-heatmap-now-6x-hotter-23fc01d301de