Library / SDK
alexbol99/flatten-js avatar
alexbol99/flatten-js

flatten-js treats a polygon as a stack of orientable faces

Javascript library for 2d geometry

654 stars63 forksJavaScriptMIT

At a glance

What is it?
flatten-js is an MIT licensed JavaScript library for 2D geometry, and its most consequential design choice is that a polygon is a multi-polygon of nested, oriented faces, which makes boolean operations work but puts a validity obligation on the caller.
Who is it for?
flatten-js fits JavaScript and TypeScript projects doing computational geometry on the client, such as CAD-style editing, path analysis or hit testing, where its shape model, boolean operations and TypeScript definitions save more than they cost.
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 66 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

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

Editorial analysis

The shape vocabulary flatten-js works with

flatten-js is a library for manipulating abstract geometrical shapes, and the list of primitives it implements is short enough to hold in the head: Point, Vector, Line, Ray, Segment, Arc, Box, Polygon, Matrix and PlanarSet. Box is documented as usable as a rectangle, and Ray is the half-line counterpart to Line.

The operations layered on top are the practical part: finding intersections, checking inclusion, calculating distance, applying affine transformations and performing boolean operations. Transformations are uniform across the classes, with translate, rotate and scale available on all of them and chainable, so a shape can be moved and turned in one expression.

A deliberate non-goal is worth naming. The documentation says the library does not concern itself too much with visualisation, and then adds that every class implements an svg() method returning a string that can be inserted into an SVG container. So it hands you geometry and a way to serialise it for one rendering target, and it leaves the painting, the hit testing on screen and the scene graph to somebody else. The documentation also notes it works well with d3js and that bridges to other graphics libraries are possible.

A polygon is a multi-polygon, and faces must be orientable

The most consequential thing the documentation says about Polygon is that in this library it is actually a multi-polygon. A polygon is a collection of faces, which are closed oriented chains of edges of type Segment or Arc. The outermost face is called an island, a face inside it is called a hole, holes can contain inner islands, and the number of inclusion levels is unlimited.

Orientation is not decoration here. The documentation states that the orientation of islands and holes matters for the calculation of relationships and boolean operations, and that holes should have orientation opposite to islands. A polygon whose faces are not orientable, meaning they have self-intersections, or whose faces overlap each other, is not a valid input for those operations. The method isValid() checks whether the polygon fits those rules, so validity is something you verify rather than something the constructor guarantees.

The constructor is generous about input. It accepts an array of Segment or Arc instances forming closed chains, an array of shapes as JSON objects, an array of Point vertices, an array of numeric pairs, or instances of Circle or Box. Once built, the API covers area, addFace, deleteFace, addVertex, cut with a multiline, findEdgeByPoint, contains, transform and reverse, and splitToIslands which returns an array of islands with their holes.

Installing the package and creating the first shapes

The package is published to npm as @flatten-js/core, and the install command in the documentation is:

bash
    npm install --save @flatten-js/core

The classes can then be imported by name:

javascript
import {Point, Vector, Circle, Line, Ray, Segment, Arc, Box, Polygon, Matrix, PlanarSet} from '@flatten-js/core';

There is a default import for the whole namespace, which you destructure if you prefer:

javascript
import Flatten from '@flatten-js/core'
const {Point, Vector, Circle, Line, Ray, Segment, Arc, Box, Polygon, Matrix, PlanarSet} = Flatten;

Several classes also have lowercase shortcuts, documented as a way to avoid the new constructor:

javascript
import {point, vector, circle, line, ray, segment, arc, polygon, matrix} from '@flatten-js/core';

The example in the documentation puts those together, building two segments and a circle and asking for an intersection:

javascript
    // extract object creators
    import {point, circle, segment} from '@flatten-js/core';

    // make some construction
    let s1 = segment(10,10,200,200);
    let s2 = segment(10,160,200,30);
    let c = circle(point(200, 110), 50);
    let ip = s1.intersect(s2);

The same snippet runs in NPM RunKit, and the repository carries examples for nodejs, for a browser using a script tag with the unpkg loader, and for a React application.

Three different things called intersect

One naming trap in the library deserves its own paragraph, because getting it wrong produces a silent logic error rather than an exception.

All classes have an intersect method that returns an array of intersection points when two shapes intersect, or an empty array otherwise. The documentation notes there is no predefined order of the points in that array. That is the geometric query.

Alongside it sit two more. BooleanOperations.intersect() performs a boolean intersection of polygons, which is a new shape rather than a list of points. Relations.intersect() is a logical predicate that answers whether two shapes intersect at all, returning a yes or no rather than a geometry. The documentation asks readers not to be confused by these three names sharing a word.

This is a naming problem worth internalising before writing code, because intersect on a shape and intersect on a relations object return different kinds of thing. A second related method, distanceTo, computes the distance to another shape and, together with the distance function, returns the shortest segment between the two shapes, the segment between the two closest points.

Planar Set is the spatial index, Multiline is the open chain

Planar Set is the container that makes the library useful for larger scenes. The documentation describes it as a container of shapes that enables spatial search by rectangular query, and it appears in the same import list as the primitives, which tells you it is a first-class part of the API rather than an add-on.

That distinction matters for how you structure code. Running an intersection test against every shape in an array is fine for a few dozen objects and hopeless for thousands. A rectangular query against a Planar Set narrows the candidates first, which is the difference between a hit test that stays responsive on a map and one that does not.

The other container is Multiline, described as an unclosed chain of edges of type Segment or Arc. It is the counterpart to a closed face, and it is the tool for the cut operation, which cuts a polygon with a multiline into sub-polygons. A Multiline is therefore not just a drawing convenience, it is a tool for dividing geometry along a path.

The library also ships a set of keywords in its package metadata that describe the scope: geometry, 2d, algorithms, shape, polygon, distance, intersection, relation, point in polygon, spatial search, affine transformations, boolean operations, polygon clipping, and the dimensionally extended 9-intersections model.

Where flatten-js stops and what you use instead

The first boundary is rendering. The documentation is explicit that visualisation is not the concern, and the svg() method covers exactly one target. A project that needs to draw into a canvas, lay out a scene, or handle interaction is still writing that layer, and the geometry library is the smaller half of the work.

The second boundary is the validity rule for polygons. Because boolean operations assume orientable, non-overlapping faces, a polygon that arrives from a user drawing, a file on disk or another library has to be checked with isValid() before those operations mean anything. A library that validated and repaired its input would hide that; this one tells you to check.

The alternative approaches differ in what they take off your hands. A general purpose graphics or canvas library gives you drawing primitives and a scene, and leaves the geometry questions like orientation, containment and boolean operations to you. Writing the geometry yourself gives you exactly the semantics you need and no dependencies, at the cost of every one of the cases the library already covers, from the 9-intersections relation model to polygon clipping. flatten-js sits in the middle: a shape model with relations and booleans, and no renderer, which is the right trade when the geometry is the hard part of your application.

MIT terms, three module formats, and a release with a bug fix in it

The package is published as @flatten-js/core under the MIT Licence, and the package metadata is unusually complete. It declares a main entry point, a umd:main entry, an unpkg entry and a module entry, which corresponds to the documentation's statement that packages are distributed in three formats: CommonJS, UMD and ES6 modules. The exports map wires the types, require, import and default conditions to those files, and the types field points at index.d.ts, the TypeScript definition file included in the package for static type checking.

Two metadata fields are worth a second look. The engines field asks for node >= 4.2.4, which is a very old floor for a package that also ships modern ES modules, so treat it as a stale declaration rather than a real constraint. The publishConfig enables provenance, which is a supply chain signal that the published artefact is tied to the build.

On maintenance, the repository is not archived and the last push was on 2026-08-18, the same day as release v1.6.14, with v1.6.13 on 2026-08-04 and v1.6.12 on 2026-04-05. The title of that third release is the most useful entry in the list, because it reads "Fix bug: Subtract creates sliver", and it is a good argument for pinning the version in a project whose boolean operations matter.

Editorial conclusion

flatten-js fits JavaScript and TypeScript projects doing computational geometry on the client, such as CAD-style editing, path analysis or hit testing, where its shape model, boolean operations and TypeScript definitions save more than they cost. It does not fit a project that expects a rendering engine, since visualisation is explicitly not its concern, and it does not fit anyone unwilling to guarantee that polygon faces are orientable and non-overlapping before calling the boolean operations. Before adopting it, run isValid() over every polygon you load, pin the version because a patch release was needed to fix a sliver produced by Subtract, and check the node engines field before assuming a current runtime is what the package expects.

Frequently asked questions

How do I install flatten-js and import its shapes?

The package is published to npm as @flatten-js/core and the documented install command is npm install --save @flatten-js/core. Classes are imported by name from @flatten-js/core, and lowercase shortcuts such as point, segment and circle exist to avoid the new constructor.

What is the difference between intersect, BooleanOperations.intersect and Relations.intersect?

The intersect method on shapes returns an array of intersection points, empty when the shapes do not intersect. BooleanOperations.intersect() performs a boolean intersection of polygons, and Relations.intersect() is a logical predicate that reports whether two shapes intersect at all.

Is a polygon in flatten-js allowed to have holes and nested islands?

Yes. A polygon in this library is a multi-polygon: the outermost face is an island, a face inside it is a hole, holes may contain inner islands, and the number of inclusion levels is unlimited. Orientation of holes must be opposite to islands, and the isValid() method checks these rules.

Does flatten-js do rendering or visualisation?

Not as its purpose. The documentation says the library does not concern itself too much with visualisation, though every class implements an svg() method returning a string for an SVG container, and it notes it works well with d3js.

What is Planar Set used for in flatten-js?

Planar Set is a searchable container of shapes that enables spatial search by rectangular query, so you can narrow candidates before running intersection or distance tests on a large collection.

Official sources

  1. alexbol99/flatten-js on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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/alexbol99-flatten-js.svg)](https://hysenlabs.com/projects/alexbol99-flatten-js)

Was this useful?