JustGains vs Fitbod
I pulled Fitbod’s workout generator out of the shipped app to see what actually decides your session, then did the same to Gravl. Three engines, three philosophies, and an honest comparison with how JustGains answers the same question.
The generator everyone means when they say "the Fitbod algorithm" is the one that runs when Fitbod’s servers don’t answer. That’s the finding that reframed this whole project for me, and it took a weekend of decompiling to be sure of it.
Obvious disclosure before you read another word: I work on JustGains, which is one of the three apps in this comparison. Read my conclusions with that in mind. What I can offer instead of neutrality is checkable work. The Fitbod and Gravl numbers below are extracted constants and named functions from shipped app packages, not marketing claims, and I’ve given each rival its own section on where it beats us.
One thing I want to say before the teardown, because it’s true and it’s easy to lose in three thousand words of arithmetic. Fitbod solved a real problem, and it solved it early. Open the app, get a session built around the muscles that have recovered and the kit you actually have, train it, close the app. For a lot of people that removed the single biggest obstacle to lifting, which was never effort. It was deciding what to do. If Fitbod is why you train three times a week, that’s worth more than any feature list on this page.
The catch that reframes everything: Optim is the fallback
Start with the finding that changes how you should read the rest of this article. In the current app, the local generator isn’t the primary generator. When you tap generate, WorkoutController asks a source determiner which engine to use, and the normal answer is a remote one: algo_direct_client, algo_llamabod, or foundational_model. Those are HTTP endpoints on Fitbod’s servers. The request and response shapes ship inside the app, so I can see exactly what gets sent. The thing on the other end doesn’t ship, and neither do its weights.
Optim runs when that call fails. There’s a handler, onRemoteWorkoutRefreshFailed(), that converts the same workout parameters into local params and forces on-device generation instead. So the honest framing is this: the part of Fitbod you can actually inspect is the part it falls back to. Anyone claiming to have reverse-engineered Fitbod’s live algorithm is overclaiming, and I’m not going to do that.

That said, Optim is worth reading closely, for three reasons. It’s a complete, shipping workout generator that has to produce a credible session with no network. It encodes Fitbod’s house opinions about training in hard constants, which is far more revealing than prose. And a fallback that a company is willing to put in front of a paying user tells you a great deal about what that company thinks a good workout is.
Inside the generator you can read
Optim’s entry point runs fourteen steps in a fixed order. Before any of it, a handler assembles the inputs: your generation history, your workout config and any one-session overrides, your selected cardio, your gym equipment, manual recovery adjustments, custom weights, the active training block and focus exercises, and optionally a forced split or a set of muscles you picked by hand. It then snapshots the exercise catalogs and caches the whole thing until your exercise data changes.

Time is the first constraint, not the last
This is the part that surprised me most. Before Optim considers a single exercise, it converts your session length into a slot budget, and it does it with a fixed price list. Each slot costs minutes according to its position in the workout, not according to what ends up in it. The first movement of the day is budgeted 15 minutes whether it turns out to be a heavy squat or a lateral raise.
| Slot | Base minutes | Effective minimum |
|---|---|---|
| Position tier 1 (first) | 15 | 11 |
| Position tier 2 | 12 | 8 |
| Position tier 3 | 10 | 6 |
| Position tier 4 and beyond | 7 | 3 |
| Core | 5 | 5 |
Your requested duration is trimmed before it’s spent: 4 minutes come off below 31 minutes, 2 minutes come off from 31 through 45, and nothing comes off above 45. Then the ladder runs. Every workout starts at 2 non-core plus 2 core slots. A third non-core slot appears once the duration clears tier 1 plus tier 2 plus core. A fourth appears once it clears tier 1 plus tier 2 plus tier 3 plus core. Past the next threshold, extra slots are added one tier-4 block at a time. Below that final threshold, core is quietly cut from two slots to one. The Muscle Tone goal adds 2 non-core and 1 core on top of whatever the ladder produced.

Who’s allowed in the workout at all
Next comes a hard eligibility pass, and it’s a rejection list rather than a preference list. An exercise is thrown out entirely if any one of these is true:
- Its difficulty level sits above your configured experience level.
- You picked the Olympic Weightlifting goal and the exercise carries an Olympic rating of 1.
- The record is an alias, or it’s cardio, distance, or mobility work (those get handled by separate generators later).
- Bodyweight-only mode is on and the exercise isn’t bodyweight, or it is bodyweight but still lists required equipment.
- Any piece of required equipment is missing from your gym profile.
- You manually selected muscles and this exercise’s primary muscle isn’t among them.
- You rated the exercise exclude.
- It’s already in the workout.
- Its primary muscle conflicts with the upper/lower or push/pull/legs split that’s been chosen or forced.
Two details in there are quietly interesting. Core exercises skip the split check entirely, which is why you can get abs on a push day. And accessory-muscle exercises are allowed, but no two exercises whose primary muscle is an accessory can coexist in the same session, which is the rule stopping your workout from becoming four flavours of arm isolation.
The score: freshness dominates everything else
Whatever survives eligibility gets a score, and the score is refreshingly plain: six terms, added together, no weights. That simplicity makes the relative sizes of the terms the whole story.
| Term | What it measures | Rough range |
|---|---|---|
| Muscle usage | How recovered the primary muscle is, as (1 - usage) * 4 | 0 to 4 |
| History | How long since you last did this movement (or an alias of it) | 0 to about 2 |
| Catalog rating | Fitbod’s own quality rating for the exercise under your goal, plus an at-home equipment boost | capped at 1 |
| Primary muscle utility | A per-muscle-group weighting from the catalog | small |
| Manual add and remove | Whether you have been adding or deleting this lift by hand | -0.6 to +0.6 |
| Your rating | Liked or disliked | -0.5 or +0.5 |
Read that table again with the ranges in mind. Muscle freshness can contribute up to 4 points. Telling Fitbod you love or hate an exercise moves the score by 0.5. That isn’t a bug, it’s the product thesis expressed as arithmetic: Fitbod is a recovery-driven generator first and a preference-driven one a distant second. It also explains a common complaint. If you like an exercise but its muscle is fatigued, your like isn’t going to rescue it, and if you dislike an exercise whose muscle is fresh, you’ll keep seeing it.
The recency term has a shape worth knowing too. It uses 1 - 1/sqrt(days), which saturates at 90 days and rises steeply in the first week. Two days off a lift barely counts as rest. Ten days off nearly maxes it out.
The recovery model itself
Fitbod’s muscle recovery, the thing on the little body map, is a linear decay. Each past workout contributes max(1 - minutesSinceWorkout / recoveryMinutes, 0) toward a muscle’s current usage, and the recovery window depends on your experience level and goal.
| Setting | Recovery window |
|---|---|
| Beginner | 5.75 days |
| Intermediate and default | 6.0 days |
| Expert | 6.25 days |
| Strength or Powerlifting goal | add 0.25 days |
Per set group, a muscle takes 0.4 of an impact hit as a primary mover and 0.075 as a secondary, reduced to 0.25 and 0.05 for broad multi-muscle work. Position, bodyweight status, and a few special muscle IDs nudge those numbers. Manual recovery adjustments (dragging a muscle’s recovery slider) multiply the decayed usage by max(1 - recoveryPercentage, 0) and suppress older workouts for that muscle. It’s a sensible, legible model, and it’s the best of the three on this page: it’s the only one that scales with how much work you actually did *and* personalises the window. It’s also a longer window than most lifters assume, at nearly six days before a muscle is considered fully clear.
Sets and reps come from a table and a calendar
This one delighted me. Fitbod doesn’t compute your set and rep scheme. It looks it up. There are 21 hardcoded tables in the app, and the row is chosen by the day of the year, offset by the exercise’s position (0, 1, 2, 3, 4), with core using day plus 5. Same day, same position, same goal means the same scheme, every time. Hold onto that, because it’s the single fact that separates Fitbod’s variety model from Gravl’s.
| Goal | Slot 1 | Slot 2 | Slot 3 | Slot 4+ |
|---|---|---|---|---|
| Powerlifting | power T1 | strength T1 | strength T2 | strength T3 |
| Olympic | oly T1 | oly T1 | strength T1 | strength T4 |
| Bodybuilding | body T1 | body T2 | body T3 | body T4 |
| Strength / default | strength T1 | strength T2 | strength T3 | strength T4 |
| Muscle Tone | strength T3 | strength T4 | strength T3 | strength T4 |
| General Fitness | general early | general early | general late | general late |
The tables themselves are a fair snapshot of mainstream programming. The strength tier 1 table cycles through 5x5, 4x5, 3x6, 4x6, 4x4 and similar. Bodybuilding tier 3 leans on 3x15 and 3x12. The Olympic tier 1 table is the outlier and looks exactly like you’d hope: 10x1, 8x3, 12x2, 6x4. Overrides sit on top for special cases, including a fixed 3x7 for one specific exercise ID, 3x8 for bands, 3x60 seconds for timed work (with L Sit and Side Bridge cut to 30), and a whole experience-by-reps-scale grid for bodyweight movements.
Load comes from your logged history through an Epley-style estimate, ((1 + sets * 0.018) * weight) / (1.0278 - min(reps, 20) * 0.0278), then the scheme weight is the same formula inverted. The history handling is more careful than the rest, and it’s Fitbod’s biggest structural advantage over Gravl: outliers beyond a z-score of 3 are replaced with the median, but only once you have 15 or more samples; data is collapsed to daily maxima; gaps longer than 2 days are linearly interpolated; and exponential smoothing runs at alpha 0.5. After 28 days away from a lift your estimated max decays, capped at losing one third by day 180. Max-effort days aren’t adaptive at all: they trigger when (seed + dayOfYear) % 4 == 0 and the last session wasn’t one.
What that design buys, and what it costs
Credit where it’s due. This is a well-built engine. It’s fully deterministic, so it can’t hallucinate an exercise or return an empty workout. It runs offline in milliseconds. It respects your equipment absolutely rather than probabilistically. The scoring is simple enough that its behaviour is predictable, which matters more in a product than cleverness does. And the exercise catalog behind it is substantial: the app ships 1,406 exercises, 1,190 of them with written instructions.
The costs are structural rather than sloppy. A fixed slot-cost table means the shape of your session is decided by arithmetic on the clock before training logic gets a say. Day-of-year scheme selection means your set and rep scheme is a rotation, not a progression; on a given day you get the row the calendar landed on. A six-day recovery window plus a freshness term worth four points means the generator will steer you toward novelty, which is excellent for general fitness and directly at odds with running the same four lifts hard for eight weeks. And your stated preferences are worth half a point against all of that.
None of this makes Fitbod a bad app. It makes it a specific one. It’s built for the lifter who wants a good session today without owning a plan, and it’s very good at exactly that.
Now the third data point: Gravl generates on your phone too, and it’s nothing like this
I did the same job on Gravl (formerly Gains AI), and I have to admit a mistake up front: my first pass concluded Gravl generated your session on a server, because I went looking for Fitbod’s vocabulary in a codebase that doesn’t use it and found nothing. That was wrong. Gravl ships a complete daily generator in its app bundle, and the correction turned out to make the comparison much more interesting. The full story is in JustGains vs Gravl; what matters here is that "both run on-device" is where the similarity ends.

Fitbod decides the shape of your session with a clock: slots priced in minutes by position, thresholds that add a slot at a time, and a position tier that then picks which of the 21 scheme tables you get. Gravl decides it with anatomy. getRankedExercises scores the catalog that’s already synced to your phone, then a placer (getReservableSubmuscles, planExerciseReservations, findPlaceableReservation, applyUnplacedReservations) reserves slots per submuscle and fills them by score priority, and getEffectiveSupersets groups the result. One engine budgets minutes. The other budgets muscle heads.
The sharper difference is variety. Fitbod’s is deterministic, seeded by the calendar. Gravl’s runs through getRandomWeightedElements, which calls Math.random and is not seeded by anything, so two regenerations of the same Tuesday genuinely differ. And Gravl does one thing on load that Fitbod flatly doesn’t: it discounts the target 1RM by the exercise’s position within its muscle group in today’s session, 1.00 then 0.93 then 0.88 then 0.84, and divides that discount back out when it learns from the result. Fitbod changes your rep scheme by position but prescribes the fourth chest movement at the same load as the first. On that one, Gravl is right.
Fitbod wins the reverse trade convincingly. Gravl has no exponential smoothing, no outlier rejection and no gap interpolation on your history, so one mis-logged set moves its estimate immediately. Its detraining floor is −8%, meaning a full year off still prescribes 92% of your old max, where Fitbod decays to a third off by day 180. And its recovery model is a 72-hour clock with no volume term and no personalisation at all. The two apps’ errors are almost perfectly complementary, which I did not expect going in.
JustGains: the same questions, answered differently
We have to answer the same three questions: what’s recovered, what should you train, and what exactly should today’s session be. We split them into three separate systems on purpose, because they have different failure modes and deserve different tools.
Recovery: a shorter window and three honest states
The shared recovery utility sorts your logged sets per muscle group into recent and older work. The fallback window is 2.5 days, considerably shorter than Fitbod’s six and shorter than Gravl’s three. Muscle groups with recent sets are marked fatigued, groups with only older sets are recovering, and groups with neither are recovered. Older sets still count toward the picture but at one third of their weight once recent work exists, so fresh fatigue always outranks last week’s.
The colour intensity on the body map is relative, not absolute: we total the weighted work across all groups and scale each one against the most-loaded group. That’s a deliberate choice about what the number is for. It answers "what have I been hammering lately" rather than pretending to measure tissue damage. Honest comparison: Fitbod’s model is more sophisticated than ours, because it personalises the window by experience and goal and weights primary versus secondary movers. Ours is volume-aware where Gravl’s isn’t, and it’s a planning signal rather than a generator input. The full write-up is in how JustGains calculates muscle fatigue.
Suggestions: deliberately not AI
The suggestion chips on your home screen come from a deterministic, rule-based engine reading a rolling 7-day window of completed workouts. It summarises each session (finish time, local day, duration, completed working sets, muscle bucket usage, session kind, focus, whether interval groups were used) and then tries to continue the pattern you already appear to be running. Push, pull and legs in your last three sessions predicts the next leg of that cycle. Upper and lower alternates. If no split is detectable, it falls back to ranking muscle buckets by freshness and neglect.
Generation: two passes, then deterministic cleanup
When you ask for a full workout, the generator runs two passes. Pass one asks a language model for candidate exercise slots given your prompt, your equipment, and your history. Those slots then drive an embedding search against the real JustGains exercise library, which returns ranked, genuine exercises rather than invented names. Pass two hands the model those ranked candidates and asks it to build the actual session: order, groups, sets, reps, targets.
Then a deterministic cleanup stage runs, and it’s the unglamorous part that makes the whole thing usable. Exercise codes are normalised and matched back against the search results, anything the model invented is dropped, duplicates are removed, group types are normalised, dumbbell weights are corrected, and non-dumbbell loads are rounded to real increments for your measurement system. If cleanup ends up with zero usable exercises, generation fails loudly instead of returning an empty workout, because an empty result flowing into an edit would wipe the workout you already had.
And the honest cost of doing it our way
Two of them, and neither is small. First, it needs a connection, where both Optim and Gravl’s generator will happily build you a session in a basement with no bars. Second, it’s the least predictable of the three: a model wrote it, so two requests for the same Tuesday differ, and the reason a particular movement appeared is less legible than "the day of the year picked row 14". The cleanup wall and the fail-loud behaviour are what we put behind it, and they stop the failure modes people actually fear (invented exercises, empty workouts) rather than making the thing deterministic. It isn’t deterministic, and I’m not going to claim it is.

| Fitbod | Gravl | JustGains | |
|---|---|---|---|
| Daily workout built | On your phone (Optim), behind three remote generators | On your phone, no server in the daily path | On our servers |
| Runtime | Kotlin and Java, Dagger, Room | React Native and Expo, Hermes bytecode | .NET service calling a language model |
| What ranks exercises | Six unweighted score terms, freshness up to 4 | getRankedExercises over the synced catalog | Embedding search over the real exercise library |
| How slots get filled | Position tiers priced in minutes (15 / 12 / 10 / 7) | Submuscle reservation, filled by score priority | Model drafts, search grounds, model assembles |
| Sets and reps from | 21 hardcoded tables, indexed by day of year | A scheme object emitted per exercise | Generated per session, then fully editable |
| What makes it vary | The calendar | Math.random, unseeded | The model, then deterministic cleanup |
| Same day, generated twice | Identical | Different | Different |
| No connection | Generates fine, that is what Optim is for | Generates fine from synced data | No generation. Logging and suggestions still work |
And then it becomes an ordinary workout
This is the structural difference that matters more than any constant in this article. What the JustGains generator produces isn’t a generated plan with a special status. It’s a normal workout, the same object you’d get by building one by hand. You can reorder it, swap movements, change the sets, save it as a routine, run it again next week unchanged, share it, or throw it away. Nothing regenerates it out from under you.



Fitbod vs JustGains, head to head
| Fitbod | JustGains | |
|---|---|---|
| Primary generator | Remote model, with the local Optim engine as fallback | Two-pass generation over your real exercise library, plus rule-based suggestions |
| Recovery window | About 6 days, adjusted by experience and goal | 2.5 days, with older work weighted at one third |
| Sets and reps | Looked up from 21 fixed tables, indexed by day of year | Generated per session, then fully editable, including rep ranges |
| Your preferences | Like or dislike worth 0.5 against a freshness term worth up to 4 | You edit the workout directly, so preference is absolute |
| Intra-session fatigue | Changes your rep scheme by position, not your load | Not modelled in the load prescription either |
| Following a fixed program | Works against the grain of a regenerating plan | First class: routines, multi-week programs, shared plans |
| Offline generation | Yes, that is exactly what Optim is for | Generation needs a connection; logging is fully offline |
| Beyond lifting | Lifting focused | GPS runs and walks, nutrition, goals, streaks, a social feed |
| Apple Watch | Yes | Yes: set logging, rest timer, heart rate zones, phone-free runs |
| Price | Subscription only after a short trial | Most features free on iOS, Android and web |
| Data import | No importer for other apps | Reads Fitbod, Hevy, Strong, StrengthLog and Caliber CSVs |
The replies I’d expect, answered
If I posted this anywhere with a comment section, these are the objections that would be at the top, so let me get to them first.
- "You work for one of these apps, this is an ad." Half right, which is why the disclosure is in the third paragraph rather than a footer. The defence isn’t my good character, it’s that the competitive claims are falsifiable: the constants are in packages you can pull yourself. An advert would not contain a paragraph explaining that Fitbod’s recovery model is better than ours and Gravl’s order discount is a mechanic we don’t have.
- "You reverse-engineered a fallback and called it the algorithm." That’s exactly the point of the second section, and it’s the reason it’s the second section.
Optimis the engine that ships. The primary path is remote and its weights are not in the package, so nobody outside Fitbod can tell you what it does, including me. - "Static analysis isn’t the running app." Correct, and worth saying plainly. I can tell you what code is in a package and what constants it holds. I can’t tell you which path your specific install takes on a given day, or whether a server flag is quietly routing you somewhere else.
- "Fine, but the algorithm isn’t the product." Genuinely agree, and it’s the last line of this article for a reason. Your consistency is the product. A teardown is a good way to understand why an app feels the way it does; it’s a bad reason on its own to switch apps you already train with.
Price, stated plainly
Fitbod has no permanent free tier. You get a short trial, typically about a week, and then it’s a subscription: roughly $15.99 per month or $95.99 per year as of mid-2026. Prices move and vary by region and platform, so treat those as a starting point and check fitbod.me for what you’d actually pay.
JustGains is free for most features on iOS, Android and the web, including AI workout generation, the full logger, GPS activities and nutrition tracking. The core logger and your own data will always stay free. I’m not going to pretend that’s charity: we’re newer, and a free tier is how a newer app earns a trial. But it does mean the comparison you can run costs you nothing.
One honest note on value. If Fitbod’s generation is genuinely what gets you into the gym, roughly $96 a year is cheap for that. The subscription only starts to sting when you’ve quietly stopped following the generated workout and are paying AI-coach prices for a logbook.
Where Fitbod is still the better pick
An article that concedes nothing is an advertisement, and I just spent a long technical section asking you to trust my work. So here are the cases where I’d tell you to stay with Fitbod, and I mean them:
- You want to make zero decisions, permanently. Fitbod is built end to end around that one promise and has years of refinement behind it. JustGains will happily generate for you, but the app assumes you might want the wheel, and that assumption shows in the interface.
- You train offline a lot.
Optimmeans Fitbod builds you a full session in a basement gym with no signal. JustGains generation needs a connection. Logging works offline and syncs later, but generation does not. (Gravl clears this bar too.) - You want the best recovery model of the three. Fitbod’s is the only one that both scales with the volume you actually did and personalises the window by experience level and goal. Ours is volume-aware but simpler; Gravl’s is a flat 72-hour clock.
- You want history handling that shrugs off a bad log. Z-score outlier rejection plus exponential smoothing at alpha 0.5 means one mis-typed weight doesn’t derail your estimates. Gravl has nothing equivalent, and ours is closer to Gravl’s than to Fitbod’s here.
- You’re a beginner who has never written a program. Fitbod’s constraints are guard rails. A generator that insists on a big compound in slot 1 and caps your accessory sprawl is doing you a favour in year one.
- You want a mature, single-purpose product. Fitbod does one thing and has polished it for years. JustGains is broader and newer, which means more surface area and, honestly, more rough edges.
- You already like the sessions. This is the real one. If you finish Fitbod workouts feeling like you trained well, no teardown on this page is a reason to switch. The algorithm isn’t the product. Your consistency is.
Bringing your Fitbod history over
If you do want to try both, don’t start from zero. Fitbod exports your full workout log as a CSV, which is genuinely to its credit and more than Gravl offers, and the JustGains importer reads that format directly. It takes a few minutes and doesn’t touch your Fitbod account.
- Export from Fitbod. Open Settings (the gear icon), choose Export Workout Data, and save the CSV somewhere you can reach it. If the menu has moved in a recent update, search Fitbod’s help center for "export".
- Open the importer. Go to the free importer on any device. It runs on the web, so a phone or a laptop both work.
- Upload the CSV. The format is detected automatically and you get a preview of every workout found before anything is written.
- Confirm. Each workout, exercise, set, rep, weight and note lands on the day you actually trained it.
Afterwards, the derived stats are rebuilt from the raw sets rather than imported blindly: PRs are re-detected, estimated 1RM curves are recalculated across your whole history, and your volume charts continue from your first Fitbod session instead of restarting. Run both apps side by side for a couple of weeks and judge with your own numbers on screen.
The honest bottom line
What I found inside Fitbod wasn’t a black box or a secret sauce. It was a careful, deterministic engine built on strong opinions: recovery matters more than preference, the clock decides the shape of the session, and the set and rep scheme comes from a table indexed by the date. Those opinions produce good workouts for the person the app is designed for. They also produce exactly the friction that the program-following lifter complains about, and now you can see why, in numbers rather than vibes.
Reading Gravl afterwards is what made the whole exercise worth it, because it proved there isn’t one obvious way to do this. Same problem, same phone, wildly different machine, and each of them right about something the other gets wrong. Ours is the third answer: ask a model, ground it in a real exercise library, check its work with code that can’t be creative, then hand you the result as an ordinary workout that belongs to you. That last part is the actual difference. Fitbod decides, you train, it decides again tomorrow, and that’s the product working as designed. We give you a draft and a set of controls. Add runs, food, goals and friends in the same app, at no cost, with your entire Fitbod history imported, and you can make that comparison yourself instead of taking anyone’s word for it. Including mine.
FAQ
Keep learning
JustGains vs Gravl
I disassembled Gravl’s shipped app to find out what actually builds your workout, got the architecture wrong the first time, and went back. Here is the corrected picture, the exact constants behind its weight prescriptions, and how three different generators answer the same Tuesday.
The best Fitbod alternatives in 2026
Fitbod subscription not earning its keep? Here is an honest, in-depth look at the best Fitbod alternatives, what each one does well, and how to take your training history with you.
How JustGains calculates muscle fatigue
JustGains estimates muscle freshness from recent sets, older sets, and recovery buckets.
How workout suggestions work
JustGains workout suggestions are deterministic, rule-based prompts from your recent training.
The best Hevy alternatives in 2026
Outgrowing Hevy or hitting the free-tier limits? Here is a deep, honest look at the best Hevy alternatives, what each one does well, what each one lacks, and how to move your entire history without re-logging a single set.
