Training for a marathon requires more than just logging miles; it requires training at the right intensities to build aerobic capacity without overtraining. This calculator uses the Karvonen Method, which is widely considered more accurate for athletes than standard formulas because it accounts for your Resting Heart Rate (RHR).
Why Use Heart Rate Training?
Heart rate training allows runners to monitor their effort objectively. External factors like heat, humidity, dehydration, and elevation can make a standard pace feel much harder. By sticking to heart rate zones, you ensure you are staying within the appropriate physiological system regardless of the conditions.
Understanding the Zones
Zone 1 (Recovery / 50-60%): Used for warm-ups, cool-downs, and active recovery runs. This promotes blood flow and helps clear lactate.
Zone 2 (Aerobic Base / 60-70%): The "bread and butter" of marathon training. Running here builds mitochondrial density and teaches your body to burn fat as fuel.
Zone 3 (Aerobic Power / 70-80%): Often called the "grey zone." It is harder than easy running but not hard enough to be a threshold workout. However, for marathoners, the upper end of this zone often mimics race pace.
Zone 4 (Threshold / 80-90%): Your lactate threshold. Training here improves your body's ability to clear lactate at faster speeds.
Zone 5 (VO2 Max / 90-100%): Maximum effort sprints or intervals. Less common in marathon builds but useful for speed and leg turnover.
The "Marathon Zone"
For most recreational runners, the optimal marathon race strategy involves running primarily in the high-aerobic zone. This typically falls between 75% and 85% of your Heart Rate Reserve. Starting the race conservatively at the lower end of this range allows you to conserve glycogen stores for the final 10 kilometers, helping you avoid "hitting the wall."
How to Measure Resting Heart Rate
To get the most accurate results from this calculator:
Check your pulse immediately after waking up, before getting out of bed.
Count the beats for 60 seconds (or 30 seconds multiplied by 2).
Repeat this for 3 days and take the average.
function calculateZones() {
// 1. Get Inputs
var ageInput = document.getElementById('age').value;
var rhrInput = document.getElementById('restingHr').value;
var maxHrInput = document.getElementById('maxHr').value;
// 2. Validate basics
if (ageInput === "" || rhrInput === "") {
alert("Please enter both your Age and Resting Heart Rate.");
return;
}
var age = parseFloat(ageInput);
var rhr = parseFloat(rhrInput);
if (isNaN(age) || age 120) {
alert("Please enter a valid age.");
return;
}
if (isNaN(rhr) || rhr 200) {
alert("Please enter a valid resting heart rate.");
return;
}
// 3. Determine Max HR
var maxHr;
if (maxHrInput !== "" && !isNaN(parseFloat(maxHrInput))) {
maxHr = parseFloat(maxHrInput);
} else {
// Standard formula: 220 – Age
maxHr = 220 – age;
}
// Validate Max HR vs RHR
if (maxHr <= rhr) {
alert("Max Heart Rate must be higher than Resting Heart Rate. Please check your inputs.");
return;
}
// 4. Calculate HRR (Heart Rate Reserve)
var hrr = maxHr – rhr;
// 5. Update Summary Display
document.getElementById('resMaxHr').innerHTML = Math.round(maxHr) + " BPM";
document.getElementById('resHrr').innerHTML = Math.round(hrr) + " BPM";
// 6. Calculate Zones (Karvonen: TargetHR = ((maxHr – rhr) * %Intensity) + rhr)
// Helper function for calculation
function getZoneLimit(percentage) {
return Math.round((hrr * percentage) + rhr);
}
// Define Zones
var zones = [
{ name: "Zone 1 (Recovery)", minPct: 0.50, maxPct: 0.60, desc: "Warm up / Cool down" },
{ name: "Zone 2 (Aerobic)", minPct: 0.60, maxPct: 0.70, desc: "Base building / Fat burning" },
{ name: "Zone 3 (Tempo)", minPct: 0.70, maxPct: 0.80, desc: "Marathon prep / Steady state" },
{ name: "Zone 4 (Threshold)", minPct: 0.80, maxPct: 0.90, desc: "Lactate threshold" },
{ name: "Zone 5 (Maximum)", minPct: 0.90, maxPct: 1.00, desc: "VO2 Max / Sprints" }
];
// 7. Populate Table
var tbody = document.getElementById('zoneBody');
tbody.innerHTML = ""; // Clear previous
for (var i = 0; i < zones.length; i++) {
var z = zones[i];
var minBpm = getZoneLimit(z.minPct);
var maxBpm = getZoneLimit(z.maxPct);
var row = "