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

@kvmet/terrain

v0.2.0

Published

Tiny DOM-aware free-space queries and obstacle-avoiding routes.

Readme

Terrain

Terrain is a tiny, dependency-free browser utility for finding legal points and collision-free paths around DOM elements. build() reads layout once; repeated queries use only cached geometry.

const terrain = new Terrain({ avoid: ".avoid" }).build();

const point = terrain.nearest(x, y);  // nearest legal point
const move = terrain.offset(x, y);    // additive correction
const points = terrain.route(a, b);   // collision-free waypoints

Terrain handles one viewport and axis-aligned DOM rectangles. It is not a diagram framework, renderer, physics engine, or polygon navigator.

Install

Load the standalone production build:

<div data-terrain-avoid>Terrain routes around me.</div>
<script src="https://cdn.jsdelivr.net/npm/@kvmet/[email protected]/terrain.min.js"></script>
<script>
  const terrain = new Terrain().build();
</script>

terrain.js is the readable build and creates the same window.Terrain global.

With npm:

npm install @kvmet/terrain
import Terrain from "@kvmet/terrain";
// const Terrain = require("@kvmet/terrain");

Open the dependency-free demo or run demo.html directly.

API

Call build() after page load and whenever relevant layout changes. It measures each distinct avoided element once, replaces the prior snapshot, and returns the Terrain instance. terrain.ready reports whether that snapshot contains navigable space. A failed build leaves Terrain not ready.

nearest(x, y, result = {})

Returns the nearest legal point represented by the mesh and its distance from the requested coordinates:

const point = terrain.nearest(x, y); // { x, y, distance }

The returned coordinates equal the requested coordinates and distance is zero when the point is already legal. Reuse the optional result object to avoid allocations in hot loops. Returns null when Terrain is not ready.

offset(x, y, result = {})

Returns the additive displacement to the nearest legal point:

const move = terrain.offset(x, y); // { dx, dy, distance }
if (move) {
  x += move.dx;
  y += move.dy;
}

dx, dy, and distance are zero when the point is already legal. The optional result object and null behavior match nearest().

route(a, b)

Lands both endpoints when necessary, then returns the landed endpoints and any required waypoints. Returns null when Terrain is not ready or no route exists. Points must have finite x and y coordinates. A successful route is an array of fresh waypoint objects with a moved boolean property. moved is true when one or both requested endpoints had to be moved; the first and last waypoints give their landed positions. Adjacent duplicate waypoints are omitted.

Options

| Option | Default | Meaning | | --- | --- | --- | | avoid | "[data-terrain-avoid]" | Selector, iterable of elements, or function returning an iterable. | | root | document | The selector's querySelectorAll() root. | | viewport | Browser viewport | { left, top, width, height }, or a function returning one. | | edgeMargin | 0 | Nonnegative inset from each viewport edge. | | obstaclePadding | 0 | Nonnegative clearance around obstacles. | | funnel | true | Keep only taut turns; false retains gate centers. |

Options are validated, snapshotted, and frozen. Functions are evaluated on every build(). Defaults are exposed as Terrain.DEFAULTS.

terrain.avoidElements() resolves avoid without measuring it, which is useful for observing the elements that the next build will read. Coordinates are local to the configured viewport.

Geometry

Obstacles are closed DOMRects. Overlapping rectangles and zero-width or zero-height lines and points are supported. Generated mesh geometry sits two adjacent floating-point values outside obstacle edges as a numerical guard so it cannot occlude itself.

If you want visible clearance around obstacles use obstaclePadding.

Rotated rectangles, polygons, connector pins, crossing avoidance, and incremental mutation are out of scope. Rebuild after layout changes.

Performance

The production build is under 10 kB minified with no runtime dependencies. Only elements resolved by avoid affect Terrain; the rest of the DOM is irrelevant.

If m elements are selected and n intersect the viewport, build() reads m DOMRects and constructs its mesh from n rectangles. Legal point queries and direct routes are roughly O(n). Blocked routes also search the mesh, so they scale with its cells and gates. Mesh construction is O(n^2 log n) in the worst case; cells and gates can be O(n^2). Terrain is designed for many queries per build.

The demo includes an opt-in browser profiler. Run the deterministic 0 to 96 obstacle scaling benchmark with:

npm --prefix terrain install
bb terrain-benchmark

Development

terrain.js and terrain.min.js are generated from src/; do not edit them directly. Build and run the source, browser-artifact, and packed-consumer checks:

bb terrain

Terrain is released under the MIT License.