npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@matthewjacobson/str8

v0.2.0

Published

Modern straight-skeleton computation in the browser and Node, powered by CGAL + WebAssembly.

Readme

str8

Modern straight-skeleton computation for the browser and Node, powered by CGAL's Straight_skeleton_2 package compiled to WebAssembly.

This is a from-scratch rebuild of the idea behind StrandedKitty/straight-skeleton, using CGAL 6.1, Emscripten 4.x (embind), and a modern ESM bundle.

What it computes

The straight skeleton of a simple polygon (with optional holes): the set of edges traced by the polygon's edges as they move inward at constant speed. Each skeleton vertex carries a time value — the distance the wavefront travelled to reach it — which doubles as a roof height.

str8 computes:

  • Interior and exterior straight skeletons.
  • Offset contours — inset (erode) or outset (dilate) a polygon by a distance, derived from the skeleton.

▶ Live demos

Install

npm install @matthewjacobson/str8

The published package is a single ESM file with the WASM embedded (base64), so there's nothing extra to host or configure.

Usage

import { init, buildFromPolygon } from '@matthewjacobson/str8';

await init(); // load the WASM module once

const skeleton = buildFromPolygon([
  // outer ring (winding order doesn't matter — it's normalized for you)
  [[-1, -1], [1, -1], [1, 1], [-1, 1]],
  // optional holes follow...
]);

if (skeleton) {
  // vertices: flat Float32Array of [x, y, time] triples
  for (let i = 0; i < skeleton.vertices.length; i += 3) {
    const [x, y, time] = skeleton.vertices.subarray(i, i + 3);
  }
  // faces: one entry per skeleton face, each a list of vertex indices
  for (const face of skeleton.faces) {
    // face = [i0, i1, i2, ...] indices into vertices
  }
}

GeoJSON

import { init, buildFromGeoJSON } from '@matthewjacobson/str8';

await init();
const results = buildFromGeoJSON({
  type: 'Polygon',
  coordinates: [[[0, 0], [4, 0], [4, 4], [0, 4]]],
});
// results: (Skeleton | null)[] — one per polygon (MultiPolygon supported)

API

| Export | Description | | --- | --- | | init(): Promise<void> | Loads and instantiates the WASM module. Await once before building. | | isReady(): boolean | Whether the module is loaded. | | buildFromPolygon(rings, opts?): Skeleton \| null | Interior skeleton of a polygon. Ring 0 is the outer boundary; the rest are holes. | | buildFromGeoJSON(geometry, opts?): (Skeleton \| null)[] | Interior skeletons from a GeoJSON Polygon / MultiPolygon. | | buildExteriorSkeleton(rings, { maxOffset }): Skeleton \| null | Exterior skeleton, framed maxOffset beyond the polygon. | | offsetPolygon(rings, distance, opts?): OffsetPolygon[] \| null | Inset (default) or outset ({ exterior: true }) offset contours. |

interface Skeleton {
  vertices: Float32Array; // [x, y, time, ...]
  faces: number[][];      // vertex indices, one list per face
}

interface OffsetPolygon {
  outer: Float32Array;    // [x, y, ...]
  holes: Float32Array[];  // each [x, y, ...]
}

An inset can split into several pieces or vanish past the polygon's max inradius, so offsetPolygon returns an array of contours (possibly empty). All builders accept { forceExact: true } to skip the fast kernel (see below).

Input handling (modernizations over the original)

  • Winding order is normalized automatically — outer ring forced CCW, holes CW. You don't have to pre-orient your data.
  • Open or closed rings both work — a duplicated closing vertex is dropped.
  • Degenerate input (fewer than 3 vertices, empty) returns null instead of aborting the WASM module.

Robustness: automatic exact fallback

CGAL's straight skeleton uses the fast inexact-constructions kernel (EPICK) by default. Highly symmetric inputs — e.g. a grid of identical, evenly-spaced holes — create many simultaneous wavefront events that EPICK's rounded arithmetic can't resolve consistently, and the computation fails.

str8 handles this automatically: if the fast kernel fails, it retries with the exact-constructions kernel (EPECK), which is slower but robust. You normally don't have to think about it. To skip the fast attempt and go straight to exact:

buildFromPolygon(rings, { forceExact: true });
buildFromGeoJSON(geometry, { forceExact: true });

Building from source

Requires the Emscripten SDK and CGAL + Boost headers (e.g. brew install cgal boost on macOS).

# 1. compile the CGAL core to a single-file ES module (src/core/skeleton.js)
source /path/to/emsdk/emsdk_env.sh
npm run build:wasm

# 2. bundle the TS wrapper + emit types -> dist/
npm run build

# or both at once:
npm run build:all

If your headers aren't in /opt/homebrew/include, set DEPS_INCLUDE_DIR:

DEPS_INCLUDE_DIR=/usr/local/include npm run build:wasm

Examples

Static demos live in example/ — serve the repo root over HTTP (e.g. npx serve or python3 -m http.server) and open them:

  • example/index.html — a gallery with a dropdown of 28 sample polygons (from LingDong-/interesting-polygon-archive), with pan, zoom, and live vertex/hole/face stats.
  • example/geojson.htmlpaste your own GeoJSON (Polygon, MultiPolygon, Feature, FeatureCollection, or a bare coordinates array) and see its skeleton, with pan/zoom and a Y-flip toggle for screen-coordinate data.
  • example/roof.html3D straight-skeleton roofs: the same sample polygons lifted into roofs where each vertex's height is its wavefront time, rendered with three.js (orbit, adjustable pitch). three.js is loaded from a CDN, so this page needs network access.
  • example/offset.htmloffsets & exterior skeletons: inset/outset offset contours (with concentric stepping) and interior/exterior straight skeletons, driven by a distance slider, with pan/zoom.

They import the built dist/str8.js, so run npm run build first.

How it works

  • src/core/skeleton.cpp — CGAL create_interior_straight_skeleton_2 over a Polygon_with_holes_2 (EPICK kernel), exposed via embind. Returns a Float32Array of vertices and a number[][] of faces directly as JS objects.
  • Built with -sMODULARIZE -sEXPORT_ES6 -sSINGLE_FILE so the WASM is embedded in an ES module. GMP/MPFR are disabled (CGAL_DISABLE_GMP) so nothing native needs cross-compiling.
  • src/index.ts — the public API: orientation normalization, GeoJSON helpers, and the WASM lifecycle.

License

MIT. CGAL is used under its own license (GPL/LGPL depending on package); Straight_skeleton_2 is GPL — review CGAL's licensing for your use case.