mrkeyoor.com_
Sat 08 Aug 22:49 UTC
npmUtilsupdated 08 Aug 2026

complex.js

Complex.js is a focused JavaScript implementation of complex-number arithmetic. It constructs values from real and imaginary components, polar coordinates, arrays, numbers, or strings such as `3 + 4i`, then provides immutable-style methods for arithmetic, roots, powers, logarithms, trigonometry, conjugates, magnitude, and phase. It is a small numerical building block rather than a general algebra system: you get one complex-number type, constants, parsing, formatting, and TypeScript declarations, but no matrices, units, symbolic expressions, arbitrary precision, or equation parser.

Verdict

Install Complex.js when you need a compact, typed complex-number value with a wide elementary function set. Pick a broader mathematics package for matrices or expressions, and verify branch and floating-point behavior before using it in sensitive numerical code.

API stability4/5The README presents a compact constructor-and-method API that has retained its basic shape across the 2.x line, and version 2.4.3 still supports object, array, string, numeric, and two-argument construction. The package exports both CommonJS and ESM entry points and ships declarations for the same methods. Stability is not a perfect 5 because numerical edge behavior can change without a type-level signal, as recent commits mention parsing and numerical-stability changes, and the documented `acot` branch convention is necessarily observable.
Docs4/5The README documents every constructor form, public attribute, arithmetic operation, trigonometric family, constant, conversion, installation path, and the important `acot` branch choice. It also includes quadratic-root and geometric examples. The weak spots are precision and error contracts: malformed-string behavior, immutability, NaN and Infinity rules, and the fixed tolerance are clearer in the source and tests than in the main narrative, so serious numerical users still need to inspect implementation evidence.
Maintenance4/5The repository is not archived, version 2.4.3 was published on November 14, 2025, and the matching last push was a commit titled `Improved numerical stability`. Other commits in 2024 and 2025 addressed parsing, complex literals, documentation, and code quality. That is credible maintenance for a small mature library. The score stops at 4 because development is low-volume, GitHub reports 9 open issues and PRs, and there is no published release feed explaining compatibility or numerical changes in a formal changelog.
Ecosystem4/5The npm download endpoint reports 3,191,762 downloads for July 31 through August 6, 2026, and the README says Complex.js underpins Polynomial.js and is used by mathjs. It supports Node.js, ESM bundlers, CommonJS, TypeScript, and direct browser scripts without runtime dependencies. Its 253 GitHub stars are modest and the ecosystem is intentionally narrow, so it does not offer the integrations, plugins, or domain breadth of mathjs despite its substantial transitive usage.

Use it if

  • You need complex arithmetic in JavaScript without adopting a full mathematics suite
  • You want the same zero-dependency package in ESM, CommonJS, TypeScript, Node.js, or a direct browser script
  • Your inputs arrive in several practical forms, including strings, rectangular pairs, arrays, and polar coordinates
  • You need elementary complex functions such as powers, logarithms, roots, circular trig, and hyperbolic trig
Skip it if

Setup reality

Installation is only `npm install complex.js`; there are no runtime dependencies, peer dependencies, credentials, native extensions, generated files, or configuration. Version 2.4.3 exposes separate CommonJS and ESM builds through `exports`, and its `complex.d.ts` file supplies TypeScript types, so `require('complex.js')` and `import Complex from 'complex.js'` both work. A browser can load `dist/complex.min.js` directly, but the README's script-tag example assumes you have copied that file or otherwise arranged a URL; npm does not configure a CDN or global asset path for you. The first surprise is semantic rather than operational. Methods return new `Complex` objects, so assign the result of `add`, `mul`, or `round` instead of expecting mutation. String parsing throws `SyntaxError: Invalid Param` for malformed input and accepts a library-specific grammar, not general mathematical expressions. Objects must contain one of the documented complete pairs such as `{ re, im }` or `{ abs, arg }`, and the README warns that their values must be Numbers. Results use ordinary floating-point, `equals()` applies the fixed `Complex.EPSILON` value of 1e-15, `toString()` also hides components below that threshold, and branch cuts matter for inverse trig and logarithms. Infinity is modeled as a library sentinel rather than preserving every signed component. Those choices are reasonable, but tests around branch boundaries, zero, NaN, Infinity, and rounding belong in any numerical application that depends on exact conventions.

Patterns

Construct rectangular complex valuesconstruct-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

Calling `Complex(...)` without `new` is supported. Object fields must be numbers, and extra fields are not preserved.

Parse a complex-number stringparse-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 understands complex literals, not arbitrary expressions. Invalid text throws `SyntaxError: Invalid Param`.

Construct from polar coordinatesuse-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());

Angles are in radians. `{ r, phi }` is also accepted, but do not mix names from the two documented pairs.

Chain arithmetic without mutationbasic-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

Arithmetic methods return new objects. The original value remains unchanged unless you assign the result back.

Find both roots of a real quadraticsolve-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']

Unlike `Math.sqrt`, `Complex(...).sqrt()` keeps negative discriminants inside the complex domain.

Read magnitude and phasemagnitude-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()` uses the principal angle returned by `atan2`, in radians, so the result lies on the usual branch around the negative real axis.

Compute a conjugate and reciprocalconjugate-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

The reciprocal of zero is the library's complex Infinity sentinel; test `z.isZero()` first when that should be an application error.

Use roots, powers, exponentials, and logspowers-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());

Complex logarithms and non-integer powers use principal branches. Values across a branch cut can jump even when inputs are close.

Evaluate complex trigonometric functionstrigonometric-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());
}

Inverse functions follow documented principal-branch choices. In particular, `acot` uses a textbook real-axis convention with a jump on the negative real axis.

Rotate a 2D point around a centerrotate-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]

This treats the real component as x and the imaginary component as y. Round only for display or tolerance checks, not after every operation.

Compare results with the built-in tolerancecompare-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 the fixed absolute tolerance `Complex.EPSILON`, which is 1e-15. Use an application-specific absolute or relative tolerance when scales vary.

Serialize components and reject non-finite resultsserialize-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` when the imaginary component is nonzero. Store `re` and `im` explicitly instead of relying on numeric coercion or formatted strings.

Alternatives

PackageRegistryPick it when
mathjsnpmChoose it when complex values are one part of a larger job involving matrices, units, expressions, or symbolic work
@stdlib/complex-float64npmChoose it for a modular scientific-computing API built around explicit double-precision complex functions
numbersnpmChoose it when you also need broader numerical algorithms and can accept an older, less focused package