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.
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
| Install | ✓ · 0.6s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package with exports map |
| Browser | 2.6 KB | gzipped (9.3 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 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
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
- You need matrices, units, statistics, symbolic expressions, or an expression parser; Complex.js exposes a single numerical value type
- You require decimal or arbitrary precision: version 2.4.3 stores both components as ordinary JavaScript Number values
- You need selectable inverse-trig branches: the README fixes acot to the textbook real range and documents its jump across the negative real axis
- Untrusted object input may contain strings in re, im, abs, or arg: the README says those fields must be Number values to avoid undefined behavior
- Your domain needs signed component infinities: division by zero resolves to the package's single Complex.INFINITY sentinel
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 4Version 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 + 3iadd, 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()); // 1inverse() 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
| Package | Registry | Pick it when |
|---|---|---|
| mathjs | npm | Use it when complex values share a program with matrices, units, expressions, or symbolic operations |
| @stdlib/complex-float64 | npm | Use it for explicit double-precision complex functions inside the modular stdlib scientific stack |
| numbers | npm | Use 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.

