A GitHub-style activity heatmap for Obsidian. Renders a 53×7 grid of the last 365 days in a sidebar pane, so you can see your writing cadence at a glance — then click any day to see the exact files you touched.
| Green | Heat | Sunset |
|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Three of the built-in palettes, in light and dark themes. Auto (theme accent) and Custom (any hex) round out the five options.
- Sidebar pane — lives in the right sidebar, collapses like any other panel.
- Configurable window — 90 / 180 / 365 day heatmap; today anchored bottom-right, today's cell always outlined.
- Quantile-based colors — buckets adapt to your vault size; no hardcoded thresholds.
- Five color palettes — Auto (theme accent), Green (GitHub-style), Heat (orange → red), Sunset (gold → indigo), or Custom hex.
- Recent-30 sparkline — compact bar chart above the legend showing the last 30 days, independent of how far you've scrolled the main grid.
- Detail panel — click any day and the panel below lists the files for that day, each clickable, each ⌘-hover previewable.
- Folder / tag filters — exclude noisy folders (e.g.
Archive) or restrict to files carrying specific tags. - Streak milestones — lucide 🔥 flame icons at 7 / 30 / 100 days, plus a 🏆 trophy at the 1-year mark. Click the trophy to see the year-1 date.
- Lifetime best — remembers your longest-ever streak so you can chase it after a break; flashes gold when you tie or beat it.
- Visibility toggles — turn the sparkline and streak counter on or off independently.
- Style Settings support — exposes the flame and trophy colors as editable variables when the Style Settings plugin is installed.
- Localized — all user-facing strings route through
src/i18n/with English shipping today and community-contributed locales welcome. - Jump to today — command palette entry, plus a "Today →" button in the detail header when you've scrolled away.
- Keyboard navigation — Tab into the grid, then ←/→ = ±week, ↑/↓ = ±day; the detail panel updates as you move.
- Apple-style scroll bounce — mass-spring-damper physics tuned to SwiftUI's default feel (
response=0.55s,dampingFraction=0.825). - Theme-aware — colors follow your Obsidian theme and re-render on theme switch.
- Auto-updates — edits, creates, deletes, and renames re-render within ~200ms (debounced).
- Mobile-compatible — no Node / Electron APIs;
isDesktopOnly: false. - Reduced-motion friendly — every animation and the scroll bounce disable when the OS has Reduce Motion enabled.
Install from Settings → Community plugins → Browse → search "Vault Pulse".
- Download
main.js,manifest.json, andstyles.cssfrom the latest release. - Copy them into
<YourVault>/.obsidian/plugins/vault-pulse/(create the folder if it doesn't exist). - In Obsidian: Settings → Community plugins, enable Vault Pulse.
git clone https://github.com/stefmf/vault-pulse.git
cd vault-pulse
npm install
npm run buildCopy main.js, manifest.json, and styles.css into <YourVault>/.obsidian/plugins/vault-pulse/, or use npm run seed (see Development) which sets up a throwaway vault with the plugin pre-installed.
Open the heatmap via the grid ribbon icon (left sidebar) or the command palette entry Vault Pulse: Open heatmap.
- Hover a cell → tooltip:
Mon DD · N files. - Click a cell → detail panel shows the file list for that day; click a file row to open it.
- ⌘-hover (macOS) / Ctrl-hover (Windows/Linux) a file row → Obsidian's native note preview popover.
- Tab into the grid, then arrow keys to move: ←/→ = prev/next week, ↑/↓ = prev/next day.
- Esc or click outside the pane closes nothing — the detail panel is always visible.
- Run Vault Pulse: Jump to today (⌘P) or click the Today → button in the detail header to scroll back and re-select today.
| Setting | Options | Notes |
|---|---|---|
| Activity source | Combined / Modified / Created | Default: Combined. A file counts for a day if either created OR updated matches that day. |
| Color palette | Auto · Green · Heat · Sunset · Custom hex | Auto follows --interactive-accent. Named palettes use discrete hex values tuned for both light and dark themes. |
| Custom hex color | #RRGGBB |
Only visible when palette is "Custom". |
| Window length | 90 / 180 / 365 days | Default: 365. Shorter windows narrow the grid but bring recent activity into sharper view. |
| Week starts on | Sunday / Monday | Default: Sunday (matches GitHub). |
| Exclude folders | Comma-separated path prefixes | e.g. Archive, _templates. Prefix matching: Archive excludes Archive/... but not My-Archive/.... Empty = include all. |
| Include tags | Comma-separated tag names (leading # optional) |
e.g. project, journal. If set, files must have AT LEAST ONE of these tags. Empty = include all. Reads both frontmatter tags: and inline #tags. |
| Show sparkline | On / Off | Default: On. Toggles the 30-day bar chart above the legend. |
| Show streak counter | On / Off | Default: On. Toggles the flame / trophy milestone row in the detail header. |
| Show mini stats | On / Off | Default: On. Toggles the week / month / year file counts under the streak row. |
| Show status bar widget | On / Off | Default: On. Toggles the streak + today's-files summary in Obsidian's status bar. |
Vault Pulse tracks consecutive active days across your whole vault (not capped to the window) and rewards consistency with a small milestone ladder:
| Day | Milestone |
|---|---|
| 7 | 🔥 first flame |
| 30 | 🔥🔥 second flame |
| 100 | 🔥🔥🔥 third flame |
| 365 | 🔥🔥🔥 🏆 trophy (grand celebration — gold confetti + bigger ripple) |
Additional:
- Lifetime best —
· best Nrenders next to the current streak and persists across broken streaks. Flashes gold on ties / new records. - Window pager —
◀ [range] ▶above the heatmap steps backward through history one window at a time. Disabled when no older activity remains or when the grid ends on today. Reusing the existing Today → button returns you home in one click and resets the offset. - Status bar widget —
🔥 N · [file] Min Obsidian's status bar shows the current streak (once it's ≥ 7 days) and today's file count. Clicking it opens the pane. - Mini stats — under the streak row,
N WEEK · M MONTH · K YEARgives at-a-glance velocity (last 7 days / calendar month / calendar year). - Tier crossings fire a short ripple + CSS confetti burst; the year-trophy crossover uses a gold-weighted palette with extra pieces and a larger ripple. Everything respects
prefers-reduced-motion.
Vault Pulse reads created and updated from each note's frontmatter when present. Supported formats: ISO 8601 (2026-04-13, 2026-04-13T12:34:56), SQL (2026-04-13 12:34:56), JS Date objects, and Unix millisecond epochs. Missing frontmatter falls back to the file's filesystem stat (ctime / mtime).
Vault Pulse runs entirely on your device. It does not make network requests, send telemetry, or transmit your notes anywhere.
To render the heatmap and detail panel, the plugin reads:
- All markdown file paths in your vault via
app.vault.getMarkdownFiles()— required to discover which days had writing activity. - Each note's frontmatter (
created,updated, andtags/tag) via Obsidian's metadata cache — used for accurate date detection and the optional Include tags filter. - Each note's inline tags via the metadata cache — also used for the tag filter.
- Filesystem
ctime/mtimeas fallbacks when frontmatter dates are absent.
The plugin does not read note bodies. Files inside folders listed under Exclude folders are skipped entirely.
All state — settings, lifetime-best streak, recent windows — is stored locally in <vault>/.obsidian/plugins/vault-pulse/data.json.
Activity levels use quantile bucketing over the non-zero days in the 365-day window:
| Level | Condition |
|---|---|
| 0 | count = 0 (theme border color) |
| 1 | count ≤ p25 |
| 2 | p25 < count ≤ p50 |
| 3 | p50 < count ≤ p75 |
| 4 | count > p75 |
This adapts automatically: a light vault with ~1 note/day and a heavy vault with dozens/day both end up with meaningful bucketing. For the Auto and Custom palettes the colors are computed as alpha-blends of your base color, so empty days show through the theme background. For Green, Heat, and Sunset, the four levels are pre-tuned discrete hex values.
- Local development, test vault setup, seeding flags, lint/test/build commands — see
tests/README.md. - Module responsibilities, file-level conventions — see
AGENTS.md. - Translations — copy
src/i18n/en.jsonto<lang>.json, translate values (don't rename keys or change{placeholder}tokens), register insrc/i18n/index.ts, open a PR. Obsidian'smoment.locale()auto-picks the right bundle.
MIT © 2026 Stefmf





