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

webgl.tree

v0.0.7

Published

webgl.tree — @nakednous/tree + @nakednous/host on raw WebGL2: gizmo line pipe, HUD, render targets, filter passes, the pipe chain, async colour-ID picking, camera install.

Readme

webgl.tree

@nakednous/tree and @nakednous/host on raw WebGL2 through twgl: a camera install that uploads only the transforms a program declares, render targets in every shape, fullscreen filter passes and the ping-pong pipe, a line pipe that draws the core's gizmo arrays, HUD mode, asynchronous colour-id scene picking, textures under one orientation. No engine, no scene graph — twgl's own verbs stay yours.

webgl.tree is an experimental implementation of the visual-computing notebook's pseudo-code design, expected to rest on its foundations: @nakednous/tree's math, twgl's calls, and only what the notebook's archetype columns write by hand and the notation leaves out. A hero that ports line for line confirms a piece of the notation; one that cannot is where the notation changes.

Status: 0.0.x. The whole surface below is shipped; the harness under testing/ runs the notebook's imaging, picking and portal heroes and every gizmo on the bridge with p5.tree beside for parity. The gizmo width mode is experimental.


Installation

Two script tags, twgl.js first; webgl.tree reads the global twgl and exposes the global webglTree:

<script src="https://cdn.jsdelivr.net/npm/twgl.js@7/dist/7.x/twgl-full.js"></script>
<script src="https://cdn.jsdelivr.net/npm/webgl.tree/dist/webgl.tree.js"></script>
const { createCanvas, setCamera, bind, draw, axes, tree } = webglTree

const gl = createCanvas(400, 300)                              // buffer at the display's pixel density
const prog = twgl.createProgramInfo(gl, [vert, frag])          // twgl's verb, not re-wrapped
const mesh = twgl.createBufferInfoFromArrays(gl, arrays)
const cam = tree.createCamera({ eye: [0, 0, 800] })

function frame() {
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  setCamera(gl, cam)                                          // V and P installed once
  bind(gl, prog, { uColor: [1, 0.3, 0.8] })
  draw(gl, mesh, M)                                           // the declared transforms uploaded
  axes(gl, { size: 100 })
}

tree holds the math — matrices, quaternions, camera states, visibility; host holds the canvas host — handles, tracks, orbit, devices, media. For ES modules, npm install webgl.tree twgl.js and import { setCamera, axes, tree } from 'webgl.tree'; the ES build keeps twgl.js, @nakednous/tree and @nakednous/host external, so a bundler or an import map resolves them.


Architecture

webgl.tree is the bridge layer of an engine-free stack — the one place that touches the GPU API. Its WebGPU twin, webgpu.tree, realizes the same surface.

  application  (imports twgl itself — createProgramInfo, createBufferInfoFromArrays, …)
      │
      ▼
  webgl.tree                  ← this package: the GPU, thinly
      │
      ├── @nakednous/host     ← pointer, handles, players, streams, media, labels, orbit
      │
      ├── @nakednous/tree     ← math, spaces, animation, visibility, gizmo generators
      │
      └── twgl                ← peer dependency, external to the bundle

@nakednous/ui is an optional peer of the application, not a dependency of the bridge. Dependency direction is strict: { tree, host } ← webgl.tree; nothing flows back.

What it takes from twgl. The ceremony, and nothing else: createProgramInfo · createBufferInfoFromArrays · setAttribInfoBufferFromArray · setBuffersAndAttributes · setUniforms · drawBufferInfo · createFramebufferInfo · resizeFramebufferInfo · bindFramebufferInfo · createTexture · setTextureFromElement. The math is @nakednous/tree's, so twgl.m4, twgl.v3 and twgl.primitives are never called — and twgl ships exactly that ceremony as its core build (twgl.js), the full build (twgl-full.js) adding the math and the generators. A consumer that needs no more than the bridge does can resolve twgl.js to the core build (it is a UMD script: an import map wants a CDN's ESM transform of it, a bundler takes it as it is), and a bundler that resolves the full ESM build instead shakes the unused math and generators away on its own.


The surface

Every export is a free function taking gl first; per-context state lives in a registry keyed by gl, created on first use and released by dispose(gl). Options object last.

| call | what it does | |---|---| | init(gl, { host, raf }) · dispose(gl) · viewOf(gl) | attach a host (its view bag receives the camera, its labels the gizmo anchors), release every GPU resource the bridge made, read the view bag a host-less application maps through | | setCamera(gl, V, P) · setCamera(gl, cam) | install the view and projection every draw reads; the state form is the seam a track, a helm or the orbit fills | | bind(gl, prog, uniforms) | useProgram + setUniforms | | buffer(gl, arrays) | a twgl bufferInfo from the arrays shape — host.loadMesh's mesh, a loadModel part's, tree.platonic's, twgl's primitives — under the bridge's attribute names: position → aPosition, normal → aNormal, tangent → aTangent, texcoord → aTexCoord, color → aColor, joints → aJoints (unnormalised), weights → aWeights; any other key holding numbers keeps its own name — a custom attribute (mesh.aHeat = { numComponents: 1, data } → in float aHeat), or a renamed one for a shader written otherwise ({ ...mesh, aUV: mesh.texcoord, texcoord: undefined }) — the rest (a mesh's bounds, a generator's count and labels) are skipped — so a mesh from host.loadMesh, tree.platonic or a twgl primitive goes in whole | | draw(gl, obj, M) · drawInstanced(gl, obj, n, M) | attributes, then the declared transforms — uModelMatrix · uViewMatrix · uModelViewMatrix · uProjectionMatrix · uModelViewProjectionMatrix · uNormalMatrix, each only if the program declares it — then drawBufferInfo; an attribute the program declares and obj lacks reads GL's constant value, (0, 0, 0, 1) unless gl.vertexAttrib* set another — never an earlier draw's array — so meshes carrying different attributes share a program | | renderTarget(gl, opts) · SCREEN | canvas-sized or { width, height }; { depth: true } a depth texture only; { depthTexture: true } colour plus a sampleable depth; { color: ['a', 'b'] } named multiple targets; { depth: false } colour only; { float: true } RGBA16F; .color, .depth, .a, resize, dispose; route passes with twgl's bindFramebufferInfo(gl, fbo) | | program(gl, frag) · fullscreen(gl) · filter(gl, prog, uniforms) | a fullscreen pass from its fragment stage; the covering quad, aTexCoord bottom-up; bind + draw with the depth test off, uSource the image (bound as tex0 too, for shaders written to p5's convention), uResolution and uTexelSize filled iff declared | | image(gl, tex, { x, y, width, height, tint, mask, blend }) | a texture onto the current target, rect in target pixels from the bottom-left, default cover; RED … masks, NORMAL · ADD · MULTIPLY blends | | pipe(gl, source, passes, opts) · releasePipe(gl, key) | the ping-pong chain over cached colour-only targets; passes are programs or { program, uniforms }; display, key, ping / pong, clear, clearFn, draw | | axes · grid · hermite · viewFrustum · trackPath · helmRig · handleLocus · pane | the scene gizmos over the core generators: (gl, subject?, { M, color, bits, size, depth, width }); viewFrustum takes a camera state, a track or { mat4Eye, mat4Proj } plus nearTexture / farTexture; trackPath draws markers and HANDLES; helmRig orients to the helm's frame and, with { x, y, size, tilt }, composites a corner readout; handleLocus draws a host handle's dot, aim and locus | | beginHUD(gl) · endHUD(gl) · cross · bullsEye | an orthographic camera over the viewport in y-down pixels with the depth test off; the two screen-space gizmos wrap themselves in it when called outside | | readPixel(gl, fbo, x, y) · pick(gl, x, y, drawFn, opts) | one pixel back through a PBO and a fence, polled on the bridge's own frame loop; colour-id scene picking on a 1×1 target through mat4Pick, paint(id) handed to drawFn, ids 1 … 2²⁴ − 1, 0 a miss | | texture(gl, source, opts) · upload(gl, tex, source) · cubemap(gl, faces, opts) | over twgl's createTexture and setTextureFromElement; images and elements flipped into GL's bottom-up space at upload, so nothing downstream flips | | mapLocation(gl, out, x, y, z, from, to) · mapDirection(gl, out, dx, dy, dz, from, to) · unproject(gl, outO, outD, sx, sy) · fragCoord(gl, out, x, y) · pixelRatio(gl, eyeZ) · mat4Viewport(gl, out) | points, directions and rays between spaces through the installed camera and the current viewport; a screen-space pixel as gl_FragCoord; world units per logical pixel at an eye-space depth; the viewport matrix W, NDC to logical pixels |

Not exported: the internal flat, line, wide-line and pass-through programs. A hero binds its own shaders.


The frame

setCamera(gl, cam)                 // 1  install V and P; the host's view bag follows
if (!h.update()) orbit.update()    // 2  the host's handles, then the orbit on what they left
bindFramebufferInfo(gl, target)    // 3  route a pass with twgl's verb; gl.clear is yours
bind(gl, prog, uniforms)           // 4
draw(gl, mesh, M)                  // 5  the declared transforms uploaded
axes(gl) · trackPath(gl, track)    // 6  gizmos, each binding and restoring its own program
pipe(gl, target, passes)           // 7  post, displayed through image
beginHUD(gl) … endHUD(gl)          // 8  screen space

Depth is the application's to clear; filter, image and the HUD disable it and restore it. Texture orientation is GL's, settled at upload. The bridge tracks no current target: twgl sets the viewport on bind and every bridge call reads it back.


Seams

| seam | direction | contract | |---|---|---| | view bag | bridge → host | setCamera(gl, cam) writes the attached host's view; without one, viewOf(gl) | | gizmo arrays | tree → bridge | the generators write the bridge's cached arrays; the returned count sizes the buffer, grown when exceeded | | label anchors | tree → bridge → host | helmRig's identify anchors go to host.labels as one-frame labels | | uniforms bag | ui → bridge | a panel's target writes u* names; bind(gl, prog, bag) reads them | | camera state | tree ↔ host ↔ bridge | plain data: a track, a helm, the orbit write it; the bridge only reads it | | id program | application → bridge | pick's opts.program for a displaced surface; paint sets its uColor |


Development

npm test                                          # node:test — the pure parts against a gl stub
npm run build                                     # rollup → dist/index.js, twgl.js external
npx browser-sync start --config bs-config.cjs     # the harness under testing/

Acknowledgements

  • twgl (Gregg Tavares) — the layer underneath: programs, buffers, textures, framebuffers; the arrays shape buffer takes is its own.
  • The numeric core's and the host's acknowledgements — glTF 2.0, OBJ, three.js — are in @nakednous/tree and @nakednous/host.

License

AGPL-3.0-only
© JP Charalambos