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

@gyeonghokim/copc-tileset

v1.0.3

Published

Stream COPC (Cloud Optimized Point Cloud) files directly into CesiumJS — no pre-tiling, no backend.

Readme

copc-tileset

Stream COPC (Cloud Optimized Point Cloud) files directly into CesiumJS — no pre-tiling, no conversion, no backend. Point a URL at a .copc.laz file on any static HTTP host and it streams into the globe.

How it works

A COPC file is already an octree with level-of-detail built in. copc-tileset reads it over HTTP range requests (via copc.js) and turns each octree node into a 3D Tiles tile on the fly, inside a Service Worker. Those tiles feed a Cesium3DTileset, so Cesium's engine drives view-dependent LOD, frustum culling, request scheduling and GPU memory — while attenuation, Eye Dome Lighting, custom shaders and picking come for free from Cesium's native point-cloud pipeline.

Only the nodes the camera actually needs are fetched and decoded, one at a time — the streaming is genuinely incremental, never a whole-file download.

Installation

npm install @gyeonghokim/copc-tileset   # or: yarn add / pnpm add

cesium is a peer dependency.

Quick Start

import { Viewer } from "cesium";
import {
  CopcProvider,
  CopcPointCloudPrimitive,
  registerCopcServiceWorker,
} from "@gyeonghokim/copc-tileset";

// 1. Register the Service Worker that serves tiles. Do this once, at startup.
//    Serve the bundled worker from your app (see "Service Worker setup" below).
await registerCopcServiceWorker("/copc-sw.js");

const viewer = new Viewer("cesiumContainer");

// 2. Open a COPC file (reads header, VLRs, octree info over range requests).
const provider = await CopcProvider.fromUrl(
  "https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz",
);

// 3. Create the point cloud and add it to the scene.
const pointCloud = await CopcPointCloudPrimitive.fromProvider(provider, {
  pointCloudShading: { attenuation: true, eyeDomeLighting: true },
});
viewer.scene.primitives.add(pointCloud);

// `Cesium3DTileset`-backed, so frame it via its bounding sphere.
viewer.camera.flyToBoundingSphere(pointCloud.boundingSphere);

Requirements

Your .copc.laz file just needs to be served over HTTP(S) with:

  • Range request support (most static hosts and object storage, e.g. S3, support this by default)
  • CORS enabled, if served from a different origin than your app

Your app must be built with a bundler that supports ?url asset imports — Vite, webpack 5, Parcel, or Rspack. Point decoding runs laz-perf (WebAssembly) inside the Service Worker, and the library resolves its .wasm via a ?url import so your bundler emits it and serves it at the right path. With a bundler that ignores ?url, the .wasm is not emitted and every tile fails to decode.

API

| | | |---|---| | CopcProvider.fromUrl(url, options?) | Reads the COPC header, VLRs and octree hierarchy via copc.js. options.headers are sent with each range request (e.g. for auth). Returns Promise<CopcProvider>. | | CopcPointCloudPrimitive.fromProvider(provider, options?) | Builds a Cesium3DTileset-backed point cloud fed by the Service Worker. Add it to viewer.scene.primitives; exposes boundingSphere and the underlying .tileset. Returns Promise<CopcPointCloudPrimitive>. | | registerCopcServiceWorker(scriptUrl, options?) | Registers the tile-serving Service Worker. Call once before creating a primitive. |

CopcPointCloudPrimitive options (all also settable at runtime as properties):

  • pointSize — fixed point size in pixels
  • maximumScreenSpaceError — screen-space error that drives octree LOD refinement (default 16)
  • dynamicScreenSpaceError — reduce detail for far tiles in dense scenes (default true)
  • cacheBytes — GPU memory budget for loaded tiles
  • pointCloudShading — attenuation and Eye Dome Lighting, mirroring Cesium's native PointCloudShading (attenuation, maximumAttenuation, eyeDomeLighting, eyeDomeLightingStrength, …). Use this — not a custom shader — for distance-based point sizing and EDL.
  • customShader — a Cesium CustomShader for attribute-driven colouring / filtering (classification, intensity, …)

Examples

Classification-based colouring (custom shader):

import { CustomShader } from "cesium";

pointCloud.customShader = new CustomShader({
  fragmentShaderText: `
    void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
      float c = fsInput.metadata.Classification;
      if (c == 2.0) material.diffuse = vec3(0.55, 0.4, 0.25);  // ground
      else if (c == 5.0) material.diffuse = vec3(0.1, 0.6, 0.1); // high vegetation
    }`,
});

Point picking — each point is a Cesium3DTileFeature with its per-point attributes:

import { Cesium3DTileFeature, ScreenSpaceEventHandler, ScreenSpaceEventType } from "cesium";

const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((movement) => {
  const feature = viewer.scene.pick(movement.position);
  if (feature instanceof Cesium3DTileFeature) {
    console.log("Classification:", feature.getProperty("Classification"));
    console.log("Intensity:", feature.getProperty("Intensity"));
    console.log("GpsTime:", feature.getProperty("GpsTime"));
  }
}, ScreenSpaceEventType.LEFT_CLICK);

Alongside other 3D Tiles — it's a regular Cesium3DTileset, so it composes with buildings, terrain and photogrammetry with correct depth ordering; just add both to scene.primitives.

A full interactive demo (dataset switcher, EDL/attenuation toggles, point size, picking) lives in examples/ — run it with npm run dev.

Service Worker setup

Tiles are generated on the fly by a Service Worker so there is no backend. Your app must serve the bundled worker (src/sw/copc-sw.ts) from its own origin and register it with registerCopcServiceWorker(url). Virtual tile URLs are relative to your app base, so the worker's default scope covers them — no special scope or Service-Worker-Allowed header is needed, even on sub-path hosts like GitHub Pages. See examples/vite.config.ts for a Vite setup that emits copc-sw.js.

registerCopcServiceWorker resolves only once the worker controls the page (otherwise the first tileset.json request would fall through to your static host). On a visitor's first load the freshly activated worker takes control via clients.claim(); in the rare case it activates without claiming, the helper performs a single guarded page reload so the next load is controlled.

Sample Data

All three are served from a public S3 bucket with HTTP Range and CORS enabled, so they work in the browser out of the box.

| Dataset | Size | URL | |---|---|---| | Autzen Stadium | 77 MB | https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz | | Millsite Reservoir | 1.35 GB | https://s3.amazonaws.com/hobu-lidar/millsite.copc.laz | | SoFi Stadium | 1.9 GB | https://s3.amazonaws.com/hobu-lidar/sofi.copc.laz |

Built On

copc.js · CesiumJS · 3D Tiles · COPC Specification

Inspired by TIFFImageryProvider, developed for the 2026 Open Source Developer Contest (Gaia3D designated task).

License

AGPL-3.0-or-later