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

@orb3d/core

v0.3.0

Published

Glass orbs with states for the web: the WebGL2 engine, presets and config model behind Orb Studio

Readme

@orb3d/core

The engine behind Orb Studio: a WebGL2 glass orb with states, the presets, the config model and the poster-first shell. Framework-agnostic; @orb3d/react and @orb3d/element wrap it.

pnpm add @orb3d/core

The shell

import { createOrb } from "@orb3d/core";
import { PRESETS } from "@orb3d/core/presets";

const orb = createOrb(document.getElementById("orb")!, PRESETS.aura(), {
  poster: "/orb.png",
});

orb.setState("thinking"); // springs there; staged if the engine is not live yet
orb.setLevel(0.6); // 0 to 1, smoothed, drives the `react` amounts
orb.set({ glass: { frost: 0.4 } }); // merged block by block, applied at once
orb.set({ contents: { type: "image", src: "/photo.jpg" } }); // other contents replace the old
await orb.snapshot({ time: 0 }); // a PNG of the opening frame
orb.destroy();

createOrb shows the poster at once, waits until the box nears the viewport, probes for a WebGL2 context on a real GPU, then fetches the engine through a dynamic import and crossfades in. Options: poster, posterFit, fadeMs, lazy, rootMargin, webgl (auto | force | off), respectReducedMotion, reducedMotionBehavior (static | poster), respectSaveData, minSizeForWebGL, paused, onReady, onFallback, onPhaseChange. The handle's phase is where the shell is (poster, loading, running, fallback); its state is what the orb shows.

For a large interactive orb, pass adaptiveQuality: true to the shell or renderer. It targets 60 fps by lowering live resolution after sustained slow frames, then recovering gradually. It respects the authored quality / dprMax ceiling, never goes below half scale or a 192-pixel shorter side, and leaves small avatars sharp. It is off by default; Studio enables it for its live preview. Captures and explicit output sizes always use the authored resolution. renderer.resolutionScale reports the current live multiplier.

The engine

import { OrbRenderer } from "@orb3d/core/renderer";

const renderer = new OrbRenderer(container, config, { respectReducedMotion: true });
renderer.start();
renderer.setState("speaking", { immediate: false });

setState, setLevel, setWeather, setConfig, setPaused, seek(time), renderOnce(), captureImage(type, quality, time), setOutputSize({ width, height }), resize(), dispose(); isContextLost remains true until a lost context has successfully rebuilt its resources; mediaLoading while a picture or clip is on its way, and shaderError (with the onShaderError option) when a shader of your own does not compile. It keeps the last working shader, or shows clear glass if there is none. It pauses offscreen and in hidden tabs (a clip with it), clamps DPR (dprMax, quality), restores a lost context and, under prefers-reduced-motion, holds a still frame and cuts between states.

On a WebGL2 context of your own, such as an OffscreenCanvas in a worker, OrbGL draws a compiled program with no DOM: use(program) links the shader its contents need, once for each kind, and binds its uniform blocks; draw(runtime, width, height) uploads the runtime's frame and draws it. OrbRenderer draws through one. Pictures and clips stay OrbRenderer's.

// A worker, handed a canvas the page transferred with transferControlToOffscreen().
import { PRESETS } from "@orb3d/core/presets";
import { compileOrb, OrbRuntime } from "@orb3d/core/program";
import { OrbGL } from "@orb3d/core/renderer";

self.onmessage = ({ data: { canvas } }: MessageEvent<{ canvas: OffscreenCanvas }>) => {
  const program = compileOrb(PRESETS.aura());
  const runtime = new OrbRuntime(program);
  const orb = new OrbGL(
    canvas.getContext("webgl2", { premultipliedAlpha: true, antialias: false })!,
  );
  orb.use(program);
  let last = performance.now();
  const frame = (now: number) => {
    runtime.advance(Math.min((now - last) / 1000, 0.1));
    last = now;
    orb.draw(runtime, canvas.width, canvas.height);
    requestAnimationFrame(frame);
  };
  requestAnimationFrame(frame);
};

The config

OrbConfig is plain JSON: contents (what is in the glass, by type: none, gradient, image, video, shader, sunset, landscape or celestial), the glass round it (glass, light, environment, weather, halo, shadow, post, react), states and a few settings. createDefaultConfig() is clear glass; createContentsConfig(type) is the same glass holding type's defaults, with its state overrides. ensureOrbConfig(partial) fills and clamps a partial config; mergeOrbConfig(a, b) merges block by block (arrays whole, contents of another type replacing the old, states replaced by name, null removing a state); mergeStates(states, changes) changes part of some states. dialsFor(type) lists every number and colour a state can move in an orb holding type, with its range and group, and writeStateDials(config, state) packs what a state resolves to. CONTENTS holds each kind's model: its label, dials, defaults and states.

A state:

{
  duration: 0.9, // seconds to settle into it
  glass: { frost: 0.4, dispersion: 0.8 },
  environment: { spin: 70 }, // degrees a second the room turns
  contents: { flow: 1.4, spin: 40 }, // the contents' own dials, here a gradient's
  react: { exposure: 0.3 }, // how much the level adds
}

Programs

@orb3d/core/program compiles a config to a program (compileOrb), the JSON other platforms run: the uniform array's structure, each state's dials, and the clocks. OrbRuntime steps it, and orbShader(dialect, program) writes its shader as GLSL, SkSL or MSL; compileOrb(config, { shaders: ["msl", "glsl"] }) carries it, which Swift and Android need for a shader of your own.

The CDN build

@orb3d/core/standalone is one file with the engine and the presets bundled, for a plain <script type="module">; mountOrb there upgrades with no second request.

Posters

orb3d poster renders a poster to a file in a build, for the shell's poster or a favicon. It draws through the engine in headless Chromium on SwiftShader, so a config gives the same file on every machine. It needs Playwright, an optional peer, and its Chromium (pnpm exec playwright install chromium).

pnpm exec orb3d poster --preset sunset --size 96 --out public/mark.png
pnpm exec orb3d poster --config orb.json --state thinking --time 2 --out thinking.webp

--preset or --config (JSON such as Studio's export, over the preset when both are given), --state, --time (default 0, the frame the orb opens on), --level, --size (default 256), --supersample (default 4: drawn larger and scaled down, which smooths the glass's edge), --quality and --out (.png, .webp or .jpg). A picture or clip named by a relative path is read from the config's folder.

Weather observations

weatherConfig(observation) creates a weather orb; { target: "hero" } creates a full-canvas sky and { target: "overlay" } creates transparent airborne effects. The shell handle and renderer expose setWeather(observation) to transition observations without resetting surface water or ice. { legible: true } lifts light rain, snow and hail so a small orb or a hero shows them, without changing the observation's rates. normalizeWeather, weatherState, fromOpenMeteo, solarPosition and lunarPhase are exported from both the package entry and standalone bundle. See weather integration for units, controls and native export guidance.