XP curve per level with math presets, custom expressions, and simulation.

xpBalance#

Defines the XP curve governing the player's level progression (or any entity with progression). Supports common math presets (linear, exponential, soft-cap, plateau) or custom expressions for unique formulas.

Why it matters#

The XP curve is one of the hardest design decisions to iterate without a tool — you change growth from 1.15 to 1.20 and need to simulate 100 levels in your head. Here the app generates the curve, shows cumulative value, average step, plateaus, and balance highlights. Editing is trivial.

And since the result becomes BalancePoint[] (an array of {level, value}), other tables/skills/items can reference this curve to scale values alongside it.

Where it usually lives#

  • Page of type 📈 XP Balance. That's the main path.
  • 👤 Characters page (comes as a recommended addon, alongside Profile).

Singleton — 1 curve per page.

Schema#

CampoTipoPadrãoDescrição
idYesstringInternal ID.
nameYesstringFriendly name (e.g., Hero XP Curve).
modeYes"preset" | "advanced"preset: uses one of 7 templates. advanced: you write the expression.
presetYes"linear" | "exponential" | "tiered" | "softCap" | "hardCap" | "diminishingReturns" | "piecewise"Which preset, when mode=preset.
expressionYesstringMath expression when mode=advanced. Supports level, basic operators, pow(), min(), max().
startLevelYesnumberFirst level of the curve (usually 1).
endLevelYesnumberLast level.
decimalsYesnumberDecimal places when rounding generated values.
clampMinnumberAbsolute minimum floor (optional).
clampMaxnumberAbsolute maximum ceiling (optional).
paramsYesBalanceFormulaParamsPreset parameters (base, growth, offset, tierStep, etc.).
profile"rpg" | "idle" | "roguelite" | "casual"Genre profile — pre-configures sensible defaults when you switch.
comparisonBaselineBalancePoint[]Comparison curve (e.g., from the studio's previous game) rendered behind the current curve in the chart.
simulationInputBalanceSimulationInputInputs to simulate time to each level given XP/min, win rate, match duration.
targetBalanceTargetInputIntended goal (e.g., player reaches level 50 in 20h). The panel suggests adjustments to hit it.

The 7 presets#

PresetFormula
linearbase + (level − 1) × step
exponentialbase × growth^(level − 1)
tieredLinear until tierStep, multiplied by tierMultiplier per tier
softCapGrows normally until capValue, then grows slowly
hardCapGrows normally until capValue, then stops
diminishingReturnsGrows with diminishing returns
piecewiseLinear with a plateau in the middle (plateauStartLevel, plateauFactor)

Example: classic exponential curve#

XP for level N = 100 × 1.15^(N−1), from level 1 to 100:

{
  "id": "xp-hero",
  "name": "Hero XP Curve",
  "mode": "preset",
  "preset": "exponential",
  "expression": "",
  "startLevel": 1,
  "endLevel": 100,
  "decimals": 0,
  "params": {
    "base": 100,
    "growth": 1.15,
    "offset": 0,
    "tierStep": 0,
    "tierMultiplier": 1,
    "capValue": 0,
    "capStrength": 0,
    "plateauStartLevel": 0,
    "plateauFactor": 1
  },
  "profile": "rpg"
}

Result: level 1 = 100 XP, level 10 = 351, level 50 = 108k, level 100 = 117M XP. (Probably too high — you'd adjust growth to 1.10 or use softCap.)

Simulation#

If you fill in simulationInput, the app automatically calculates:

  • Minutes per level — how long the player takes to level up given the input
  • Hours to milestones — cumulative time to reach levels 10, 25, 50, 100
  • Calendar days — useful for games that cap XP per session

Lets you answer "how many days does the player take to reach level 50 playing 1h/day?" without manual calculation.

Wizard#

The 📈 XP Balance page type creates this addon with sensible defaults (preset exponential, growth 1.15, levels 1–100). You adjust in the panel.

Who points to xpBalance#

  • craftTable.unlock.level.xpAddonRef — recipes unlocked by level.
  • skills.unlock.level.xpAddonRef — skills unlocked by level.
  • progressionTable (indirectly) — when your columns scale with level, the reference curve is often an XP one.

See also#