Monster CR Calculator

ANALife Services AuthorityNational Calculator Authority›Monster CR Calculator

.calc-container { max-width: 640px; margin: 2rem 0; padding: 1.5rem; background: #fff; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); font-family: system-ui, -apple-system, sans-serif; } .calc-container h3 { font-family: Georgia, serif; font-size: 1.15rem; color: #1a1a1a; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--ac, #3d5a80); } .calc-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap; } .calc-row label { min-width: 160px; font-size: 0.9rem; color: #333; font-weight: 500; } .calc-row input[type="number"], .calc-row select { flex: 1; min-width: 120px; max-width: 200px; padding: 0.5rem 0.6rem; border: 1px solid #ccc; border-radius: 4px; font-size: 0.9rem; font-family: system-ui, sans-serif; color: #1a1a1a; background: #fafaf8; } .calc-row input:focus, .calc-row select:focus { outline: none; border-color: var(--ac, #3d5a80); box-shadow: 0 0 0 2px rgba(26,74,138,0.12); } .calc-row .unit { font-size: 0.82rem; color: #888; min-width: 30px; } .calc-btn { display: inline-block; margin-top: 0.5rem; padding: 0.55rem 1.5rem; background: var(--ac, #3d5a80); color: #fff; border: none; border-radius: 4px; font-size: 0.9rem; font-weight: 600; cursor: pointer; font-family: system-ui, sans-serif; } .calc-btn:hover { opacity: 0.9; } .calc-result { margin-top: 1.25rem; padding: 1rem 1.25rem; background: #f0f6fc; border-left: 3px solid var(--ac, #3d5a80); border-radius: 0 6px 6px 0; display: none; } .calc-result.visible { display: block; } .calc-result-label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; color: #666; margin-bottom: 0.25rem; } .calc-result-value { font-size: 1.6rem; font-weight: 700; color: var(--ac, #3d5a80); } .calc-result-detail { font-size: 0.85rem; color: #555; margin-top: 0.5rem; line-height: 1.5; } .calc-note { margin-top: 1rem; font-size: 0.8rem; color: #888; font-style: italic; } .calc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-top: 0.75rem; } .calc-grid-item { padding: 0.6rem 0.8rem; background: #f8f9fa; border-radius: 4px; border: 1px solid #eee; } .calc-grid-item .label { font-size: 0.75rem; color: #888; text-transform: uppercase; letter-spacing: 0.04em; } .calc-grid-item .value { font-size: 1.1rem; font-weight: 600; color: #1a1a1a; } @media (max-width: 720px) { .calc-row { flex-direction: column; align-items: flex-start; gap: 0.3rem; } .calc-row label { min-width: auto; } .calc-row input[type="number"], .calc-row select { max-width: 100%; width: 100%; } .calc-grid { grid-template-columns: 1fr; } } .calc-chart { margin: 1rem 0; text-align: center; } .calc-chart svg { max-width: 100%; height: auto; } .calc-chart-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem 1.2rem; margin-top: 0.6rem; font-size: 0.8rem; color: #555; } .calc-chart-legend span { display: inline-flex; align-items: center; gap: 0.3rem; } .calc-chart-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; font-style: normal; } .calc-related { max-width: 640px; margin: 2rem 0 1rem; padding: 1.25rem 1.5rem; background: #f8f9fa; border: 1px solid #e8e8e8; border-radius: 8px; } .calc-related h3 { font-family: Georgia, serif; font-size: 1rem; color: #1a1a1a; margin: 0 0 0.75rem; padding-bottom: 0.4rem; border-bottom: 2px solid var(--ac, #3d5a80); } .calc-related-list { list-style: none; padding: 0; margin: 0 0 0.75rem; display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem 1.5rem; } .calc-related-list li a { font-size: 0.88rem; color: var(--ac, #3d5a80); text-decoration: none; } .calc-related-list li a:hover { text-decoration: underline; } .calc-browse-all { margin: 0.5rem 0 0; font-size: 0.9rem; font-weight: 600; } .calc-browse-all a { color: var(--ac, #3d5a80); text-decoration: none; } .calc-browse-all a:hover { text-decoration: underline; } @media (max-width: 720px) { .calc-related-list { grid-template-columns: 1fr; } }

Monster CR Calculator

Estimate a D&D 5e monster's Challenge Rating (CR) based on its Armor Class, Hit Points, Attack Bonus, and average damage per round.

Hit Points (HP)

Armor Class (AC)

Attack Bonus

Avg Damage Per Round (DPR)

Save DC (optional, 0 to ignore)

Calculate CR Fill in the fields above and click Calculate.

// DMG p.274-275 tables // Defensive CR by HP (after AC adjustment) // Offensive CR by DPR (after attack bonus / save DC adjustment)

const MON_DEF_CR = [ // [maxHP, cr] [6,0],[13,0.125],[20,0.25],[36,0.5],[52,1],[70,2],[90,3],[110,4], [130,5],[150,6],[170,7],[190,8],[210,9],[230,10],[250,11],[270,12], [290,13],[310,14],[330,15],[350,16],[370,17],[390,18],[410,19],[430,20], [450,21],[470,22],[490,23],[510,24],[530,25],[550,26],[570,27],[590,28], [610,29],[999999,30] ];

const MON_OFF_CR = [ // [maxDPR, cr] [1,0],[3,0.125],[5,0.25],[8,0.5],[14,1],[20,2],[26,3],[32,4], [38,5],[44,6],[50,7],[56,8],[62,9],[68,10],[74,11],[80,12], [86,13],[92,14],[98,15],[104,16],[110,17],[116,18],[122,19],[128,20], [134,21],[140,22],[146,23],[152,24],[158,25],[164,26],[170,27],[176,28], [182,29],[999999,30] ];

// Expected AC and Attack Bonus by CR (DMG p.274) const MON_EXPECTED_AC = [ [0,13],[0.125,13],[0.25,13],[0.5,13],[1,13],[2,13],[3,13],[4,14], [5,15],[6,15],[7,15],[8,16],[9,16],[10,17],[11,17],[12,17],[13,18], [14,18],[15,18],[16,18],[17,19],[18,19],[19,19],[20,19],[21,19], [22,19],[23,19],[24,19],[25,19],[26,19],[27,19],[28,19],[29,19],[30,19] ];

const MON_EXPECTED_ATK = [ [0,3],[0.125,3],[0.25,3],[0.5,3],[1,3],[2,3],[3,4],[4,5], [5,6],[6,6],[7,6],[8,7],[9,7],[10,7],[11,8],[12,8],[13,8], [14,8],[15,8],[16,9],[17,10],[18,10],[19,10],[20,10],[21,11], [22,11],[23,11],[24,12],[25,12],[26,12],[27,13],[28,13],[29,13],[30,14] ];

const MON_EXPECTED_SAVE = [ [0,13],[0.125,13],[0.25,13],[0.5,13],[1,13],[2,13],[3,13],[4,14], [5,15],[6,15],[7,15],[8,16],[9,16],[10,17],[11,17],[12,17],[13,18], [14,18],[15,18],[16,18],[17,19],[18,19],[19,19],[20,19],[21,19], [22,19],[23,19],[24,19],[25,19],[26,19],[27,19],[28,19],[29,19],[30,19] ];

const CR_LABELS = { 0:"0",0.125:"1/8",0.25:"1/4",0.5:"1/2", 1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10", 11:"11",12:"12",13:"13",14:"14",15:"15",16:"16",17:"17",18:"18", 19:"19",20:"20",21:"21",22:"22",23:"23",24:"24",25:"25",26:"26", 27:"27",28:"28",29:"29",30:"30" };

const CR_XP = { 0:10,0.125:25,0.25:50,0.5:100,1:200,2:450,3:700,4:1100,5:1800, 6:2300,7:2900,8:3900,9:5000,10:5900,11:7200,12:8400,13:10000, 14:11500,15:13000,16:15000,17:18000,18:20000,19:22000,20:25000, 21:33000,22:41000,23:50000,24:62000,25:75000,26:90000,27:105000, 28:120000,29:135000,30:155000 };

function monLookupCR(table, value) { for (let [max, cr] of table) { if (value 0) { // use whichever is higher: atk bonus or save DC comparison let atkDiff = atk - expectedAtk; let saveDiff = saveDC - expectedSave; diff = Math.max(atkDiff, saveDiff); } else { diff = atk - expectedAtk; } let shift = Math.round(diff / 2); return rawCR + shift; }

function monClampCR(cr) { const valid = [0,0.125,0.25,0.5,1,2,3,4,5,6,7,8,9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]; // find closest valid CR let best = valid[0]; let bestDiff = Math.abs(valid[0] - cr); for (let v of valid) { let d = Math.abs(v - cr); if (d Please enter a valid HP (≥ 1).'; return; } if (isNaN(ac) || ac 30) { res.innerHTML='Please enter a valid AC (1–30).'; return; } if (isNaN(atk) || atk 20) { res.innerHTML='Please enter a valid Attack Bonus (−5 to +20).'; return; } if (isNaN(dpr) || dpr Please enter a valid DPR (≥ 0).'; return; } if (saveDC 30) { res.innerHTML='Save DC must be 0–30 (0 = ignore).'; return; }

// Step 1: Defensive CR from HP let defCRRaw = monLookupCR(MON_DEF_CR, hp); // Step 2: Adjust defensive CR for AC let defCR = monAdjustDefCR(defCRRaw, ac); defCR = Math.max(0, Math.min(30, defCR));

// Step 3: Offensive CR from DPR let offCRRaw = monLookupCR(MON_OFF_CR, dpr); // Step 4: Adjust offensive CR for attack bonus / save DC let offCR = monAdjustOffCR(offCRRaw, atk, saveDC); offCR = Math.max(0, Math.min(30, offCR));

// Step 5: Average defensive and offensive CR let avgCR = (defCR + offCR) / 2; let finalCR = monClampCR(avgCR);

let xp = CR_XP[finalCR] || "?"; let crLabel = CR_LABELS[finalCR] || String(finalCR); let defLabel = CR_LABELS[monClampCR(defCR)] || String(defCR); let offLabel = CR_LABELS[monClampCR(offCR)] || String(offCR);

res.innerHTML = ` Estimated CR: ${crLabel} (${xp.toLocaleString()} XP)

Defensive CR: ${defLabel} (HP ${hp}, AC ${ac}) Offensive CR: ${offLabel} (DPR ${dpr}, Atk +${atk}${saveDC > 0 ? ', Save DC ' + saveDC : ''}) Final CR = average of Defensive CR (${defCR.toFixed(2)}) and Offensive CR (${offCR.toFixed(2)}) = ${avgCR.toFixed(2)} → rounded to nearest valid CR `; }

#### Formula (DMG p.274–275)

1. Defensive CR — Look up raw CR from HP table. Adjust: for every 2 points AC differs from expected AC at that CR, shift CR ±1. 2. Offensive CR — Look up raw CR from DPR table. Adjust: for every 2 points Attack Bonus (or Save DC) differs from expected at that CR, shift CR ±1. 3. Final CR = (Defensive CR + Offensive CR) ÷ 2, rounded to nearest valid CR value. Valid CRs: 0, 1/8, 1/4, 1/2, 1–30.

#### Assumptions & References

More Calculators

Read Next

Study Time Planner Authority Network America › Life Services Authority › National Calculator Authority .calc-container { max-width: 640px;...

References