# Nerdamer 2.0: a TypeScript rewrite of the JavaScript symbolic math library

> Nerdamer 2.0 replaces the string parsing internals of the long-standing JavaScript CAS with a TypeScript implementation, keeps the familiar string-based API, and adds typed entry points for the core, algebra, calculus, solve, structures, assumptions and parser modules. It is Apache-2.0 licensed and published on npm under the `latest` tag, but the last tagged release on the repository is still v1.1.12 from 2021.

**jiggzson/nerdamer** — a symbolic math expression evaluator for javascript

- Repository: https://github.com/jiggzson/nerdamer
- Website: http://www.nerdamer.com
- Stars: 561 · Forks: 82
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jiggzson-nerdamer

## What Nerdamer 2.0 is, and who reaches for it

Nerdamer is a symbolic mathematics library and computer algebra system for JavaScript and TypeScript. Nerdamer 2.0 is a TypeScript rewrite of Nerdamer, and the README is clear that the familiar string-based API remains available while the package also exposes typed modules for direct use of the symbolic engine, algebra, calculus, solving, structures, assumptions, parser APIs and advanced algorithms. The branch prepares 2.0.0.

The audience is a developer who needs computed algebra inside an application rather than a notebook. A product that shows the derivative of a user entered formula, a form that simplifies what the user typed, or a bot that answers an equation can call a CAS in the browser without a Python service or a WebAssembly runtime. That is the space Nerdamer occupies, and the 2.0 work is about making that usable from TypeScript with types rather than about adding new mathematics.

The core interaction is a callable. You pass a string and get back a symbolic entity, and you ask that entity for its text:

```javascript
const nerdamer = require('nerdamer');

const expression = nerdamer('x^2+2*(cos(x)+x*x)');
console.log(expression.text());
// 2*cos(x)+3*x^2
```

The same call in TypeScript, with an expansion and a sorted output:

```typescript
import nerdamer from 'nerdamer';

const expression = nerdamer('(x+1)^3').expand();
console.log(expression.text({ sort: true }));
// x^3+3*x^2+3*x+1
```

The output is a Nerdamer notation string, not a JavaScript number, which is the point. Nothing is evaluated until you ask for it.

## Installing the package and wiring it into a TypeScript build

Installation is a single npm command, and the README states that Nerdamer 2.0 is published under npm's `latest` tag:

```bash
npm install nerdamer
```

That detail matters more than it looks. Publishing 2.0.0 under `latest` means an existing project that runs `npm install nerdamer` after a release will pull the rewrite rather than staying on the 1.x line, and the package version in the repository is 2.0.0 with a publishConfig tag of `latest`.

The TypeScript note in the README is short and easy to miss: TypeScript projects compiling to CommonJS should enable `esModuleInterop`. That is the setting that makes the default import in the example above resolve correctly, and a project that hits it without the flag gets an import error rather than a CAS failure.

The package manifest gives you the rest of the shape. The main entry is dist/bundle.js and the types entry is index.d.ts, and the files array ships dist, output, docs-data, index.d.ts, README.md, BREAKING_CHANGES.md, docs/MIGRATION_FROM_1X.md and docs/RELEASE_NOTES_2.md. So the migration documentation travels inside the package rather than only living on the site.

For a browser without a bundler, the README points at the two distribution bundles. dist/bundle.js is the complete Nerdamer package and dist/parser.js is a parser-focused browser bundle, and the markup to load the full one is:

```html
<script src="./node_modules/nerdamer/dist/bundle.js"></script>
<script>
    console.log(nerdamer('factor(x^2-1)').text());
</script>
```

The parser bundle exposes `nerdamerParser` rather than the complete `nerdamer` API, so the two bundles are not interchangeable and the global name changes with it.

## Splitting the CAS into typed module entry points

The structural change in 2.0 is that the monolithic library can be loaded in pieces. The normal `nerdamer` entry point assembles the complete CAS, registering the parser and core, the math functions, and the algebra, calculus and solving domains together.

The package exports the complete callable API plus typed module entry points:

```typescript
import nerdamer from 'nerdamer';
import { Expression, Rational, type TextOptions } from 'nerdamer/core';
import { Polynomial, gcd, lcm, partfrac } from 'nerdamer/algebra';
import { diff, integrate, limit } from 'nerdamer/calculus';
import { solve, PolynomialSolver } from 'nerdamer/solve';
import { Matrix, Vector, ValuesSet } from 'nerdamer/structures';
import { assume, clearAssumptions } from 'nerdamer/assumptions';
import { Parser } from 'nerdamer/parser';
```

Additional supported entry points are `nerdamer/advanced` and `nerdamer/debug`.

That list is the practical payoff of the rewrite. A tool that only needs to parse and print expressions does not need the solver, and a bundle that only needs the parser does not need the higher-level algebra, calculus and solver domains. The parser entry point is the clearest case:

```typescript
import { Parser } from 'nerdamer/parser';

const expression = Parser.parse('sqrt(x^2+1)');
console.log(expression.text());
```

The parser entry point contains the symbolic parser, scripting, assumptions, structures, complex and polynomial operations, and general math functions, but not the higher-level domains. The same split shows up in the browser builds, with a complete bundle and a parser-only bundle.

The `assumptions` module deserves a note, because it is a mechanism rather than a namespace. `assume` and `clearAssumptions` let you attach conditions to the symbolic session, which is how a CAS is told that a symbol is positive before it attempts a simplification, and since settings remain shared by the package instance, that state persists for the lifetime of the instance.

## Parser results are typed entities, not one universal Expression

The most consequential breaking change is visible in the return type. `nerdamer(...)` returns the Nerdamer entity represented by the input. Scalar symbolic input normally returns an `Expression`, but equations and structured notation can return `Equation`, `Vector`, `Matrix`, `Collection`, `ValuesSet` or `Dictionary`.

```javascript
const scalar = nerdamer('x^2+1');
const equation = nerdamer('x^2=1');
const vector = nerdamer('[x, y, 3]');
```

The README is explicit that this is intentional: Nerdamer 2.0 does not wrap every parser result in a compatibility `Expression` object. Code written against 1.x that assumed `.text()` on everything still works for scalars, but code that assumed a uniform Expression type now has to branch, or use the typed classes exported from `nerdamer/structures`, which include `Matrix`, `Vector` and `ValuesSet`.

The structured values are first-class parser entities in their own right:

```javascript
nerdamer('matrix([1,2],[3,4])').text();
nerdamer('[1,x,3]').text();
nerdamer('{1,2,x}').text();
```

Set and vector notation that was a string convention in 1.x is now a data structure, and that is why the structures module exists as a separate entry point.

Substitution follows from the same design. Values supplied to `nerdamer(...)` are substitutions and they do not imply numerical evaluation:

```javascript
const substituted = nerdamer('x^2+cos(x)', { x: 6 });
const evaluated = substituted.evaluate();
```

The substitution is a symbolic replacement, and `.evaluate()` is the separate step that turns it into a number. Values can themselves be symbolic expressions, so `nerdamer('x^2+1', { x: 'y+1' })` substitutes one symbol for another rather than computing anything.

## Output formatting, TeX and the deprecated conversion alias

Output is controlled per call, which matters when you are rendering results into a UI. `text()` returns Nerdamer notation and the term ordering can be requested per call:

```javascript
const expression = nerdamer('x^2+2*x+1');

expression.text();
// 1+2*x+x^2

expression.text({ sort: true });
// x^2+2*x+1
```

The full package also supplies `toText()` and TeX conversion:

```javascript
expression.toText();
expression.toTeX();
nerdamer.convertToTeX('sqrt(x^2+1)');
nerdamer.convertFromLaTeX('\\frac{x^2+1}{2}');
```

`convertToLaTeX()` remains as a deprecated compatibility alias, so a 1.x call site will keep working but is on notice, and `convertFromLaTeX` gives you a way to accept TeX from a user and get a symbolic entity back.

Scientific formatting is available without changing the underlying expression:

```javascript
nerdamer('1200').text({ scientific: 4 });
// 1.200e3
```

One caveat on the browser bundle: the TeX conversion helpers come from the full package, so a build that only loads dist/parser.js does not have them. The parser bundle gives you `nerdamerParser` and the parser domain, and the README does not claim the full conversion API is present there.

## Algebra, calculus and the rewritten polynomial path

The mathematics is reached through a flat function API on the main entry point:

```javascript
nerdamer.expand('(x+1)^4').text();
nerdamer.factor('x^4-1').text();
nerdamer.simplify('sin(x)^2+cos(x)^2').text();
nerdamer.gcd('x^2-1', 'x^2-2*x+1').text();
nerdamer.partfrac('1/(x^2-1)', 'x').text();
```

The README states that the 2.0 algebra implementation includes the rewritten polynomial GCD and factorization path, partial fractions, polynomial solving support and direct polynomial APIs. The direct polynomial API is what the `Polynomial`, `gcd`, `lcm` and `partfrac` exports from `nerdamer/algebra` are for, and it is a different route to the same results than the string based calls.

Prime factorization has two explicit forms, a list and a map:

```javascript
nerdamer.pfactor(100).text();
// [2,2,5,5]

nerdamer.pfactord(100).text();
// {2=>2,5=>2}
```

Calculus works the same way:

```javascript
nerdamer.diff('x^3+sin(x)', 'x').text();
nerdamer.integrate('x^2', 'x').text();
nerdamer.limit('sin(x)/x', 'x', '0').text();
nerdamer.laplace('t', 't', 's').text();
```

and the same operations are available in Nerdamer notation inside a string, for example `nerdamer('diff(x^3,x)').text()`. That dual surface is deliberate and it is also the main compatibility trap, because the notation parser and the function API do not always grow in step.

## Solving, the scripting layer and runtime functions

Single-variable solving returns a `SolutionSet`:

```javascript
const roots = nerdamer.solve('x^2-1', 'x');
console.log(roots.text());
```

System solving returns Nerdamer structures rather than legacy JavaScript arrays:

```javascript
const solutions = nerdamer.solveSystem(
    ['x+y=3', 'x-y=1'],
    ['x', 'y']
);

console.log(solutions.text());
```

The name change is the thing to note. The legacy `solveeqs` name is retained as the compatibility alias for system solving, and `solveEquations` is not part of the 2.0 public API. Code that was written against a 1.x `solveEquations` helper has to change, while code on `solveeqs` keeps working.

Nerdamer notation also includes a symbolic scripting layer with assignments, user functions, local bindings, conditionals, loops, blocks, logical operations, `return`, `break`, `continue` and error-handling helpers:

```javascript
const result = nerdamer(`
    f(x):=x^2+1;
    f(12)
`);

console.log(result.text());
```

Control-flow operations that intentionally produce no mathematical value use an internal null signal, and the README notes that this signal is distinct from legitimate values such as an empty vector and is not exposed as a normal parser entity. That distinction is a small but real design detail, because it means an empty result from a loop is not confused with an empty vector.

Finally, you can add your own runtime functions, constants and variables to the shared instance:

```javascript
nerdamer.setFunction('line', ['x', 'm', 'b'], 'm*x+b');
nerdamer.setConstant('g', 9.81);
nerdamer.setVar('a', 5);
```

Settings remain shared by the package instance, which is convenient and also means state can leak between requests in a server context, since the module is a singleton.

## Where Nerdamer 2.0 is the wrong tool, and the licence and upgrade position

The first limit is the upgrade boundary itself. The README is explicit that Nerdamer 2.0 is a rewrite rather than an incremental release, and the package version, the typed entry points, the non-Expression parser results and the removed `solveEquations` name all point the same way. The repository ships BREAKING_CHANGES.md, docs/MIGRATION_FROM_1X.md and docs/RELEASE_NOTES_2.md for this, and a 1.x codebase with string handling around solver results will need work rather than a version bump.

The second limit is the release history. The recent releases listed for the repository are v1.1.12 from 2021-07-06, v1.1.11 from 2021-05-24 and v1.1.9 from 2021-03-26, and the last push was on 2026-10-17. So the 2.0 work is on master and published to npm under the `latest` tag, but the tagged release history on the repository still belongs to the 1.x line. If you pin a version from the git tags rather than from npm, you are pinning the old implementation.

The third limit is scope. The documentation describes a symbolic CAS for JavaScript and TypeScript and does not mention a numeric array library, a plotting system or a statistics package, and if you need dense numerical work rather than symbolic manipulation this is the wrong dependency.

The licence is Apache-2.0, with LICENSE.md in the repository root and a CITATION.cff file alongside it. Apache 2.0 is permissive, includes an express patent grant and requires you to keep the licence and notices when you redistribute, which matters if you ship a bundle into a product, and it is not legal advice, so read LICENSE.md yourself.

Compare this with a Python-hosted CAS such as SymPy reached over an API. The difference in approach is that Nerdamer runs in the same process and the same language as your application, so there is no service to run, no network hop and no serialization of expressions, and in exchange you are limited to what the JavaScript implementation covers and you accept a rewrite of the API in 2.0. Against a WebAssembly port of a desktop CAS, the trade goes the other way: you get a smaller runtime and a JavaScript-native API, and you give up the breadth of a system built for symbolic research.

## Conclusion

Nerdamer 2.0 suits a TypeScript codebase that needs symbolic algebra in the browser or in Node without shipping a WebAssembly runtime, and it suits a project willing to read BREAKING_CHANGES.md before upgrading from 1.x. It does not suit an application that expects every parser result to be an Expression, that relies on the removed `solveEquations` name, or that needs the 1.x release line, since the newest tag on the repository is v1.1.12 from 2021-07-06 and the 2.0 work sits on master. Before you adopt it, install with `npm install nerdamer`, check that your TypeScript build enables `esModuleInterop` if it compiles to CommonJS, and read docs/MIGRATING_FROM_1X.md to see which of your call sites change.

## FAQ

### What changed in Nerdamer 2.0 compared with 1.x?

Nerdamer 2.0 is a TypeScript rewrite. The string-based API remains, and the package adds typed module entry points for the core, algebra, calculus, solve, structures, assumptions and parser APIs. Parser results are no longer all wrapped in an Expression, and `solveEquations` is not part of the 2.0 public API, with `solveeqs` retained as the alias.

### How do I install Nerdamer and use it in a TypeScript project?

Run `npm install nerdamer`, then import the default export and call it with an expression string. TypeScript projects compiling to CommonJS should enable `esModuleInterop`. Nerdamer 2.0 is published under npm's `latest` tag, and the package version in the repository is 2.0.0.

### What is the difference between the nerdamer and nerdamer/parser entry points?

The normal `nerdamer` entry point assembles the complete CAS, registering the parser and core with the math functions and the algebra, calculus and solving domains. The `nerdamer/parser` entry point holds the symbolic parser, scripting, assumptions, structures, complex and polynomial operations and general math functions without the higher-level algebra, calculus and solver domains, and it is exposed in the browser as dist/parser.js under the global `nerdamerParser`.

### Does nerdamer('x^2+1', { x: 6 }) evaluate the expression?

No. Values supplied to `nerdamer(...)` are substitutions and do not imply numerical evaluation, so you call `.evaluate()` on the result to get a number. The substituted value can also be symbolic, for example `{ x: 'y+1' }`.

### Is the project actively maintained?

The repository is not archived and the last push was on 2026-10-17, and the README states that this branch prepares 2.0.0, with Nerdamer 2.0 published under npm's `latest` tag. The most recent tagged releases on the repository are from the 1.x line, with v1.1.12 dated 2021-07-06.

## Sources

- [jiggzson/nerdamer on GitHub](https://github.com/jiggzson/nerdamer)
- [License: Apache-2.0](https://github.com/jiggzson/nerdamer/blob/master/LICENSE)
- [Project website](http://www.nerdamer.com)
- [README](https://github.com/jiggzson/nerdamer/blob/master/README.md)
- [Releases](https://github.com/jiggzson/nerdamer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jiggzson-nerdamer
