D2R IAS Calculations — Reference
This document explains how Diablo II: Resurrected calculates attack speed, what breakpoints are, and how every formula in the calculator's engine works. The reference authority for these formulas is the original IAS calculator's source code, which was reverse-engineered from the game's animation system.
Table of Contents
- Core Concepts
- Terminology
- The EIAS Formula
- Animation Speed and Frame Count
- What Breakpoints Are
- Skill Categories
- Dual-Wield Mechanics
- Wereform Speed Reduction
- Attack Speed Skills
- Debuffs
- Table Variables
- URL Sharing Format
- Appendix: Constants and Limits
Core Concepts
Diablo 2's animations run at 25 frames per second. Every attack animation has a fixed number of frames per direction (FPD) determined by the character class and weapon type. The game speeds up or slows down this animation based on the character's total effective attack speed. Because the result is measured in integer frames, the relationship between IAS and attack speed is not smooth — it jumps at specific thresholds called breakpoints.
The central question the calculator answers is: "Given my character, weapon, skill, and modifiers, how many frames does each attack take, and how much more IAS do I need to reach the next breakpoint?"
Terminology
| Abbreviation | Full Name | Description |
|---|---|---|
| IAS | Increased Attack Speed | Total IAS from all gear except weapon-specific IAS |
| WIAS | Weapon IAS | IAS on the weapon itself (relevant for dual-wield) |
| GIAS | Gear IAS | IAS from non-weapon gear when WIAS is being tracked separately |
| SIAS | Skill IAS | Speed contribution from skills (Fanaticism, BoS, etc.) and debuffs |
| IEIAS | Item EIAS | The effective IAS after applying the diminishing-returns conversion |
| EIAS | Effective IAS | The final combined speed modifier: SIAS - WSM + IEIAS |
| WSM | Weapon Speed Modifier | Intrinsic speed of the weapon base (-30 to +20). Lower is faster. |
| FPD | Frames Per Direction | Base frame count for the attack animation |
| FPA | Frames Per Attack | The actual frame count after speed modification (what breakpoints show) |
The EIAS Formula
All sources of attack speed ultimately combine into a single number: EIAS (Effective Increased Attack Speed). This is the number the game's animation engine actually uses.
IAS to EIAS Conversion (Diminishing Returns)
IAS does not scale linearly. The conversion from IAS to EIAS uses:
IEIAS = floor(120 * IAS / (120 + IAS))
This creates diminishing returns:
| IAS | IEIAS | Marginal gain per 20 IAS |
|---|---|---|
| 0 | 0 | — |
| 20 | 17 | +17 |
| 40 | 30 | +13 |
| 60 | 40 | +10 |
| 80 | 48 | +8 |
| 100 | 54 | +6 |
| 120 | 60 | +6 |
At 120 IAS you've only gained 60 EIAS. The formula asymptotically approaches 120 EIAS but never reaches it.
The inverse conversion (EIAS back to IAS) is:
IAS = ceil(120 * EIAS / (120 - EIAS))
Single-Weapon EIAS
For standard (non-dual-wield) attacks:
EIAS = SIAS - WSM + floor(120 * (GIAS + WIAS) / (120 + GIAS + WIAS))
Where:
- SIAS is the sum of all skill-based speed modifiers (see Attack Speed Skills and Debuffs)
- WSM is the weapon's speed modifier (e.g., Phase Blade = -30, Berserker Axe = 0)
- GIAS is gear IAS (only tracked separately in WIAS table-variable modes; otherwise folded into IAS)
- WIAS is weapon-specific IAS (only non-zero when dual-wielding and the WIAS field is visible)
Dual-Wield EIAS
When dual-wielding with sequence/dual-wield skills (Frenzy, Dragon Claw, Double Swing, etc.), the formula changes to average both weapons:
WSM = (WSM1 + WSM2) / 2
IEIAS = (floor(120 * (GIAS + WIAS1) / (120 + GIAS + WIAS1)) + floor(120 * (GIAS + WIAS2) / (120 + GIAS + WIAS2))) / 2
EIAS = floor(SIAS - WSM + IEIAS)
Note: The WSM averaging and IEIAS averaging both use the unrounded intermediate values. Only the final EIAS is floored (using the trun function, which floors positive numbers and ceils negative numbers).
EIAS Limits
EIAS is clamped before use:
| Form | Minimum | Maximum |
|---|---|---|
| Human | -85 | 75 |
| Wereform (Werewolf/Werebear) | -85 | 150 |
The higher wereform cap exists because wereforms apply a speed reduction factor that counteracts the higher EIAS range. The practical speed achievable is similar.
Animation Speed and Frame Count
Once EIAS is known, the game calculates how many frames the attack takes.
Base Animation Speed
Most attacks use a base animation speed of 256. Exceptions:
| Condition | Animation Speed |
|---|---|
| Default | 256 |
| Laying Traps | 128 (half speed) |
| Claw (non-charge-up, non-kick) with 12 FPD | 227 |
| Claw (non-charge-up, non-kick) with other FPD | 208 |
| Druid Kick with One-Handed Swinging / Two-Handed Sword | 224 |
Human Form Speed Calculation
For human form characters, the speed increase is:
speedIncrease = floor(animationSpeed * (100 + clamp(EIAS)) / 100)
Then the frame count is:
FPA = ceil(256 * (FPD - startingFrame) / speedIncrease) - offset
Where:
startingFrameis usually 0, but is 1-2 for Amazon/Sorceress with certain weapon types using Standard/Fend/Zealoffsetis 1 for standard attacks, 0 for sequence/rollback/special skills
Wereform Speed Calculation
Wereforms use a fundamentally different formula. Instead of a simple percentage, they apply a speed reduction factor:
speedReduction = fpdWereform / fpdHuman
speedIncrease = floor((animationSpeed + floor(animationSpeed * clamp(EIAS) / 100)) * speedReduction)
Where:
fpdWereformis the wereform's base FPD (13 for Werewolf, 12 for Werebear)fpdHumanis the human-form FPD for the same weapon type/character combination
This means wereforms have a multiplier on their speed that depends on the ratio between their wereform frames and the human frames for that weapon. Weapons with high human FPD give wereforms a proportionally larger speed reduction.
What Breakpoints Are
Because FPA is an integer (you can't have fractional frames), the relationship between EIAS and attack speed is a step function. The FPA stays constant across a range of EIAS values, then drops by 1 at a specific threshold.
For example, an Amazon with a Phase Blade might see:
| IAS | FPA |
|---|---|
| 0 | 11 |
| 6 | 10 |
| 16 | 9 |
| 30 | 8 |
| 52 | 7 |
| 89 | 6 |
The breakpoint at IAS 6 means: any IAS from 0-5 gives 11 FPA, but 6+ gives 10 FPA. There is no benefit to having 5 IAS — you need to hit exactly 6 to gain a frame. This is why the calculator exists — to tell you the exact thresholds so you don't waste gear affixes on IAS that doesn't reach the next breakpoint.
How the Calculator Finds Breakpoints
The engine iterates over every possible acceleration value from 0 (or -85 for EIAS mode) to the maximum allowed value, computes the resulting frame count at each step, and records only the values where the frame count changes. Those change points are the breakpoints.
Skill Categories
Standard Skills
Skills that use the weapon's normal attack animation: Standard Attack, Concentrate, Berserk, Bash, Stun, Sacrifice, Vengeance, Conversion, Tiger Strike, Cobra Strike, Phoenix Strike, Feral Rage, Rabies.
These use the weapon type's FPD directly from the animation data tables. Some characters have alternate attack animations (e.g., Amazon with swords has two different swing animations that alternate).
Sequence Skills
Multi-frame skills with hardcoded frame counts that differ from the weapon's normal animation:
| Skill | Sequence FPD |
|---|---|
| Jab | 18 (thrusting) / 21 (other) |
| Impale | Same as Jab |
| Fists of Fire / Claws of Thunder / Blades of Ice | 12 (claw/unarmed) / 16 (other or dual-wield) |
| Dragon Claw | 12 (claw/unarmed) / 16 (other) |
| Double Swing / Frenzy | 17 |
| Double Throw | 12 |
Rollback Skills
Multi-hit skills where each successive hit starts from a "rolled back" animation frame, making later hits faster than the first:
| Skill | Hits | Rollback Factor | Final Hit Uses |
|---|---|---|---|
| Zeal | 1 rollback hit + final | 100% (no rollback) | FPD2 |
| Dragon Talon | 1 rollback hit + final | 100% (no rollback) | FPD2 |
| Fury | 3 rollback hits + final | 70% | FPD2 |
| Strafe | 4 rollback hits + final | 50% | FPD2 |
| Fend | 4 rollback hits + final | 30% | FPD2 |
The rollback calculation is iterative:
For each hit after the first:
rollback = trun(trun((256 * previousRollback + speedIncrease * previousHitLength) / 256) * ((100 - rollbackFactor) / 100))
hitLength = ceil(256 * (FPD1 - rollback) / speedIncrease)
Final hit:
hitLength = ceil(256 * (FPD2 - lastRollback) / speedIncrease) - 1
Where FPD2 is the "second phase" frame count (weapon-type-dependent, used for the finishing animation).
The result is displayed as a formatted string like (4)+3+5 where:
- Parenthesized values repeat for each intermediate hit
- The last value is the final hit's frame count
+separates distinct hit lengths
Strafe and Fend generate two breakpoint tables: one for even arrow/hit counts and one for odd counts, because the oscillating even/odd behavior produces different timings.
Whirlwind
Whirlwind is unique in several ways:
- Uses truncation (
floor) instead of ceiling for frame calculation:FPA = floor(256 * (actionFrame - startingFrame) / speedIncrease) - Uses the weapon type's action frame instead of FPD
- Barbarian Two-Handed Swords are always treated as One-Handed Swinging for Whirlwind
- When dual-wielding, two independent tables are generated and then merged into a combined effective-speed table (see Dual-Wield Mechanics)
Special Skills
| Skill | Behavior |
|---|---|
| Kick | Fixed 13 FPD (Assassin) or 12 FPD (other characters). Used for kicking barrels/objects. |
| Dodge/Avoid/Evade | Fixed 9 FPD, always uses Fanaticism as the table variable. Independent of weapon. |
| Laying Traps | Fixed 8 FPD, half animation speed (128 instead of 256). |
| Smite | Fixed 12 FPD. |
| Dragon Tail | Standard FPD but with a -40 SIAS penalty. |
| Dragon Talon | Fixed 4-frame action frame, rollback with 100% factor (1 hit + final). |
Dual-Wield Mechanics
When Dual-Wield Is Available
- Assassin: When primary weapon is a Claw, can equip a second Claw
- Barbarian: Can equip a second weapon in the off-hand (One-Handed Swinging, One-Handed Thrusting, or Two-Handed Sword)
- Frenzy Barbarian (A5 Merc): Same as Barbarian
Dual-Wield-Only Skills
Dragon Claw, Double Swing, Frenzy, and Double Throw require dual-wielding. When these skills are selected, the IAS table variable is hidden and replaced with Primary/Secondary Weapon IAS.
WSM and IEIAS Averaging
For dual-wield sequence skills (Fists of Fire, Claws of Thunder, Blades of Ice, Dragon Claw, Double Swing, Frenzy, Double Throw), both weapons' WSM and IEIAS values are averaged:
effectiveWSM = (primaryWSM + secondaryWSM) / 2
effectiveIEIAS = (IEIAS_primary + IEIAS_secondary) / 2
This means a fast off-hand weapon partially compensates for a slow main-hand weapon.
Whirlwind Dual-Wield Merge
Whirlwind with two weapons generates three tables:
- Primary weapon table — Breakpoints for the main-hand weapon alone
- Secondary weapon table — Breakpoints for the off-hand weapon alone
- Merged table — Combined effective speed
The merge algorithm:
- Takes the first breakpoint from each table
- Averages their FPA values (ceiling)
- Sorts all remaining breakpoints by acceleration threshold
- For each subsequent breakpoint, updates the corresponding hand's FPA and recomputes the average
- Only records a merged breakpoint when the new average is strictly lower than the previous
The merged table uses the EIAS values from the slower weapon (the one with higher EIAS, since that weapon determines the bottleneck).
Note: The merge algorithm is a simplification. It doesn't consider all possible combinations of frame values more than 1 frame apart. For edge cases, manual combination is required.
Wereform Speed Reduction
When a character is in Werewolf or Werebear form, the game uses different base FPD values:
| Wereform | FPD1 | FPD2 | FPD3 |
|---|---|---|---|
| Werewolf | 13 | 9 | 13 |
| Werebear | 12 | 10 | 12 |
Special overrides:
- Fury (Werewolf): FPD1 = 7
- Hunger/Rabies (Werewolf): FPD1 = 10
The speed reduction ratio FPD3 / FPDhuman is applied as a multiplier on the animation speed, effectively making wereform attacks slower with weapons that have fast human-form animations and faster with weapons that have slow human-form animations.
Attack Speed Skills
These skills provide EIAS bonuses using a diminishing-returns formula:
Standard Formula
For Fanaticism, Burst of Speed, Werewolf, and Frenzy:
bonus = min(minBonus + floor(factor * floor(110 * level / (level + 6)) / 100), maxBonus)
| Skill | Min | Factor | Max | Example (level 20) |
|---|---|---|---|---|
| Fanaticism | 10 | 30 | 40 | 10 + floor(30 * floor(110 * 20 / 26) / 100) = 10 + floor(30 * 84 / 100) = 10 + 25 = 35 |
| Burst of Speed | 15 | 45 | 60 | 15 + floor(45 * 84 / 100) = 15 + 37 = 52 |
| Werewolf | 10 | 70 | 80 | 10 + floor(70 * 84 / 100) = 10 + 58 = 68 |
| Frenzy | 0 | 50 | 50 | 0 + floor(50 * 84 / 100) = 42 |
| Holy Freeze | 25 | 35 | 50 | 25 + floor(35 * 84 / 100) = 25 + 29 = 54 → capped at 50 |
Maul Formula
Maul uses a different formula:
bonus = factor * (floor(level / 2) + 3)
With factor = 3, this gives:
- Level 1: 3 * (0 + 3) = 9
- Level 10: 3 * (5 + 3) = 24
- Level 20: 3 * (10 + 3) = 39
- Level 30: 3 * (15 + 3) = 54
Skill Predicates
Attack speed skills only apply when their conditions are met:
- Werewolf: Only in Werewolf form
- Maul: Only in Werebear form
- Frenzy: Only for Barbarian and Bash Barbarian (A5 Merc)
- Burst of Speed: Available to all, but table variable only shown for Assassin
When a skill is selected as the table variable (the X-axis of the breakpoint table), its EIAS contribution is excluded from the base calculation and instead varied across the table rows. This lets you see breakpoints as a function of that skill's level.
Debuffs
Debuffs reduce SIAS (applied additively before the EIAS formula):
| Debuff | SIAS Reduction |
|---|---|
| Decrepify | -50 (not applied to Dodge) |
| Chilled | -50 |
| Slowed By | -N (variable, user-entered) |
| Holy Freeze | Level-based (see formula above) |
| Mark of Bear | +25 (bonus, player characters only) |
Skill-Specific SIAS Adjustments
Some skills have built-in SIAS modifiers:
| Skill | SIAS Adjustment |
|---|---|
| Double Swing | +20 |
| Dragon Tail | -40 |
| Jab, Fists of Fire, Claws of Thunder, Blades of Ice, Dragon Claw, Frenzy, Double Throw | -30 (player characters only) |
Table Variables
The breakpoint table's left column (the "variable") can display different values depending on what the user wants to optimize for:
| Table Variable | Description | Range |
|---|---|---|
| EIAS | Raw EIAS value | -85 to 75 (150 for wereforms) |
| IAS | Total IAS from gear | 0 to 88 (83 for 2H, 78 for mercs) |
| Primary Weapon IAS | IAS on main-hand weapon | 0 to 120 |
| Secondary Weapon IAS | IAS on off-hand weapon | 0 to 120 |
| Fanaticism | Fanaticism skill level | 0 to 60 |
| Burst of Speed | Burst of Speed skill level | 0 to 60 |
| Werewolf | Werewolf skill level | 0 to 60 |
| Maul | Maul skill level | 0 to 60 |
| Frenzy | Frenzy skill level | 0 to 60 |
When a skill-based table variable is selected, the breakpoint table shows skill levels on the left and FPA on the right. The conversion from internal EIAS breakpoints to skill levels uses the inverse of the attack speed skill formula (a reverse lookup table built at initialization).
WIAS Conversion
Converting internal EIAS breakpoints to WIAS values requires working backward through the dual-wield averaging formula:
neededIEIAS = 2 * neededEIAS - 2 * SIAS + WSM1 + WSM2 - IEIAS_otherHand
neededWIAS = convertEIAStoIAS(neededIEIAS) - GIAS
This is the most complex conversion because it must account for the other hand's contribution.
URL Sharing Format
The ?data= query parameter encodes the full calculator state as a - separated string with spaces converted to underscores. The 20 fields in order:
| # | Field | Type | Example |
|---|---|---|---|
| 1 | Character | int (0-10) | 2 (Barbarian) |
| 2 | Wereform | int (0-2) | 0 (Human) |
| 3 | Primary Weapon | string | Phase_Blade |
| 4 | Primary WIAS | int | 0 |
| 5 | Is One-Handed | bool (0/1) | 0 |
| 6 | Secondary Weapon | string | None |
| 7 | Secondary WIAS | int | 0 |
| 8 | Skill | string | Whirlwind |
| 9 | Table Variable | int (0-8) | 1 (IAS) |
| 10 | IAS | int | 0 |
| 11 | Fanaticism | int | 0 |
| 12 | Burst of Speed | int | 0 |
| 13 | Werewolf | int | 0 |
| 14 | Maul | int | 0 |
| 15 | Frenzy | int | 0 |
| 16 | Mark of Bear | bool (0/1) | 0 |
| 17 | Holy Freeze | int | 0 |
| 18 | Slowed By | int | 0 |
| 19 | Decrepify | bool (0/1) | 0 |
| 20 | Chilled | bool (0/1) | 0 |
Example URL:
?data=2-0-Phase_Blade-0-0-None-0-Whirlwind-1-0-0-0-0-0-0-0-0-0-0-0
This format is identical between the original calculator and this rewrite. Links generated by either calculator will load correctly in both.
Appendix: Constants and Limits
EIAS Bounds
| Parameter | Value |
|---|---|
| Minimum EIAS | -85 |
| Maximum EIAS (human) | 75 |
| Maximum EIAS (wereform) | 150 |
IAS Iteration Limits
These define how far the calculator iterates when generating breakpoint tables:
| Table Variable | Max Value |
|---|---|
| Character IAS (one-hand/barbarian) | 88 |
| Character IAS (two-handed) | 83 |
| Mercenary IAS | 78 |
| Weapon IAS | 60 (iteration) / 120 (display cap) |
| Skill levels | Skill-specific max |
The trun Function
The engine uses a custom truncation function that matches Diablo 2's internal integer math:
function trun(n: number): number {
if (n === -0) n = 0;
return n >= 0 ? Math.floor(n) : Math.ceil(n);
}
This truncates toward zero (positive numbers floor, negative numbers ceil), matching C-style integer division. The -0 check prevents IEEE 754 negative-zero from affecting comparisons.