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

@vctrl/core

v0.18.0

Published

vctrl/core is the foundational package providing server-side 3D model processing capabilities. It contains the core classes for loading, optimizing, and exporting 3D models using glTF-Transform and Three.js, designed for Node.js environments.

Readme

@vctrl/core

Server-side 3D model processing for Node.js. This package provides the shared loading, optimization, and export pipeline used by other Vectreal packages.


Installation

npm install @vctrl/core
# or
pnpm add @vctrl/core

@vctrl/core targets Node.js only. It uses Sharp for server-side texture compression, which requires a native build.


Module overview

| Module | Import path | Description | | ---------------- | ----------------------------- | ------------------------------------------------------------------ | | ModelLoader | @vctrl/core/model-loader | Load model files into glTF-Transform Document or Three.js scenes | | ModelOptimizer | @vctrl/core/model-optimizer | Run optimization passes and export optimized output | | ModelExporter | @vctrl/core/model-exporter | Export Document or Three.js objects to GLB or GLTF | | SceneAsset | @vctrl/core | Scene asset serialization helpers and shared server payload types |


ModelLoader

import { ModelLoader } from '@vctrl/core/model-loader'
import { readFile } from 'node:fs/promises'

const loader = new ModelLoader()

const result = await loader.loadFromFile('model.glb')

const buffer = await readFile('model.glb')
const resultFromBuffer = await loader.loadFromBuffer(
	new Uint8Array(buffer),
	'model.glb'
)

const sceneResult = await loader.loadToThreeJS('model.glb')

Export methods

| Method | Description | | --------------------------------------------------------- | ----------------------------------------------------- | | loadFromFile(input) | Load from file path in Node or browser File | | loadFromBuffer(buffer, fileName) | Load from Uint8Array data | | loadGLTFWithAssets(...) / loadGLTFWithFileAssets(...) | Load GLTF with external resources | | documentToThreeJS(document, modelResult) | Convert glTF-Transform Document to a Three.js scene | | loadToThreeJS(input) | Load and convert to Three.js scene | | loadGLTFWithAssetsToThreeJS(...) | GLTF plus assets directly to Three.js | | isSupportedFormat(fileName) | Validate extension support | | getSupportedExtensions() | Return supported extensions |

documentToThreeJS requires both the Document and the original ModelLoadResult metadata object:

const loaded = await loader.loadFromFile('model.glb')
const threeResult = await loader.documentToThreeJS(loaded.data, loaded)

ModelOptimizer

import { ModelOptimizer } from '@vctrl/core/model-optimizer'

const optimizer = new ModelOptimizer()

await optimizer.loadFromBuffer(modelBuffer)
await optimizer.simplify({ ratio: 0.5 })
await optimizer.deduplicate()
await optimizer.quantize({ quantizePosition: 14 })
await optimizer.compressTextures({ quality: 80 })

const optimizedBuffer = await optimizer.export()

Methods

| Method | Options | Description | | --------------------------- | ------------------------------------------------------- | -------------------------------------------- | | loadFromThreeJS(model) | - | Load a Three.js scene into the optimizer | | loadFromBuffer(buf) | - | Load GLB binary data | | loadFromFile(path) | - | Load from file path | | loadFromJSON(json) | - | Load serialized glTF JSON and resources | | simplify(opts) | { ratio: number } | Mesh simplification from 0.0 to 1.0 | | deduplicate(opts) | DedupOptions | Remove duplicate geometry and material data | | quantize(opts) | QuantizeOptions | Reduce precision to reduce size | | optimizeNormals(opts) | NormalsOptions | Recompute or normalize normal data | | compressTextures(opts) | TextureCompressOptions | Server-side texture compression | | optimizeAll(opts) | { simplify?, dedup?, quantize?, normals?, textures? } | Run batch optimization passes | | getReport() | - | Return before and after optimization metrics | | export() / exportJSON() | - | Export optimized GLB or JSON glTF document | | hasModel() / reset() | - | Model state utilities |

Optimization options reference

simplify(options?: SimplifyOptions)

| Option | Type | Default | Notes | | ------- | -------- | ------- | -------------------------------------------------------------- | | ratio | number | 0.5 | Target simplification ratio. Lower values are more aggressive. | | error | number | 0.001 | Allowed geometric error threshold for simplification. |

deduplicate(options?: DedupOptions)

| Option | Type | Notes | | ----------- | --------- | ------------------------------------- | | textures | boolean | Forwarded to glTF-Transform dedup() | | materials | boolean | Forwarded to glTF-Transform dedup() | | meshes | boolean | Forwarded to glTF-Transform dedup() | | accessors | boolean | Forwarded to glTF-Transform dedup() |

quantize(options?: QuantizeOptions)

| Option | Type | Notes | | ------------------ | -------- | ---------------------------------------- | | quantizePosition | number | Forwarded to glTF-Transform quantize() | | quantizeNormal | number | Forwarded to glTF-Transform quantize() | | quantizeColor | number | Forwarded to glTF-Transform quantize() | | quantizeTexcoord | number | Forwarded to glTF-Transform quantize() |

optimizeNormals(options?: NormalsOptions)

| Option | Type | Notes | | ----------- | --------- | ------------------------------------------------- | | overwrite | boolean | Recompute normals even when normals already exist |

compressTextures(options?: TextureCompressOptions)

| Option | Type | Current behavior | | ----------------------- | --------------------------- | ------------------------------------------------------------------------------- | | resize | [number, number] | Passed to compressTexture() | | targetFormat | 'webp' \| 'jpeg' \| 'png' | Passed to compressTexture() | | quality | number | Passed to compressTexture() | | requestTimeoutMs | number | Present in type but not consumed by the current ModelOptimizer implementation | | maxTextureUploadBytes | number | Present in type but not consumed by the current ModelOptimizer implementation | | maxRetries | number | Present in type but not consumed by the current ModelOptimizer implementation | | maxConcurrentRequests | number | Present in type but not consumed by the current ModelOptimizer implementation | | serverOptions | ServerOptions | Accepted in type but stripped before local Sharp compression |

When Sharp is unavailable, compressTextures falls back to basic texture optimization using dedup and prune instead of throwing.

optimizeAll(options?)

await optimizer.optimizeAll({
	simplify: { ratio: 0.6 },
	dedup: {},
	quantize: { quantizePosition: 14 },
	normals: { overwrite: false },
	textures: { targetFormat: 'webp', quality: 80 }
})

Execution order is fixed:

  1. simplify unless false
  2. deduplicate unless false
  3. quantize unless false
  4. optimizeNormals unless false
  5. compressTextures only when textures is provided

Calling optimizeAll() with no arguments runs simplify, dedup, quantize, and normals. Texture compression is opt-in.

getReport() return structure

getReport() includes:

  • originalSize, optimizedSize
  • compressionRatio as originalSize / optimizedSize
  • appliedOptimizations
  • stats before and after metrics for vertices, triangles, materials, texture size in bytes (textures), texture asset count (texturesCount), textureResolutions, meshes, and nodes
const report = await optimizer.getReport()
console.log(report.stats.textureResolutions.before)
console.log(report.stats.textureResolutions.after)

ModelExporter

import { ModelExporter } from '@vctrl/core/model-exporter'

const exporter = new ModelExporter()

const glb = await exporter.exportThreeJSGLB(scene, {})
const gltf = await exporter.exportThreeJSGLTF(scene)
const zip = await exporter.createZIPArchive(gltf, 'model')

Primary methods

| Method | Description | | ------------------------------------- | ------------------------------------------------ | | exportDocumentGLB(document) | Export a glTF-Transform Document to GLB | | exportDocumentGLTF(document) | Export a Document to GLTF JSON and assets | | exportThreeJSGLB(object, options) | Export a Three.js object to GLB | | exportThreeJSGLTF(object) | Export a Three.js object to GLTF JSON and assets | | createZIPArchive(result, baseName?) | Bundle GLTF and assets into a zip | | saveToFile(result, filePath) | Persist export result on the Node filesystem |

exportThreeJSGLB(object, options) accepts modifiedTextureResources in its options type, but that field is currently ignored for direct Three.js GLB export.


Use in API routes

@vctrl/core is used by the Vectreal Platform server-side optimization endpoint. A minimal API route example:

import { ModelOptimizer } from '@vctrl/core/model-optimizer'

export async function POST(request: Request) {
	const formData = await request.formData()
	const file = formData.get('file') as File

	const buffer = Buffer.from(await file.arrayBuffer())

	const optimizer = new ModelOptimizer()
	await optimizer.loadFromBuffer(new Uint8Array(buffer))
	await optimizer.simplify({ ratio: 0.7 })
	await optimizer.compressTextures({ quality: 80 })

	const optimized = await optimizer.export()

	return new Response(optimized, {
		headers: { 'Content-Type': 'model/gltf-binary' }
	})
}

Requirements

| Requirement | Version | | ----------- | ----------- | | Node.js | 18 or later | | sharp | ^0.34 |


Related docs


Source

The full source and README live in packages/core.

License

AGPL-3.0-only. See LICENSE.md.