How Echo Scores Are Calculated

Every formula behind the scores and ranks this tool displays is published here. This is the technical breakdown for anyone who wants to check the reasoning behind the numbers.

← Back to Simulator

1. Two Scoring Modes

Scores are produced one of two ways. The first sorts substats by hand into four buckets — recommended, preferred, acceptable, unnecessary — and applies a fixed multiplier per bucket (the "category mode" below). The second derives a continuous weight for each substat from the partial derivatives of the damage formula, given the character's base stats, signature weapon, and damage type breakdown (the "build variant mode").

In build variant mode the buckets themselves are inferred back out of the weights. Characters with the required data use this mode; the rest fall back to category mode automatically.

A "build variant" captures the fact that the same character plays as a main DPS, sub DPS, or support depending on the team, and that each of those wants different sets and stats. The closest matching variant is selected from the harmony set you have equipped.

2. Deriving the Weights

The damage formula assumed throughout is:

DMG ∝ MainStat_total × (1 + %DMG_typeX) × CritMult
CritMult = 1 + critRate × critDmg

The weight of a substat is the relative damage increase ΔDMG/DMG from adding one maximum roll of it. The absolute magnitude carries no meaning — only the ratios between substats matter, because everything is later divided by a theoretical maximum.

Crit Rate and Crit DMG

The expected damage multiplier 1 + critRate × (critDmg − 1) is differentiated with respect to crit rate and crit DMG separately. This is what makes crit rate lose weight automatically on a build where it is already high.

cdBonus  = baselineCritDmg - 1        // portion above non-crit
critMult = 1 + baselineCritRate × cdBonus

weight[critRate] = (cdBonus × maxRollFraction(critRate)) / critMult
weight[critDmg]  = (baselineCritRate × maxRollFraction(critDmg)) / critMult

ATK% and Flat ATK (scaling stats)

A baseline for the other percentage buffs already stacked on the build is assumed (0.45), plus the character's own buffs and the signature weapon's substat when it grants ATK%. That total goes in the denominator: percentage stats dilute as they pile up, and that dilution is modelled explicitly.

existingPctSum = 0.45 + selfAtkBuffPercent + weaponGivesScalingPct

weight[atkPercent] = maxRollFraction(atkPercent) / (1 + existingPctSum)
weight[atkFlat]    = maxRollFraction(atkFlat) / (totalMainStat × (1 + existingPctSum))
// totalMainStat = character base ATK (Lv90) + signature weapon base ATK (Lv90)

Attack type DMG% (basic / heavy / skill / liberation)

The share each attack type contributes to the character's total damage multiplies the weight directly. A heavy-attack character gets a correspondingly larger weight on Heavy ATK DMG%.

weight[basicAttackDmg]    = maxRollFraction(basicAttackDmg)    × typeShares.basic
weight[heavyAttackDmg]    = maxRollFraction(heavyAttackDmg)    × typeShares.heavy
weight[resonanceSkillDmg] = maxRollFraction(resonanceSkillDmg) × typeShares.skill
weight[resonanceLibDmg]   = maxRollFraction(resonanceLibDmg)   × typeShares.lib

Energy Regen

Energy regen is a threshold stat: highly valuable until the rotation requirement is met, then close to worthless. It admits no linear approximation from the damage formula, so a static weight keyed to each variant's energy requirement is substituted instead, scaled to sit alongside the other weights (roughly 0.02 to 0.10).

Energy requirementWeight
≤ 1.050.02
≤ 1.300.05
≤ 1.600.08
Above that0.11

When base data for the weapon or character is missing, scoring falls back to category mode.

3. Computing the Score

Substat contribution

points = (sub.value / sub.maxValue) × weight[sub.key]  // roll ratio × weight

Theoretical maximum

The theoretical maximum fills every substat slot available at that cost with the highest-weighted stats at a "reference roll." That reference is the same for every stat: the 5/7 point of the eight-step value array. Using 5/7 rather than the maximum keeps the 100-point mark attainable — anchoring it to an all-perfect-rolls echo, which essentially never happens, would leave scores feeling detached from reality.

REFERENCE_ROLL_FRACTION = 5 / 7
referenceRatio(key) = values[round(5/7 × (values.length-1))] / values[values.length-1]

topKeys        = weights sorted descending, first substatCount entries
theoreticalMax = Σ referenceRatio(k) × weight[k]   (k ∈ topKeys)

substatScore = (Σ points / theoreticalMax) × 100

Main stat adjustment

A penalty applies when the main stat does not match what that variant wants at that cost. An echo with great substats but a mismatched main stat is hard to justify slotting in practice.

ConditionAdjustment
Recommended main stat0
Acceptable main stat−10
Neither−25

Harmony set adjustment

ConditionAdjustment
Recommended set0
Acceptable set−5
Neither−10

Combo bonus

Each substat weight is compared against the highest weight and sorted into an equivalent bucket; a bonus is then added based on how many land in "recommended." This rewards echoes where several good substats line up, beyond what a plain sum would give.

weightToCategory(weight, maxWeight):
  rel = weight / maxWeight
  rel >= 0.85 → recommended
  rel >= 0.50 → preferred
  rel >= 0.15 → acceptable
  else        → unnecessary

COMBO_BONUS = { 3 stats: +3, 4 stats: +7, 5 stats: +12 }

Final score

rawScore = round(substatScore + mainstatBonus + setBonus) + comboBonus
score    = clamp(rawScore, 0, 100)

4. Ranks: Percentiles of the Drop Distribution

Converting a raw score straight into a rank does not match how the grind feels. When five slots are drawn at equal probability from 13 types, hitting all five wanted stats at high rolls is vanishingly unlikely — landing all five at any roll is already about 0.08% — so genuinely good drops kept settling into middling ranks.

Instead, thresholds come from a drop score distribution precomputed per character and per build variant. Holding the recommended main stat and harmony set fixed, five substats are drawn completely at random across 500,000 trials, and the score at each top-X% cut becomes that rank's threshold.

RankCumulative probability (top)
GOD0.01%
S+1%
S5%
A15%
B35%
C65%
DBelow that

So an S rank means "an echo in the top 5% of drops for this character." Characters and variants without threshold data fall back to fixed score cutoffs: S+ ≥ 90, S ≥ 75, A ≥ 58, B ≥ 42, C ≥ 25, and D below that.

5. Which Mode Gets Used

The scoring mode is chosen in this order.

1. No character selected     → generic category scoring
2. Build variant data exists → infer the variant from the equipped set
                               and score in build variant mode
                               (fall through to 3 if base data is missing)
3. Otherwise                 → category scoring

The variant is inferred from the equipped harmony set. With only one variant defined, that one is used; with several, the tool looks for one listing the equipped set as recommended or acceptable, and defaults to the first variant if none matches.

6. The Two Modes Side by Side

AspectCategory modeBuild variant mode
Weight unitFour fixed multipliers (2.0 / 1.6 / 0.8 / 0.1)Continuous, from the damage formula's partial derivatives
Basis for sortingListed by hand per characterComputed from base stats, signature weapon, and damage breakdown
Theoretical maximumFixed multiplier (≈1.84) × reference rollSum of the top N weights × reference roll ratio
Rank determinationFixed cutoffs on the raw scoreDrop distribution percentiles per character and variant
CoverageAll charactersOnly characters with complete data

7. Percentile Badge and Distribution Chart

To address the gap between a raw score and how the rank feels — a score in the 50s can still be an A — a "top X%" badge sits next to the score. Alongside it, a histogram plots how often each score occurs, with score on the horizontal axis and frequency on the vertical. Rank bands are shaded in the background and the bin holding your score is highlighted. Hovering any bin shows its frequency and percentile.

The histogram bins come from the same threshold generation run, tallying scores from 0 to 100 in steps of 2 into a 51-element frequency array. The percentile itself is interpolated log-linearly from a separately stored series mapping top-% to score.

Recommended sets, main stats, and substat priorities for each character are listed on the build data by character page.