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

@polar3d/model-converter

v1.1.1

Published

Universal 3D model format converter supporting GLB, GLTF, OBJ, STL, and 3MF formats

Readme

@polar3d/model-converter

Universal 3D model format converter supporting GLB, GLTF, OBJ, STL, and 3MF formats. Built on Three.js with TypeScript support.

Features

  • Multiple Format Support: Convert between GLB, GLTF, OBJ, STL, and 3MF
  • TypeScript: Full type safety and IDE autocomplete
  • Zero Config: Works out of the box with sensible defaults
  • Browser & Node.js: Universal compatibility
  • Batch Processing: Convert multiple files with concurrency control
  • Sliced File Detection: Automatically detects and rejects sliced 3MF files
  • Model Transformations: Scale, center, and coordinate system conversion
  • Detailed Metadata: Get vertex count, face count, bounding box, and more
  • Progress Tracking: Monitor conversion progress for large files

Installation

npm install @polar3d/model-converter three

or

yarn add @polar3d/model-converter three

Note: three is a peer dependency and must be installed separately.

Quick Start

Simple Conversion

import { glbToStl } from "@polar3d/model-converter";

// Convert a GLB file to STL
const glbUrl = "https://example.com/model.glb";
const stlBlob = await glbToStl(glbUrl);

// Download the result
const url = URL.createObjectURL(stlBlob);
const a = document.createElement("a");
a.href = url;
a.download = "model.stl";
a.click();

With Options

import { glbToStl } from "@polar3d/model-converter";

const stlBlob = await glbToStl("model.glb", {
  binary: true, // Export as binary STL (default)
  scale: 2.0, // Scale model by 2x
  center: true, // Center at origin
  includeNormals: true, // Include vertex normals
});

Using the Main Converter Class

import { ModelConverter } from "@polar3d/model-converter";

const result = await ModelConverter.convert(
  "model.glb", // Input (URL, Blob, ArrayBuffer, or File)
  "glb", // Input format
  "stl", // Output format
  {
    binary: true,
    scale: 1.5,
    center: true,
  },
);

console.log("Converted!", result.metadata);
// { vertices: 1234, faces: 456, fileSize: 98765, format: 'stl', boundingBox: {...} }

Supported Conversions

Input Formats

  • GLB - Binary GLTF (.glb)
  • GLTF - JSON GLTF (.gltf)
  • OBJ - Wavefront (.obj)
  • STL - STereoLithography (.stl)
  • 3MF - 3D Manufacturing Format (.3mf) - Unsliced only

Output Formats

  • STL - Binary or ASCII (.stl)
  • OBJ - Wavefront (.obj)
  • GLTF - JSON GLTF (.gltf)
  • GLB - Binary GLTF (.glb)

Convenience Functions

import {
  glbToStl,
  gltfToStl,
  objToStl,
  stlToObj,
  stlToGlb,
  glbToObj,
  objToGlb,
  threemfToStl,
  anyToStl,
  autoToStl,
} from "@polar3d/model-converter";

// GLB → STL
const stl1 = await glbToStl("model.glb");

// OBJ → STL
const stl2 = await objToStl("model.obj");

// STL → OBJ
const obj = await stlToObj("model.stl");

// Auto-detect format and convert to STL
const stl3 = await autoToStl("model.glb"); // Auto-detects GLB

// Any format to STL
const stl4 = await anyToStl("model.3mf", "3mf");

API Reference

ModelConverter Class

convert(input, inputFormat, outputFormat, options?)

Main conversion method.

Parameters:

  • input: URL string, Blob, ArrayBuffer, or File
  • inputFormat: 'glb' | 'gltf' | 'obj' | 'stl' | '3mf'
  • outputFormat: 'stl' | 'obj' | 'gltf' | 'glb'
  • options?: ConversionOptions (see below)

Returns: Promise<ConversionResult>

convertBatch(inputs, outputFormat, options?)

Convert multiple files in parallel.

const results = await ModelConverter.convertBatch(
  [
    { input: "model1.glb", inputFormat: "glb", name: "Model 1" },
    { input: "model2.obj", inputFormat: "obj", name: "Model 2" },
  ],
  "stl",
  { concurrency: 4 },
);

ConversionOptions

interface ConversionOptions {
  binary?: boolean; // Binary vs ASCII output (default: true)
  includeNormals?: boolean; // Include normal vectors (default: true)
  mergeMeshes?: boolean; // Merge multiple meshes (default: false)
  scale?: number; // Scale factor (default: 1.0)
  center?: boolean; // Center at origin (default: false)
  flipYZ?: boolean; // Flip Y/Z axes (default: false)
  outputFormat?: "blob" | "arraybuffer" | "base64"; // Output type (default: 'blob')
  onProgress?: (progress: number) => void; // Progress callback
}

ConversionResult

interface ConversionResult {
  blob: Blob; // Converted file as Blob
  arrayBuffer?: ArrayBuffer; // ArrayBuffer (if requested)
  base64?: string; // Base64 string (if requested)
  metadata: ModelMetadata; // Model information
}

ModelMetadata

interface ModelMetadata {
  vertices: number; // Vertex count
  faces: number; // Triangle/face count
  fileSize: number; // Output file size in bytes
  format: string; // Output format
  boundingBox?: {
    min: { x: number; y: number; z: number };
    max: { x: number; y: number; z: number };
    size: { x: number; y: number; z: number };
  };
}

Advanced Usage

Attribution

By default, all converted files include "Created using Polar3d.com" attribution:

  • STL (Binary): Added to the 80-byte header
  • STL (ASCII): Added to the solid name line
  • OBJ: Added as comment lines at the top
  • GLTF/GLB: Added to asset metadata (generator and copyright fields)

To disable attribution:

const result = await glbToStl("model.glb", {
  addAttribution: false, // Disable attribution
});

Batch Conversion with Progress

import { ModelConverter } from "@polar3d/model-converter";

const inputs = [
  { input: "model1.glb", inputFormat: "glb" as const },
  { input: "model2.obj", inputFormat: "obj" as const },
  { input: "model3.3mf", inputFormat: "3mf" as const },
];

const results = await ModelConverter.convertBatch(inputs, "stl", {
  concurrency: 2, // Process 2 at a time
  binary: true,
  scale: 1.0,
});

results.forEach((result, index) => {
  if (result.success) {
    console.log(`✓ Converted ${inputs[index].input}`);
    console.log(`  Vertices: ${result.result!.metadata.vertices}`);
    console.log(`  Size: ${result.result!.metadata.fileSize} bytes`);
  } else {
    console.error(`✗ Failed ${inputs[index].input}:`, result.error);
  }
});

Coordinate System Conversion

// Convert from Z-up to Y-up coordinate system
const result = await ModelConverter.convert("model.stl", "stl", "obj", {
  flipYZ: true, // Swap Y and Z axes
});

Get Detailed Metadata

import { convertWithMetadata } from "@polar3d/model-converter";

const result = await convertWithMetadata("model.glb", "glb", "stl");

console.log("Metadata:", result.metadata);
// {
//   vertices: 8462,
//   faces: 4231,
//   fileSize: 123456,
//   format: 'stl',
//   boundingBox: {
//     min: { x: -50, y: -50, z: 0 },
//     max: { x: 50, y: 50, z: 100 },
//     size: { x: 100, y: 100, z: 100 }
//   }
// }

Handling Sliced 3MF Files

import { ModelConverter, SlicedFileError } from "@polar3d/model-converter";

try {
  const result = await ModelConverter.convert("sliced.3mf", "3mf", "stl");
} catch (error) {
  if (error instanceof SlicedFileError) {
    console.error("This is a sliced 3MF file. Please use an unsliced model.");
  }
}

Format Detection

import { detectFormat } from "@polar3d/model-converter";

const format = detectFormat("model.glb");
console.log(format); // 'glb'

const file = new File([blob], "model.stl");
const fileFormat = detectFormat(file);
console.log(fileFormat); // 'stl'

Error Handling

import {
  ModelConverter,
  UnsupportedFormatError,
  SlicedFileError,
  ConversionError,
} from "@polar3d/model-converter";

try {
  const result = await ModelConverter.convert("model.glb", "glb", "stl");
} catch (error) {
  if (error instanceof UnsupportedFormatError) {
    console.error("Format not supported:", error.message);
  } else if (error instanceof SlicedFileError) {
    console.error("Sliced files not supported:", error.message);
  } else if (error instanceof ConversionError) {
    console.error("Conversion failed:", error.message, error.cause);
  } else {
    console.error("Unknown error:", error);
  }
}

Use Cases

AI Model Processing Pipeline

import { glbToStl } from "@polar3d/model-converter";

// Convert AI-generated GLB model to printable STL
async function processAIModel(glbUrl: string) {
  const stlBlob = await glbToStl(glbUrl, {
    binary: true,
    center: true, // Center for 3D printing
    scale: 1.0,
  });

  // Upload to server, send to slicer, etc.
  return stlBlob;
}

3D Model Library

import { ModelConverter } from "@polar3d/model-converter";

// Convert user uploads to standard format
async function standardizeModel(file: File) {
  const format = detectFormat(file);

  if (format && format !== "stl") {
    const result = await ModelConverter.convert(file, format, "stl", {
      binary: true,
    });

    return new File([result.blob], file.name.replace(/\.\w+$/, ".stl"));
  }

  return file;
}

Browser vs Node.js

Browser

import { glbToStl } from "@polar3d/model-converter";

// From URL
const blob = await glbToStl("https://example.com/model.glb");

// From File input
const input = document.querySelector<HTMLInputElement>('input[type="file"]');
const file = input.files[0];
const stlBlob = await glbToStl(file);

Node.js

import { ModelConverter } from "@polar3d/model-converter";
import { readFileSync, writeFileSync } from "fs";

// Read file
const buffer = readFileSync("model.glb").buffer;

// Convert
const result = await ModelConverter.convert(buffer, "glb", "stl");

// Write result
const arrayBuffer = await result.blob.arrayBuffer();
writeFileSync("model.stl", Buffer.from(arrayBuffer));

Performance Tips

  1. Use batch conversion for multiple files with controlled concurrency
  2. Binary formats are faster and smaller than ASCII
  3. Disable center/scale if not needed to improve performance
  4. Stream large files instead of loading entirely into memory when possible

Limitations

  • Sliced 3MF files (containing gcode) are not supported
  • FBX and PLY formats are not currently supported
  • Texture information may be lost in some conversions
  • Some advanced GLTF features (animations, morph targets) may not convert perfectly

License

MIT

Contributing

Contributions are welcome! Please open an issue or pull request on GitHub.

Support

For issues and questions, please visit: https://github.com/Polar3D/model-converter/issues

Credits

Built with ❤️ by Polar3D for the 3D printing community.