ernestdefoe / cadence
Cadence — see a member's rhythm, not just their volume. An activity map on every profile, with the day you click actually opening that day.
Fund package maintenance!
Requires
- php: ^8.3
- flarum/core: ^2.0
Requires (Dev)
- flarum/approval: ^2.0
- flarum/likes: ^2.0
- flarum/phpstan: ^2.0
- flarum/testing: ^2.0
- fof/best-answer: ^2.0
- fof/reactions: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
See a member's rhythm, not just their volume.
An activity map on every profile — and, if you want it, a compact trace beside every post and on user cards.
Press any square and it tells you what happened that day: 2 things on September 7, 2026 — 1 discussion started · 1 reply.
On a phone the map scrolls sideways inside the profile card, and it opens on the newest weeks:
Why not just copy GitHub's graph
Because GitHub's has six problems, and on a forum they matter more than they do on a code host.
- It counts one undifferentiated thing. Cadence separates discussions, replies, likes, reactions and best answers, and only offers the kinds a member actually has.
- Its scale is relative to each person's own busiest day, so someone with two posts all year and someone with two hundred produce similar-looking graphs. Cadence uses an absolute scale with a legend that states real numbers, so two profiles can be compared.
- It shows a fixed year. Someone who joined last month gets eleven months of grey, which reads as "inactive" when the truth is "new". Cadence starts at the join date.
- Colour is the only encoding. Cadence's levels differ in lightness as well as hue, and every square carries its real numbers for screen readers.
- It's decoration. Cadence's squares are buttons: press one and it tells you what happened that day.
- It can't tell you when someone is around. Cadence stores hourly, so a day is the reader's day rather than the server's.
Where it shows
| Placement | Default |
|---|---|
| The full map on member profiles | On |
| A 26-week sparkline beside every post | Off |
| A sparkline on user cards | Off |
🚨 The sparklines cost no extra requests. They read an attribute that rides along on the user the page had already loaded. A placement that appears once per post cannot fetch its own data — thirty posts would be thirty requests, which is how a shared host runs out of database connections and returns 500 for the whole forum rather than for the decoration that caused it.
Daily streaks
A flame for every member who comes back tomorrow.
A streak counts the days in a row a member shows up — in their timezone, not the server's — and turns it into a flame: after their name in posts, on their card, in the header and on a leaderboard where the next rival is always within reach.
- You choose what keeps it alight: contributing (posting, replying, liking or reacting) or simply visiting.
- It starts with your history. Other streak add-ons start every member at
zero; Cadence already holds your forum's activity, so the member who has
posted every day for two years gets a two-year flame on day one.

-
Tiers you design — days, colour, icon, glow — and seasonal flames for the dates you pick. Members with a birthday in Calendar get a golden flame.
-
A missed day is not the end. Milestones earn freezes that melt on their own to save a missed day, and a long flame that goes out leaves embers: post within 48 hours to bring half of it back.
-
The leaderboard: Current, All-time and the Hall of Flames, your rank, the member just ahead of you, and a community bonfire everyone feeds together.

-
Alerts members control: milestones, new tiers and records as alerts and a toast on the first page of the day, and an "at risk" email before a flame goes out.
-
Widgets for Bespoke and fof/forum-widgets-core: My flame, Top flames, the Bonfire.

-
Admin: an overview of the whole board and a flame sheet for every member — give freezes, set, revive or reset — with every change journaled.
🚨 It costs your forum one query per page, however many flames are on it. The flame rides along on users the page already loaded. The leaderboard, the profile tab and the header menu load their code only when opened.
For the "at risk" and "embers" alerts, Flarum's scheduler must be running
(* * * * * php flarum schedule:run). Without it, flames still go out on
time — only those two alerts wait.
Don't want streaks? Turn off Daily streaks at the top of Cadence's settings. That one switch removes everything (the flames, header button, Streaks link, leaderboard, profile tab, widgets, alerts and emails) and stops counting, while the activity map keeps working. Nothing is deleted, so turning it back on picks up where you left off.
Only activity the map's audience could see counts toward a streak, and in visit mode a member who hides their online status keeps their visit days to themselves.
Settings
Admin → Cadence:
Installation
composer require ernestdefoe/cadence php flarum cache:clear
Then enable Cadence in the admin. Enabling returns at once: building every member's activity map and flame from your forum's history happens in the background, a slice of members at a time —
- on your queue, if the forum has one (database, Redis/Horizon);
- otherwise every minute from the scheduler (
php flarum schedule:runin cron, as Flarum recommends); - and while the Cadence settings page is open, which shows the progress ("Building streaks: 42%"). Flames appear as they are built.
On a big forum, or to watch it finish, run it yourself:
php flarum cadence:rebuild-streaks
It shows a progress bar, resumes a rebuild that is pending or was interrupted
(stop it with Ctrl+C and run it again any time), and with --fresh starts again
from the first member. As a guide, 500,000 posts and 15,000 members take about
15 seconds on MySQL.
php flarum cadence:rebuild
Rebuilds the posts and likes buckets from your forum's own tables, then every flame. Safe to re-run.
Only activity a guest could see is counted — or, on a forum guests cannot open, activity an ordinary member could see. Posts in staff-only tags, private discussions and direct messages never reach a map, because the map is one set of counts shown to everyone who can open the profile. Change who can see a tag and run this again, so activity already counted follows the new permissions.
🚨 Reactions and best answers cannot be rebuilt — neither records when it happened, so those two accumulate from the day you install rather than backfilling. Posts and likes rebuild exactly.
Works alongside Calendar
ernestdefoe/calendar has a profile heatmap of its own. When Cadence is
installed and showing on profiles, Calendar stands its own one down, so nobody
ends up with two. Turn Cadence's profile placement off and Calendar's comes back.
How it stays cheap
Activity is rolled up into one row per member, per hour, per kind. A profile
view is an indexed range scan over that member's own rows — it never aggregates
over posts, which on a forum of any size is a table scan per profile visit.
Hourly rather than daily is what lets the day boundary be decided when the map is read, in the reader's timezone, instead of being baked into the data forever at the server's.
Requirements
- Flarum 2.0
- PHP 8.3+
Support
- Support forum: Cadence on ernestdefoe.online
- Flarum community: Cadence on discuss.flarum.org
- Bug reports: GitHub issues
Licence
MIT.







