The core loop in LairsFlow has always been the same three steps: solve a question, log what happened, repeat. It works, but it gave you nothing back in the moment. You would submit a log entry and the app would quietly move on to the next question, no different from filling out a form. We wanted the loop itself to feel like progress, not just produce data you look at later on the dashboard.
What we added
Four pieces, all scoped to the practice loop:
- XP, difficulty weighted. Solving a hard question earns more than an easy one, and a live correct streak adds a small bonus on top so consecutive right answers are worth more than the same number of scattered ones.
- A question streak, the number of correct answers in a row right now, and the best you have ever managed.
- A daily goal, set once in your profile as either a question count or a study-time target per day.
- A goal streak, how many days in a row you actually hit that target.
LairsFlow already had two other streak concepts before this: one in the notifications system that counts consecutive days with any question logged at all, and one on the daily reports screen that tracks whether you hit a planned question count. We deliberately did not merge the new goal streak into either of them. Changing what an existing number means, quietly, is a worse experience than having three streaks with slightly different definitions. The new one is additive, not a replacement.
Numbers, not a ledger
The tempting way to build this is a running counter: add XP here, increment a streak there, save it. We did not do that. Every number the feature shows, XP total, current streak, best streak, goal streak, is computed live from your existing question log and study sessions, the same way the dashboard already derives your accuracy and the Subjects page already derives chapter completion. Nothing new is written to storage except the daily goal setting itself.
That choice pays for itself immediately. Undo a logged question and your XP and streak just update, because they were never anything but a function of the log to begin with. Pull fresh data from another device and the numbers are correct without a merge step, because there was nothing to merge. We have shipped enough features with a separate mutable counter to know that counter always drifts eventually. This one cannot.
A level curve that front-loads the win
Ten levels, a fixed XP requirement for each: 100, 300, 600, 1000, 1500, 2100, 2800, 3600, 4500, and level 10 is the ceiling. The gap between levels grows by a flat 100 XP each time, so the first few levels come quickly and the climb gets longer as you go, the same shape as most things worth sticking with actually feel.
It took three tries to stop looking like a toy
The first version put the progress bar inside the timer's mode switcher, swapped in for the Practice/Stopwatch/Countdown toggle while a session was running. It looked fine until you actually used it: the moment you finished a question and the log screen took over, the bar was gone, because it lived inside a screen that gets hidden. Progress that disappears the instant you go to record progress is not a good look.
So we pulled it out into its own element and pinned it to the bottom of the screen so it would survive every screen switch. And then, left to our own devices with a fresh canvas, we overdid it. Pastel gradients, glass blur, glowing rings, a bouncy animation every few seconds, particles floating up on every question. It read exactly like what it was: a UI element invented on the spot with no relationship to the rest of the app.
LairsFlow has an actual design law for this, written down in DESIGN.md before any of this feature existed: monochrome ink over a soft grey fill, color spent only when it means something, motion that confirms a state change and nothing longer than three tenths of a second. We had ignored all three rules building something "fun," and the fun did not survive contact with a product you are supposed to look at for hours a day. The fix was going back and actually following the spec. The pastel palette became the same ink and accent tokens every button and badge in the app already uses. Color still appears exactly once, when a streak is actually alive, and it reuses --timer-done, a token already defined a few lines above it in the same file, rather than inventing a new green. Every glow, flicker, and continuous animation is gone. What is left is a short 0.2 second fill on the bar and a brief scale bump when a number changes, both of which confirm something happened instead of decorating the screen for no reason.
One piece of motion survives on purpose: a small hop every seven seconds when the pill is idle, so it does not feel dead when nothing else is moving. It switches off completely the instant a timer starts running, because a bouncing UI element next to a question you are trying to focus on is not a feature, it is a distraction we would have shipped by accident.
The last fix was positioning. Pinning the pill to the browser viewport meant it stayed dead center even when you opened the sidebar and the actual content area got narrower, which put it visibly off-center from everything around it. It is now anchored to the content column itself instead of the viewport, so it re-centers the instant the sidebar opens or closes, with no JavaScript watching for the resize.
Wired into the Advisor
The Advisor already had access to your study time, mock scores, and lecture hours through its context system. Gamification joined that list as its own category, on by default: total XP, level, current and best question streak, and your daily goal progress, all pulled from the same live functions the pill itself uses, so if you ask "how is my streak going" the Advisor answers with the real number, not a guess. Like every other category, you can turn it off from the context menu if you would rather it not see this particular slice of your data.
None of this changes what LairsFlow measures. It is the same question log, the same difficulty ratings, the same daily target you would have set anyway. It just gives that data a pulse in the one place you actually watch it happen, the moment right after you solve something.