Library / SDK
smogon/damage-calc avatar
smogon/damage-calc

The Pokémon damage calculator, as a library and as a simulator's front end

Pokemon games damage calculator

540 stars505 forksTypeScriptMIT

At a glance

What is it?
The damage calculator that competitive players use is a piece of software with two halves that are usually confused for one thing. The first is a library that implements the damage formula of every generation of the games, and the second is an interface that lets a person set up a matchup. The interesting design in this repository is the line between them, and the rule that the data comes from the simulator rather than from the calculator.
Who is it for?
The damage calculator library fits a developer who wants the game's damage formula in their own tool, and it fits a player who wants an answer they can trust, because the data comes from the simulator that the competitive community itself uses. It does not fit somebody who wants a general game mechanics engine.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two things in one repository

The repository is the official home of the damage calculator that competitive players use, and its first job is to say what is inside. It houses both the package implementing the core damage formula mechanics for each generation, and the logic and the markup for the official interface.

That split is the whole design, and it is the right one. The damage formula is a piece of rules: a multiplier from the attacker's statistics, a multiplier from the defender's, a table of type effectiveness, a random roll within a range, and a series of generation-specific wrinkles about whether a status effect applies, whether a critical hit is possible, how a move's power is distributed across a spread move, and so on. Those rules change with every new release of the games, and they are what a competitive player is actually arguing about.

The interface is a different kind of artefact. It is a form, with dropdowns and sliders and a table of results, and its job is to let somebody express a matchup without knowing the formula. It also has to be right, because the numbers it shows are the numbers people plan around.

Separating them means the interface can change without touching the rules, and the rules can be used without the interface, and the rules can be published as a package for other tools to use. That last one is what the package is for, and the readme says the package provides a programmatic interface for computing damage ranges, that its code runs on either the server or the browser, and that it can be used as a building block for alternative interfaces or applications.

So the repository is three things: a library, an interface, and a small amount of shared data that both need. And the data is the interesting part, because it is the part that has to agree with something outside this repository.

The interface of the calculation

The calculation function takes four things, and the readme explains each of them in a way that tells you how the library thinks about scope.

The first is a generation, which contains the information about which damage mechanics to apply and where all the data about that generation can be found. The second and third are the attacker and the defender, and the note about them is important: only the relevant attributes are required, and everything else has sensible defaults. So a calculation does not need a complete picture of a creature; it needs the item, the nature, the effort values and the boosts, and the rest can be left out.

The fourth is the move, and it takes a generation too, so that the move's data is scoped to the right ruleset.

And there is an optional fifth: a field object, which carries the state of each side, which is how the light screen, the reflect, the weather and the terrain get into the calculation.

The example is worth reading because it shows the shape of the data.

ts
import {calculate, Generations, Pokemon, Move} from '@smogon/calc';

const gen = Generations.get(5); // alternatively: const gen = 5;
const result = calculate(
  gen,
  new Pokemon(gen, 'Gengar', {
    item: 'Choice Specs',
    nature: 'Timid',
    evs: {spa: 252},
    boosts: {spa: 1},
  }),
  new Pokemon(gen, 'Chansey', {
    item: 'Eviolite',
    nature: 'Calm',
    evs: {hp: 252, spd: 252},
  }),
  new Move(gen, 'Focus Blast')
);

So a creature is a name plus a handful of attributes, and the attributes that are not supplied are left alone. The generation can be given as a number, which is the shortcut for the case where you do not care about the data layer and want the one the package ships with.

And the result is the object you actually wanted, with methods for fetching the damage rolls, the ranges, the descriptions, and the recoil or drain information. The distinction between a roll and a range is worth pausing on, because it is the difference between the mechanics and the presentation. A roll is a single outcome of the random step; a range is the minimum and maximum. The result object gives you both, plus the numbers you need to explain a result in prose, which is what the interface does with it.

The `Field` object is the last piece, and it is the one that shows the library is not just about the attacker's statistics. A screen in the way, a reflect, the terrain, the weather: these are all properties of the situation rather than of either creature, and a damage calculation that ignored them would be wrong often enough to matter.

The data, and the rule about where it comes from

The package ships with all the data the calculation needs, exposed through a generations object that comes from the package's own data layer, and the readme offers a shortcut: the generation argument can be a number, and the library will look up the corresponding object from the data it ships with.

Then there is the rule, and it is the most important sentence in the document.

The data must be kept in sync with the simulator. If there is an issue with the calculator's data, it should be fixed in the simulator first.

That is a data lineage, and it is worth understanding why it runs in that direction. The simulator is the tool the competitive community actually plays on, and the damage formula implemented in the calculator is the same formula the simulator uses. The simulator is upstream; the calculator is a presentation of it. So when the two disagree, the calculator is wrong, and the fix belongs where the rules live rather than in the copy of the rules.

The readme also says that manual edits to the data files are generally not a good idea, and that in future the files are likely to be generated programmatically. Both of those sentences are signs of a project that has been bitten by hand-edited data, which is the normal way a copy of something drifts from the original: somebody changes a value in one place and forgets the other.

And then there is the escape hatch for the case where shipping two copies of the data is a problem. The adaptable entry point works with any data layer that implements the library's generations interface, and that interface is a subset of the one in the fuller data package. So an application that wants the competitive data and does not want to ship the calculator's own copy can use the fuller one through the adaptable entry point, and the two will not both reach the user.

That is a well-designed seam. The library does not insist on its own data, it insists on an interface, and the data package satisfies a superset of it.

The browser, and a load order that matters

The browser story is short and has one sharp edge in it.

The recommended way to use the package in a browser is to configure your bundler to minimise it and package it with the rest of the application. If you do not use a bundler, a convenience pre-minified build is included, and you depend on it in a script tag, after which the calculator is accessible as a global.

And then the requirement: you must also provide a generations implementation, either the package's own data layer or an alternative one, and you must load the data layer before the calculator.

html
<script src="./node_modules/@smogon/calc/data/production.min.js"></script>
<script src="./node_modules/@smogon/calc/production.min.js"></script>

The order is not arbitrary and the reason is architectural. The data layer is what the calculator consults for statistics, types, moves and the mechanics of each generation, and the calculator is built on top of it. Loading them the other way round means the calculator is constructed before the thing it depends on exists, and you get a failure that says nothing useful.

The unpkg shortcut in the installation section shows the same ordering in a form you can paste, and the comment next to it explains the subtlety: including the data code as well is what fulfils the calculator's data layer requirement, and a fuller data layer can be used instead. So even the copy-and-paste path has a decision in it, and the documentation says what the decision is.

The bundler recommendation is the better advice, and it is not only about size. With a bundler, the data layer and the calculator are resolved as modules, so the order is the module system's problem rather than yours, and a page that loads two script tags has to get the order right by hand. The convenience build exists for the case where a bundler is not wanted, and the documentation is careful to say what the cost of that choice is.

The interface, and the build that produces it

The interface layer is written in plain JavaScript and HTML, and the readme explains how to run it locally, which is a short set of steps with one idea running through them.

First the dependencies, installed at the top level and again inside the calculator directory, which creates a modules folder under each. The repetition is because the two halves are separate packages, and the readme says to install without the calculator directory the first time so that the two are not confused.

Then a build command run from the root, which compiles the package from TypeScript to JavaScript that can be run in the browser, compiles the templated HTML, and copies everything into a top-level distribution folder. The result is opened directly from the file system in a browser.

And the point of the whole exercise is in the note about what to run when. If you change something in the calculator package, you must run the full build, because that is what recompiles and recopies. If you change the HTML or the JavaScript in the interface source, you can run a view-only build, which is faster because it skips the compilation of the package you did not touch. The full build works too, it says, but it is slower for an unnecessary reason.

That is a small but genuinely useful thing for a repository with two build targets, and it is the kind of note that only appears in a project where somebody has actually worked on it. The same discipline shows up in the script names in the manifest, where the compile, bundle, build and build-interface commands are all separate, and the test command runs the package's tests, then a build, then the linter over the interface source with a cache.

So the testing story is: the rules are tested by the package's own suite, the interface is checked by the linter, and the whole thing is rebuilt before the tests are declared green. That is a slower test command than the minimum, and it is the right one for a project whose output is a page of numbers that people argue about.

The repository, and the publishing path

The top level of the repository tells you how the two halves are put together and published.

There is a calculator directory, which is the package, and a source directory, which is the interface. There is a build script at the root that drives both, and a bundler configuration next to it. There is a server file, which is the local server for the interface, and a publish directory, which is the machinery for putting the package on the registry. There is an import directory, which is the part of the data pipeline that brings the data over from the simulator, and a smoke test directory, which is a check that the published artefact actually works rather than merely compiles.

That import directory is the physical counterpart of the rule about the data lineage. The data is not hand-maintained in this repository; it comes from somewhere else, and the directory that gets it in is where the pipeline lives. The readme's note that the data files are likely to be generated programmatically in future is a statement about that directory's future, not about the files' future.

There is a task list file, which is the repository's own backlog in the open, and a container file with a build ignore file beside it, which is how the project serves the interface as a service.

And the manifest is a good summary of the shape. The package is private, so the root is not published; the homepage points at the live calculator; the licence is MIT. The development dependencies are three: a linter, a web framework for the local server, and a tool for running the same script across the sub-packages, which is what makes the lint, fix, compile and test commands fan out into the calculator directory without duplicating them.

The optional dependencies are the interesting list, and they are a set of compiler plugins for the language the interface is written in. Almost all of them exist to support one syntax feature: optional chaining, class properties, object rest and spread, optional catch binding, and the rest. That is a list that says the interface uses a modern dialect throughout and that the toolchain is configured to compile it for the browsers the project supports, which for a page that competitive players open on their own machines is a real consideration rather than a formality.

So the repository is a small, carefully organised piece of infrastructure: a rules package with a published interface, an interface built in plain browser JavaScript, a data pipeline that keeps the two in step with the simulator, and a publish path that puts the package where other tools can use it.

Editorial conclusion

The damage calculator library fits a developer who wants the game's damage formula in their own tool, and it fits a player who wants an answer they can trust, because the data comes from the simulator that the competitive community itself uses. It does not fit somebody who wants a general game mechanics engine. Verify first which generation you are targeting, because the mechanics differ between them and the library takes the generation as a parameter for that reason, that the data layer you load matches the data layer the library expects, since the browser build has a strict load order, and that you do not edit the data files by hand, because the readme says the source of truth is the simulator and that the data is likely to be generated programmatically in future.

Frequently asked questions

What does the Pokémon damage calculator repository contain?

Two things: a published package that implements the core damage formula mechanics for every generation, and the logic and markup for the official interface. The package provides a programmatic interface for computing damage ranges, runs on either the server or the browser, and is meant to be used as a building block for other interfaces.

What arguments does the damage calculation take?

A generation, which says which mechanics to apply and where the data comes from; the attacker and the defender, each of which needs only the relevant attributes and defaults the rest; the move, which also takes a generation; and optionally a field object carrying the state of each side, which is how screens, terrain and weather enter the calculation. The result exposes the damage rolls, the ranges, the descriptions and the recoil or drain information.

Where does the damage calculator's data come from?

From the simulator the competitive community plays on, and the readme says the data must be kept in sync with it, that an issue in the calculator's data should be fixed in the simulator first, and that manual edits to the data files are generally a bad idea because they are likely to be generated programmatically in future.

Can I use the damage calculator with a different data layer?

Yes, through the adaptable entry point, which works with any data layer implementing the calculator's generations interface, and that interface is a subset of the one in the fuller data package. That lets an application use the competitive data without shipping a second copy of the same data to its users.

How do I use the damage calculator in a browser without a bundler?

Include the pre-minified build in a script tag, after which the calculator is available as a global, and also provide a generations implementation. The data layer has to be loaded before the calculator, and the readme shows the two script tags in the order that works.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. smogon/damage-calc on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/smogon-damage-calc.svg)](https://hysenlabs.com/projects/smogon-damage-calc)

Was this useful?