| .agent/workflows | ||
| .github | ||
| docs | ||
| EXAMPLE_VAULT | ||
| public | ||
| src | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| ARCHITECTURE.md | ||
| CHANGELOG.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| esbuild.config.mjs | ||
| eslint.config.mjs | ||
| generate_examples.sh | ||
| jest.config.js | ||
| LICENSE | ||
| manifest.json | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| RELEASING.md | ||
| ROADMAP.md | ||
| SECURITY.md | ||
| tsconfig.json | ||
| update-version.sh | ||
| version-bump.mjs | ||
| versions.json | ||
Heatmap Tracker plugin for Obsidian
The Heatmap Tracker plugin for Obsidian is a powerful and customizable tool designed to help you track, visualize, and analyze data over a calendar year. Perfect for habit tracking, project management, personal development, or any kind of data visualization, this plugin enables you to create beautiful, interactive heatmaps directly within Obsidian. Whether you’re monitoring progress, visualizing trends, or staying on top of daily goals, the Heatmap Tracker enhances your productivity and organization. Discover its intuitive features, flexible customization options, and seamless integration with Obsidian in the detailed guide below.
Tip: Check Example Vault. There're lots of good examples (and I update it often).
Prerequisites
This plugin requires the Obsidian Dataview plugin to be installed and enabled to automatically fetch data from your notes.
Getting Started
- Install Dataview: Ensure the Dataview plugin is active in your Obsidian vault.
- Add Data to Daily Notes: Add a frontmatter property to your daily notes (e.g.,
YYYY-MM-DD.md) that you want to track.- Numeric:
photo-taking: 10 - Boolean:
photo-taking: true(counts as 1)
- Numeric:
- Insert Heatmap: Use the command
Insert Heatmap Trackerto generate a heatmap through the interactive modal.
Basic Usage
This plugin comes with frontmatter tracking out of the box. You can use the heatmap-tracker codeblock with the following parameters:
```heatmap-tracker
property: <frontmatter_property_key>
```
This will look for frontmatter_property_key in your daily notes and activate a spot on the heatmap wherever that property is set.
You can also use an array of property names as such:
```heatmap-tracker
property: [<frontmatter_property_key_1>, <frontmatter_property_key_2>, ...]
```
This will aggregate the values of all specified properties on the heatmap.
You can narrow down which notes are included with path, tags, and filters — all optional, and all can be combined:
```heatmap-tracker
property: exercise
path: "daily notes"
tags: [journal]
filters:
- property: status
operator: equals
value: done
```
path: folder to search in. Leave unset to search the whole vault.tags: only include notes with at least one of these tags (the leading#is optional).filters: additional frontmatter conditions a note must satisfy — all conditions must match. Each entry has:property: the frontmatter key to check.operator:equals,contains, ornotEmpty.value: compared againstproperty's value (ignored, and not required, fornotEmpty).
Basic Usage 2.0
You can add a heatmap tracker using command: Insert Heatmap Tracker. This is the easiest way to get started.
Advanced Usage
If you want something more involved, you may use a dataviewjs codeblock as such (update trackerData with your own dataset to visualize custom data points):
// Update this object
const trackerData = {
entries: [],
separateMonths: true,
heatmapTitle: "This is the title for your heatmap",
heatmapSubtitle: "This is the subtitle for your heatmap. You can use it as a description.",
}
// Path to the folder with notes
const PATH_TO_YOUR_FOLDER = "daily notes preview/notes";
// Name of the parameter you want to see on this heatmap
const PARAMETER_NAME = 'steps';
// You need dataviewjs plugin to get information from your pages
for(let page of dv.pages(`"${PATH_TO_YOUR_FOLDER}"`).where((p) => p[PARAMETER_NAME])){
trackerData.entries.push({
date: page.file.name,
// Use absolute file path so clicks open the exact note (for cases when you have multiple notes with the same name)
filePath: page.file.path,
intensity: page[PARAMETER_NAME],
});
}
// Optional: set base path so new files are created here if missing
trackerData.basePath = PATH_TO_YOUR_FOLDER;
renderHeatmapTracker(this.container, trackerData);
Notes
- If you provide
filePathfor each entry (page.file.path), clicking a heatmap box opens that exact file. If the file is missing, the plugin offers to create it at the same path. - If
filePathis not set on a box buttrackerData.basePathis provided, the plugin proposes creating/openingtrackerData.basePath/YYYY-MM-DD.md. - If neither is available, it falls back to the Daily Notes settings (folder/format) via the Daily Notes API.
Tracker Settings Documentation
This section is the authoritative reference for every trackerData parameter. EXAMPLE_VAULT has copy-pasteable dataviewjs examples for each one — each parameter below links to its example.
year
- Type:
number - Default: Current year (
new Date().getFullYear()) - Description: Specifies the year for which the heatmap should display data by default.
- Example: year
heatmapTitle
- Type:
string | number - Default:
undefined - Description: Title displayed above the heatmap. Supports HTML for custom styling.
- Example: heatmapTitle
heatmapSubtitle
- Type:
string | number - Default:
undefined - Description: Subtitle/description displayed under the title. Supports HTML for custom styling.
- Example: heatmapSubtitle
colorScheme
- Type:
object - Default:
{
"paletteName": "default",
"customColors": []
}
- Description: Defines the color scale used for representing different intensity levels in the heatmap. Each color corresponds to a specific range of data intensity. Use
paletteNameto reference a palette from plugin settings, orcustomColorsto provide your own array of colors inline. - Example: colorScheme
customColor
- Type:
string - Default:
undefined - Description: Entry property (set on an item inside
entries, not ontrackerDataitself). Sets the color for that specific entry, overridingcolorScheme.
entries
- Type:
array - Default:
[
{ "date": "1900-01-01", "customColor": "#7bc96f", "intensity": 5, "content": "" }
]
- Description: A list of data entries for the heatmap. Each entry includes:
date: The date of the entry (ISO string format).intensity: The data intensity for that date.content: Optional tooltip or note associated with the date.customColor: Overrides the color for that entry.filePath: Absolute path to the file to open when clicked.customHref: Custom URL to open when clicked (takes precedence overfilePath).
- Example: entries
showCurrentDayBorder
- Type:
boolean - Default:
true - Description: Indicates whether the current day should be highlighted with a border on the heatmap.
- Example: showCurrentDayBorder
intensityConfig
- Type:
object - Default:
{
"scaleStart": undefined,
"scaleEnd": undefined,
"defaultIntensity": 4,
"showOutOfRange": true,
"excludeFalsy": undefined
}
- Description: Configures the intensity scale used to map entry values to colors.
scaleStart/scaleEnd: The minimum/maximum values of the intensity scale. Useful for a custom range, e.g. tracking reading time only between 30 minutes and 2 hours.defaultIntensity: Intensity assigned to entries that don't specify one.showOutOfRange: Whether entries outsidescaleStart/scaleEndare still shown (clamped) or hidden.excludeFalsy: Whentrue, entries with falsy intensity (0,undefined,null,false) are excluded from the heatmap and don't break streaks.
- Example: intensityConfig
Migrating from
defaultEntryIntensity/intensityScaleStart/intensityScaleEnd: these top-level parameters are removed as of the API described here. Old codeblocks using them keep working (they're folded intointensityConfigautomatically), but new heatmaps should useintensityConfigdirectly.
basePath
- Type:
string - Default:
undefined - Description: Base folder used to collect entries. If set, the plugin will propose creating new files in this folder when clicking on empty heatmap boxes.
- Example: basePath
separateMonths
- Type:
boolean - Default:
true - Description: Determines whether months should be visually separated within the heatmap layout.
- Example: separateMonths
disableFileCreation
- Type:
boolean - Default:
false - Description: When
true, clicking an empty heatmap box will not offer to create a new file. - Example: disableFileCreation
insights
- Type:
array - Default:
[] - Description: Powerful property for calculating and displaying your own insights in
Statistics. - Example: insights
layout
- Type:
"default" | "monthly" - Default:
"default" - Description: Controls the heatmap grid arrangement.
"default"renders the traditional GitHub-style week-column grid."monthly"renders one row per month with days 1–31 as columns, providing a compact calendar-style view. - Example: layout
Date range: monthsToShow, daysToShow, startDate/endDate
These four parameters all narrow which dates the heatmap displays instead of the full year. Only one wins when several are set — they're resolved in this order (highest priority first):
monthsToShow(number, defaultundefined) — current month plus the N previous months.monthsToShow: 3displays 4 rows (current month + 3 prior). Best used withlayout: "monthly".daysToShow(number, defaultundefined) — the last N days ending today.startDate+endDate(string, formatYYYY-MM-DD, defaultundefined) — an explicit range. Both must be set, andstartDatemust not be afterendDate.
If none of these are set, the heatmap falls back to showing the full year. This precedence is implemented once, in resolveDateRange — that function's doc comment is the source of truth if this section and the code ever disagree.
- Example: Date Range Parameters
To be used with Obsidian Dataview, but could be used standalone or with other plugins as well (if you know some javascript).
📦 Plugin Features
1. Easy switch between years. Render a dynamic heatmap for the selected year, displaying data intensity for each day.
Easily switch between years using left and right navigation arrows, allowing you to explore data across multiple years effortlessly.
2. Customizable Colors and Intensity. Define your own color schemes and intensity ranges to match your data's theme.
You have lots of options for defining colors:
- Create your own palette in plugin settings (or use default one)
- Use `customColors` to set your set of colors for specific plugin
- Use `customColor` for specific entry
3. User-Defined Insights. This feature allows you to analyze data in ways that matter most to you.
Customize insights such as:
- The most productive day
- The longest streak without breaks
- The most active month
- Your average daily intensity
Check this file for more information Insights
4. Monthly Separation Option. Choose whether to separate months visually within the heatmap for better clarity and structure.
5. Localization. Plugin supports 9 languages: English, German, Russian, Chinese, Hindi, Spanish, French, Portuguese, and Polish.
See src/localization/locales for the current list, and docs/add-new-language.md if you'd like to contribute a translation.
6. Statistics View. View your progress with an integrated statistics panel.
7. Display Week Numbers. View week numbers alongside the heatmap for better time tracking.
8. Insert Heatmap Tracker. Easily add a heatmap tracker to your notes using a simple command.
9. Customizable Font. Use your favorite font with this plugin.
Additionally, you can use HTML to further customize the plugin's appearance.
10. Monthly Layout. Display your heatmap as a compact calendar with one row per month.
Set layout: "monthly" to switch from the default GitHub-style grid to a calendar-style view with days 1–31 as columns. Combine with monthsToShow to display only recent months.
Roadmap
📍 Check out the Roadmap to see what's planned for the future!
Development (Windows/Mac):
📐 New to the codebase? ARCHITECTURE.md maps how data flows from a codeblock/dataviewjs script through to the rendered heatmap.
npm run dev - will start an automatic TS to JS transpiler and automatically copy the generated JS/CSS/manifest files to the example vault when modified (Remember to run npm install first).
After the files have been transpiled, the hot-reload plugin (https://github.com/pjeby/hot-reload) then reloads Obsidian automatically.
Hot-reload is installed in the example vault by default. its used to avoid restarting obsidian after every change to code.
(remember to add an empty .hotreload file to "EXAMPLE_VAULT/.obsidian/plugins/heatmap-tracker/" if not already present, as this tells hot-reload to watch for changes)
npm run build generates the files ready for distribution.
Tip: ctrl-shift-i opens the devtools inside Obsidian.
