mrkeyoor.com_
Wed 23 Sept 00:33 UTC
npmUtilsupdated 22 Sept 2026

complex.js review

Our test install found a compact numerical type rather than a general math toolkit. Complex.js 2.4.3 represents one complex value with real and imaginary Number fields, accepts rectangular, polar, array, numeric, and string inputs, and returns new values from arithmetic operations. Its API covers roots, powers, logarithms, circular and hyperbolic functions, magnitude, phase, conjugates, parsing, and formatting. The 9.3 KB minified browser bundle leaves matrices, units, symbolic algebra, arbitrary precision, and expression evaluation to larger packages.

Verdict

Complex.js 2.4.3 installed in 0.6 seconds and produced a 9.3 KB minified bundle in our sandbox, with no dependencies or audit findings. It is a sensible small choice for Number-based complex arithmetic, provided your tests pin its tolerance and branch conventions.

We installed it

Lab card: what happened when we installed complex.jsScreenshot of complex.js documentation
Install✓ · 0.6s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser2.6 KBgzipped (9.3 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does complex.js install cleanly?

Yes. In a fresh container with an empty cache, npm install complex.js finished in 0.6s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does complex.js add to a browser bundle?

2.6 KB gzipped (9.3 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does complex.js work with both ESM and CommonJS?

Yes. Both import 'complex.js' and require('complex.js') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does complex.js include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

complex.js or mathjs: which should you use?

mathjs: Use it when complex values share a program with matrices, units, expressions, or symbolic operations. Complex.js 2.4.3 installed in 0.6 seconds and produced a 9.3 KB minified bundle in our sandbox, with no dependencies or audit findings.

When should you not use complex.js?

You need matrices, units, statistics, symbolic expressions, or an expression parser; Complex.js exposes a single numerical value type

API stability4/5Version 2.4.3 keeps the same constructor forms and chainable value methods used throughout the 2.x documentation. The exports map provides matching ESM, CommonJS, and TypeScript entry points, while the public surface remains one class plus constants. Numerical behavior can still shift without a type error: the November 2025 repository update was specifically about numerical stability, and the observable acot branch rule needs tests around its discontinuity.
Docs4/5The README lists each constructor shape, public component, arithmetic method, trig family, constant, conversion, and browser or package-manager setup. It goes further than most small numerical libraries by explaining the chosen acot range and its negative-axis jump. Source and tests are still needed for the exact Invalid Param exception, immutable return behavior, fixed 1e-15 equality tolerance, and the treatment of division by zero and non-finite components.
Maintenance4/5The repository is active rather than archived, has 253 stars, and was last pushed on November 14, 2025 for a numerical-stability change that matches the npm 2.4.3 release. Work in 2024 and 2025 also touched parsing and complex literals. Nine open issues and pull requests is a small queue, though the project does not publish a detailed changelog that lets numerical users audit every behavioral adjustment between releases.
Ecosystem4/5npm counted 3,560,297 downloads for August 18 through August 24, 2026. The README identifies Complex.js as a basis for Polynomial.js and says mathjs uses it, which explains reach beyond direct adopters. It works in ESM, CommonJS, TypeScript, Node.js, and direct browser builds without dependencies. Its integrations remain deliberately narrow compared with mathjs, so download volume does not make it a complete mathematics platform.

Use it if

  • You need complex-number arithmetic but do not need the rest of a scientific mathematics suite
  • Your data arrives as literals such as 3-4i, rectangular pairs, arrays, or polar coordinates
  • You need roots, logarithms, powers, circular trig, and hyperbolic trig over complex inputs
  • One implementation must work through ESM, CommonJS, TypeScript, Node.js, and a browser bundle
Skip it if

Setup reality

Our fresh Node 22 install of complex.js 2.4.3 completed in 0.6 seconds. It put 1 package and 1 MB on disk, with 0 direct dependencies, 0 peer dependencies, and 0 npm audit findings. The package is 184 KB unpacked under the MIT license, bundles TypeScript declarations, and declares node: '*' as its engine range. require() and ESM import both worked through the exports map.

There are no credentials, native extensions, configuration files, or postinstall steps. The package publishes separate CommonJS and ESM builds. A browser build also exists, but a script tag still needs a real URL or a copied asset; npm does not arrange that path. Our whole-package esbuild check measured 9.3 KB minified and 2.6 KB gzipped.

Arithmetic methods allocate new Complex instances. z.add(1) does not modify z, so retain the returned value. The string parser understands the package's complex-literal grammar and throws SyntaxError with Invalid Param for malformed text. Objects must use a complete documented pair such as { re, im } or { abs, arg }; unrelated properties are discarded.

Version 2.4.3 still uses binary floating point. equals() applies the fixed Complex.EPSILON value of 1e-15, and toString() suppresses components below that threshold. log(), powers, roots, and inverse trig use principal-branch choices, while acot has the README's documented negative-real-axis discontinuity. Test zero, NaN, Infinity, branch boundaries, and the scale of your tolerance before using results in scientific or financial decisions.

Patterns

Create values from rectangular components construct-values

import Complex from 'complex.js';

const a = new Complex(3, 4);
const b = Complex({ re: -2, im: 0.5 });
const c = Complex([7, -1]);

console.log(a.re, a.im); // 3 4

Version 2.4.3 accepts calls with or without new. Numeric object fields are read, while extra object properties are dropped.

Turn a complex literal into a value parse-string

import Complex from 'complex.js';

function parseComplex(input) {
  try {
    return Complex(input);
  } catch (error) {
    if (error instanceof SyntaxError) return null;
    throw error;
  }
}

console.log(parseComplex('3 - 4i')?.toString());

The parser accepts complex-number text rather than general formulas. Bad input raises SyntaxError with the message Invalid Param.

Build a value from radius and angle use-polar-form

import Complex from 'complex.js';

const radius = 2;
const angle = Math.PI / 3;
const z = Complex({ abs: radius, arg: angle });

console.log(z.re, z.im);
console.log(z.abs(), z.arg());

Polar angles are radians. Use either the abs and arg pair or the r and phi pair without mixing their field names.

Keep the result of each arithmetic call basic-arithmetic

import Complex from 'complex.js';

const z = Complex(2, 3);
const result = z.add(4).mul(0, 1).div(2);

console.log(z.toString());      // 2 + 3i
console.log(result.toString()); // -1.5 + 3i

add, mul, and div return new Complex objects in 2.4.3. The original z stays at 2 + 3i in this example.

Retain complex roots of a real quadratic solve-quadratic

import Complex from 'complex.js';

function quadraticRoots(a, b, c) {
  if (a === 0) throw new RangeError('a must be nonzero');
  const discriminant = Complex(b * b - 4 * a * c).sqrt();
  return [
    Complex(-b).add(discriminant).div(2 * a),
    Complex(-b).sub(discriminant).div(2 * a),
  ];
}

console.log(quadraticRoots(1, 4, 5).map(String)); // ['-2 + i', '-2 - i']

Complex(...).sqrt() represents a negative discriminant as an imaginary result. The explicit a check avoids dividing by zero.

Convert rectangular data to magnitude and phase magnitude-phase

import Complex from 'complex.js';

const z = Complex(-3, 4);
const magnitude = z.abs();
const phaseRadians = z.arg();
const phaseDegrees = phaseRadians * 180 / Math.PI;

console.log({ magnitude, phaseDegrees }); // { magnitude: 5, phaseDegrees: ... }

arg() returns an atan2-based principal angle in radians. Convert to degrees only at the display boundary.

Calculate the conjugate and multiplicative inverse conjugate-inverse

import Complex from 'complex.js';

const z = Complex(1, 2);
console.log(z.conjugate().toString()); // 1 - 2i
console.log(z.inverse().toString());   // 0.2 - 0.4i
console.log(z.mul(z.inverse()).round(12).toString()); // 1

inverse() on zero returns Complex.INFINITY. Guard with isZero() when division by zero should stop the calculation.

Apply principal roots, powers, and logarithms powers-and-logs

import Complex from 'complex.js';

const root = Complex(-4).sqrt();
const squared = root.pow(2);
const roundTrip = Complex(2, 3).log().exp();

console.log(root.toString());         // 2i
console.log(squared.round(12).toString());
console.log(roundTrip.round(12).toString());

Version 2.4.3 uses principal complex branches. Nearby inputs on opposite sides of a branch cut can produce discontinuous outputs.

Evaluate circular and hyperbolic functions trigonometric-functions

import Complex from 'complex.js';

const z = Complex(1, 0.5);
const values = {
  sin: z.sin(),
  cos: z.cos(),
  tanh: z.tanh(),
  asin: z.asin(),
};

for (const [name, value] of Object.entries(values)) {
  console.log(name, value.toString());
}

The README fixes inverse-function branches. acot follows the textbook real range and jumps across the negative real axis.

Rotate a Cartesian point with multiplication rotate-point

import Complex from 'complex.js';

function rotate(point, center, radians) {
  const turn = Complex({ abs: 1, arg: radians });
  return Complex(point).sub(center).mul(turn).add(center);
}

const rotated = rotate([2, 1], [1, 1], Math.PI / 2);
console.log(rotated.round(12).toVector()); // [1, 2]

The real field acts as x and the imaginary field as y. Delay round() until display or comparison to avoid compounding rounding loss.

Choose a tolerance that matches your scale compare-results

import Complex from 'complex.js';

const calculated = Complex(0.1 + 0.2, 0);
console.log(calculated.equals(0.3)); // true

function closeTo(a, b, tolerance = 1e-10) {
  return Complex(a).sub(b).abs() <= tolerance;
}
console.log(closeTo(calculated, 0.3));

equals() uses Complex.EPSILON, fixed at 1e-15. A domain-specific relative or absolute comparison is safer when magnitudes vary.

Store the two numeric components explicitly serialize-and-guard

import Complex from 'complex.js';

function toRecord(value) {
  const z = Complex(value);
  if (!z.isFinite()) throw new RangeError('finite complex value required');
  return { re: z.re, im: z.im, label: z.toString() };
}

console.log(toRecord('2+3i'));

valueOf() returns null whenever the imaginary component is nonzero. Persist re and im instead of depending on coercion or toString formatting.

Alternatives

PackageRegistryPick it when
mathjsnpmUse it when complex values share a program with matrices, units, expressions, or symbolic operations
@stdlib/complex-float64npmUse it for explicit double-precision complex functions inside the modular stdlib scientific stack
numbersnpmUse it when a wider set of numerical algorithms matters more than a focused modern complex-number API

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.