Body weight is a terrible time series. On a single morning it can swing by a kilogram or more because of water, salt, glycogen and whatever is still in your gut. Yet the question everyone actually asks is a forecasting question: "At this rate, when do I hit my goal?"
I built a free goal-weight date calculator (Weight Forecast, part of Life Forecast) and had to pick how to turn noisy weigh-ins into a projected date. Here are the three standard options, with code, a worked example, and why I ended up with the boring one.
The data
Fourteen mornings of weigh-ins (kg), one per day:
const w = [85.0, 85.6, 84.7, 84.9, 85.3, 84.4, 84.6,
84.1, 84.8, 84.0, 83.9, 84.3, 83.6, 83.8];
Day-to-day the series jumps by up to 0.9 kg. The direction is clearly down, but by how much per week?
Option 1: 7-day simple moving average
const sma = (xs, k = 7) =>
xs.map((_, i) => {
const win = xs.slice(Math.max(0, i - k + 1), i + 1);
return win.reduce((a, b) => a + b, 0) / win.length;
});
Last value: 84.07 kg. Easy to explain ("your average this week"). Downsides: every point in the window counts equally, a value drops off a cliff when it leaves the window, and the average sits about 3 days behind today.
Option 2: exponentially smoothed moving average (The Hacker's Diet)
John Walker's The Hacker's Diet popularized this for weight tracking: each day the trend moves 10% of the way toward today's weight.
const ema = (xs, alpha = 0.1) =>
xs.reduce((acc, x, i) =>
i === 0 ? [x] : [...acc, acc[i - 1] + alpha * (x - acc[i - 1])], []);
Last value: 84.46 kg. Very smooth and a single outlier barely moves it. The cost is lag: with α = 0.1 the trend responds slowly, so after two weeks of steady loss it still reads ~0.7 kg above where the data is now. It's great for "am I still on track?", less great for "what date do I hit 80 kg?" because the smoothed line, not the data, sets the slope.
Option 3: least-squares linear regression
Fit weight = a + b·day through the window, then extend the line to the goal.
function fit(xs) {
const n = xs.length;
const mx = (n - 1) / 2;
const my = xs.reduce((a, b) => a + b, 0) / n;
let num = 0, den = 0;
xs.forEach((y, x) => { num += (x - mx) * (y - my); den += (x - mx) ** 2; });
const b = num / den; // kg per day
return { a: my - b * mx, b };
}
const { a, b } = fit(w);
const today = w.length - 1;
const goal = 80;
const daysToGoal = b < 0 ? (goal - a) / b - today : null; // no date if not trending down
Result: slope −0.119 kg/day ≈ −0.83 kg/week, fitted value today 83.73 kg, goal of 80 kg reached in ~31 days on the straight line.
Regression uses every point in the window, doesn't lag the way an EMA does, and gives you the two things a forecast needs directly: a rate and an intercept. Its weaknesses are real too: one wild outlier tilts the line more than it would tilt an EMA, and it assumes the rate is constant, which it isn't. Loss usually slows as weight drops.
Side by side
| 7-day SMA | EMA (α = 0.1) | Linear regression | |
|---|---|---|---|
| Reading "today" | 84.07 | 84.46 | 83.73 |
| Lag | ~3 days | large | low |
| Outlier sensitivity | medium | low | medium–high |
| Gives a rate directly | no | no (need slope of trend) | yes |
| Handles missing days | needs care | needs care | just skip them |
What I shipped, and the guardrails
Life Forecast fits an ordinary least-squares line through your weigh-ins from the last 30 days (one per calendar day, morning preferred), needs at least 3 weigh-ins, and applies no extra smoothing. Days you didn't log are just absent from the regression, which is the main practical reason I chose it over moving averages. The full method and its limits are written up here.
Product rules I'd recommend to anyone building something similar:
- Call it projected, never exact. A straight line through 30 days of water-weight noise is an estimate. The UI says so under the chart.
- Show no date when the slope points the wrong way instead of inventing one.
-
Round at the edges, not in the middle. I had a bug where a kg↔lb switch passed
1.0999999999999943through a URL. Convert from the last value the user typed, and round only for display and transport.
Try it / steal it
The calculator is free with no sign-up needed to get a projected date: goal weight date calculator. The app adds Telegram reminders and Garmin scale sync (through Garmin), has no ads, and exposes an MCP server if you want to query your own data from an AI assistant.
The plain-English version of this post, without code, is on the Life Forecast blog: Weight Trend Line Explained.
How would you model this? A robust regression (Theil–Sen, Huber) is the obvious next step, and I'd love to hear from anyone who's tried it on real scale data.
Top comments (0)