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

@threecyborgs/infinite-inversion

v0.2.0

Published

Render an unbounded physics space into a bounded spherical room via spherical inversion, warping geometry exactly on the GPU. Three.js core with optional Rapier physics and a flight controller.

Readme

@threecyborgs/infinite-inversion

Equal-size boxes shrinking toward the unreachable center of the inverted room

Render objects from an unbounded physics space into a bounded spherical room using spherical inversion:

x = (R² / |p|²) · p          physics space  →  room space

The map is its own inverse. The room wall is the fixed sphere |p| = R; the room center is physics infinity and can never be reached. Geometry is warped exactly, per-vertex, on the GPU — straight rods bow into arcs and far ends shrink more than near ones, instead of the flat per-object approximation a single matrix gives.

The core is physics-engine agnostic (it only needs three). A Rapier binding and a spaceship flight controller ship as optional subpath exports.

Install

npm i @threecyborgs/infinite-inversion three
# optional, for the /rapier binding:
npm i @dimforge/rapier3d-compat

three is a peer dependency. @dimforge/rapier3d-compat is an optional peer — only needed if you import @threecyborgs/infinite-inversion/rapier.

Quick start

The InvertedWorld facade wires the room shell, the warp + matching shadow depth material, frustumCulled/double-sided setup, per-frame sync, and camera clip planes for you:

import * as THREE from "three";
import {
  DEFAULT_ROOM_RADIUS,
  InvertedWorld,
  invert,
  recommendedRendererParams,
} from "@threecyborgs/infinite-inversion";

const R = DEFAULT_ROOM_RADIUS; // 50 (a 100 m-wide room)

// A logarithmic depth buffer is required — recommendedRendererParams() sets it.
const renderer = new THREE.WebGLRenderer(recommendedRendererParams());
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(70, innerWidth / innerHeight, 0.01, 100);

const world = new InvertedWorld({ scene, roomRadius: R });

// Place markers in PHYSICS space with real, physical dimensions. The GPU warp
// shrinks and curves them into the room automatically — no room-space tweaking.
const mat = new THREE.MeshStandardMaterial({ color: 0x4dabf7 });
world.addStatic(new THREE.BoxGeometry(10, 10, 10), mat, {
  position: new THREE.Vector3(0, 0, 400),
});

// View the room from a physics-space position.
const viewerPhysics = new THREE.Vector3(0, R * 0.3, R * 1.5);

function frame() {
  requestAnimationFrame(frame);
  camera.position.copy(invert(viewerPhysics, R)); // physics -> room
  camera.lookAt(0, 0, 0);
  world.sync(); // push physics poses into meshes
  world.configureCamera(camera, viewerPhysics); // set near/far for this depth
  renderer.render(scene, camera);
}
frame();

For dynamic objects bound to a physics body, use world.addBody(geometry, material, body) where body is any PoseSource (a Rapier RigidBody works directly). A full runnable version is in examples/minimal.

If you'd rather assemble the primitives yourself, the lower-level building blocks (RoomRenderer, InvertedStatic, makeInvertedMesh, applyInversionWarp, createWarpDepthMaterial, configureInversionCamera) are all exported too.

Rules of the road

  • Author everything in physics space: real positions and real dimensions (geometry radius, collider size, spacing in metres). Let inversion produce the room appearance — never hand-set room-space scale/position to "fix" looks.
  • Meshes that warp must have frustumCulled = false (their physics-space bounds don't match the room-space camera frustum). InvertedBody / InvertedStatic set this for you.
  • Use a logarithmicDepthBuffer on your renderer — the inversion spans a huge depth range (metre-scale wall → sub-mm centre markers).

Subpath exports

@threecyborgs/infinite-inversion/rapier

A minimal Rapier world bounded by the room wall, with a fixed-timestep stepper.

import { Physics } from "@threecyborgs/infinite-inversion/rapier";
import { InvertedBody } from "@threecyborgs/infinite-inversion";

const physics = await Physics.create({ roomRadius: R, rhoMax: 500, restitution: 1 });
const body = physics.createDynamicSphere(pos, radius, velocity);
room.add(new InvertedBody(mesh, body, R)); // body satisfies PoseSource

// each frame: physics.step(dt); room.sync();

Any object implementing PoseSource ({ translation(), rotation() }) works with InvertedBody, so you can drive it from any engine — not just Rapier.

@threecyborgs/infinite-inversion/controls

A frictionless, momentum-based spaceship Player flown in physics space and rendered through the inversion (thrust toward where you look, boost, dampener, first/third-person). See examples/vanilla for full wiring.

@threecyborgs/infinite-inversion/webgpu

WebGPU + TSL port of the warp, plus a GPU-compute level-of-detail field for millions of instances. LOD is keyed off the inverted (room-space) apparent size, never the physics distance — because under inversion two objects far apart in physics can both collapse toward the room centre and appear tiny and adjacent. A compute pass projects each instance, computes its apparent pixel size, and writes a per-instance morph factor + cull flag (with hysteresis); one instanced draw then geomorphs the geometry and discards sub-pixel instances. No indirect draw and no per-instance CPU readback (only a 16-byte atomic counter buffer for telemetry).

import { WebGPURenderer } from "three/webgpu";
import { InvertedLODField } from "@threecyborgs/infinite-inversion/webgpu";

const field = new InvertedLODField({
  renderer, roomRadius: R, count: 1_000_000,
  instances, // Float32Array of [x, y, z, featureRadius] * count (physics space)
});
field.addTo(scene);
// each frame, before render:
field.update(camera); // projects + classifies LOD on the GPU

See examples/webgpu-lod (npm run dev:webgpu). Validated by tests-webgpu/lod.spec.ts (npm run test:webgpu) — including a deterministic test that two instances 4.75× apart in physics, engineered to the same apparent size, receive the same computed LOD.

WebGPU testing caveat. WebGPU needs a real adapter, so the WebGPU suite uses its own playwright.webgpu.config.ts (flags --enable-unsafe-webgpu, not the WebGL config's SwiftShader, which has no WebGPU adapter). It is kept out of npm test / CI because Linux CI runners may lack a WebGPU adapter; run it locally on a GPU-backed machine.

API

Core

| Export | Description | |---|---| | InvertedWorld | High-level facade: room shell + warp + sync + camera in one object. | | invert(v, R, out?) | Invert a point through the sphere (physics ↔ room). | | roomScale(p, R) | Uniform render scale R²/|p|² at physics point p. | | reflectionMatrix(n, out?) | Householder reflection H = I − 2nnᵀ. | | inversionMatrix(p, q, R, out?) | Full room-space Matrix4 for a pose (CPU proxy). | | applyInversionWarp(material, R) | Patch a material to warp position + normals on the GPU. | | createWarpDepthMaterial(R) | Matching depth material so shadows warp too. | | makeInvertedMesh(geo, mat, R, opts?) | Build a warp-ready mesh (warp + frustumCulled + double-side + depth). | | prepareInvertedMesh(mesh, R, opts?) / prepareInvertedMaterial(mat, R) | Same setup for existing meshes/materials. | | configureInversionCamera(camera, R, physicsPos) | Set near/far for the viewer's depth. | | recommendedRendererParams() | WebGLRenderer params (logarithmic depth buffer). | | RoomRenderer | Holds InvertedRenderables, sync()s them each frame. | | InvertedBody<B> / InvertedStatic | Bind a mesh to a physics-space pose. | | PoseSource | { translation(), rotation() } — any body type drives InvertedBody. | | buildRoom(R) | The visible room shell (wall + grid). | | DEFAULT_ROOM_RADIUS | 50. |

Compatibility

The GPU warp injects GLSL by replacing three's built-in vertex shader chunks (#include <project_vertex> etc.) via Material.onBeforeCompile. Those chunk names are stable but can change between major three releases.

  • Peer range: three >= 0.160.
  • Tested against [email protected]. The tests/render-smoke.spec.ts test fails loudly if the warp shader stops compiling against the installed three, so a version bump can't silently break consumers. Run npm test after upgrading.

Development

npm run dev          # Vite dev server for examples/vanilla (full demo)
npm run dev:minimal  # Vite dev server for examples/minimal (core only)
npm run dev:webgpu   # Vite dev server for examples/webgpu-lod (GPU-compute LOD)
npm run build        # build the library (tsup → dist/, ESM + .d.ts)
npm run typecheck
npm test             # headed Playwright tests (WebGL)
npm run test:webgpu  # headed Playwright tests (WebGPU; needs a real adapter)

License

MIT