3d-file-parsers
v1.0.0
Published
Lightweight, zero-dependency parsers for niche 3D file formats (M3D, X3D, IrrMesh, Terragen, PTS) that major libraries don't support. Returns raw typed arrays — works with Three.js, Babylon.js, or any WebGL framework.
Maintainers
Readme
3d-file-parsers
Lightweight, framework-agnostic parsers for niche 3D file formats that Three.js, Babylon.js, and Assimp.js don't natively support.
All parsers return raw Float32Array / Uint32Array data — plug them into any WebGL/WebGPU renderer with zero adapter code.
Used in production at 3D Tools by FabRapid — a free, browser-based 3D file converter supporting 70+ input formats and 23 output formats with zero server uploads.
Supported Formats
| Parser | Format | Extension | Description |
|--------|--------|-----------|-------------|
| parseM3D | Model3D | .m3d | Binary mesh format with optional zlib compression. Supports int16/float32/float64 vertex precision and polygon meshes. |
| parseX3D | X3D | .x3d | XML-based 3D scene format (ISO standard). Handles IndexedFaceSet, IndexedTriangleSet, and Box primitives with material colors. |
| parseIrrMesh | Irrlicht Mesh | .irrmesh | XML mesh format from the Irrlicht Engine. Supports standard, 2tcoords, and tangents vertex types. |
| parseTerragen | Terragen | .ter | Binary heightmap terrain format. Automatically triangulates the elevation grid into a renderable mesh. |
| parsePTS | Leica PTS | .pts | ASCII point cloud format used by Leica laser scanners. Supports XYZ, XYZ+intensity, and XYZ+intensity+RGB columns. |
Why This Exists
When building 3d-tools.fabrapid.com, we needed to support dozens of 3D formats in the browser. Most common formats (GLTF, OBJ, STL, FBX) are well-covered by Three.js loaders and Assimp WASM. But several formats — M3D, X3D, IrrMesh, Terragen, and PTS — had no JavaScript parser available, or the existing implementations were tightly coupled to a specific rendering engine.
We wrote these parsers from scratch based on the official format specifications, tested them against real-world files, and extracted them into this standalone package so others don't have to.
Design principles:
- Zero framework dependency — returns plain typed arrays, not Three.js objects
- Isomorphic — works in browsers and Node.js (XML parsers accept a custom
DOMParser) - Tiny footprint — only
fflate(3KB gzipped) for M3D's zlib decompression; everything else is zero-dep - TypeScript-first — full type definitions with documented interfaces
Installation
# pnpm
pnpm add 3d-file-parsers
# npm
npm install 3d-file-parsers
# yarn
yarn add 3d-file-parsersQuick Start
Import everything
import { parseM3D, parseX3D, parseIrrMesh, parseTerragen, parsePTS } from '3d-file-parsers';Or import individual parsers (tree-shakeable)
import { parseM3D } from '3d-file-parsers/m3d';
import { parseX3D } from '3d-file-parsers/x3d';
import { parseIrrMesh } from '3d-file-parsers/irrmesh';
import { parseTerragen } from '3d-file-parsers/terragen';
import { parsePTS } from '3d-file-parsers/pts';Usage
Parse M3D (Model3D)
import { parseM3D } from '3d-file-parsers';
const response = await fetch('model.m3d');
const buffer = await response.arrayBuffer();
const { positions, normals, indices, vertexCount, faceCount } = parseM3D(buffer);
console.log(`Vertices: ${vertexCount}, Faces: ${faceCount}`);
// positions: Float32Array — flat xyz array (length = vertexCount * 3)
// normals: Float32Array | null — auto-computed face normals
// indices: Uint32Array — triangle indicesParse X3D
import { parseX3D } from '3d-file-parsers';
const response = await fetch('scene.x3d');
const text = await response.text();
const { meshes } = parseX3D(text);
for (const mesh of meshes) {
console.log(`Mesh: ${mesh.indices.length / 3} triangles`);
// mesh.positions: Float32Array
// mesh.normals: Float32Array | null
// mesh.indices: Uint32Array
// mesh.color?: [r, g, b] — diffuse material color (0-1)
}Node.js usage (X3D / IrrMesh)
XML-based parsers need a DOMParser. In Node.js, pass one from @xmldom/xmldom:
import { DOMParser } from '@xmldom/xmldom';
import { parseX3D } from '3d-file-parsers';
const { meshes } = parseX3D(xmlString, new DOMParser());Parse IrrMesh (Irrlicht)
import { parseIrrMesh } from '3d-file-parsers';
const response = await fetch('model.irrmesh');
const text = await response.text();
const { buffers } = parseIrrMesh(text);
for (const buf of buffers) {
console.log(`Buffer: ${buf.vertexCount} vertices, ${buf.faceCount} faces`);
// buf.positions: Float32Array
// buf.normals: Float32Array
// buf.indices: Uint32Array
}Parse Terragen Terrain
import { parseTerragen } from '3d-file-parsers';
const response = await fetch('terrain.ter');
const buffer = await response.arrayBuffer();
const { positions, indices, normals, xpts, ypts } = parseTerragen(buffer);
console.log(`Terrain grid: ${xpts} x ${ypts} (${xpts * ypts} vertices)`);
// Automatically triangulated — ready to render as a meshParse PTS Point Cloud
import { parsePTS } from '3d-file-parsers';
const response = await fetch('scan.pts');
const buffer = await response.arrayBuffer();
const { positions, colors, pointCount, hasColor } = parsePTS(buffer);
console.log(`Points: ${pointCount}, Has color: ${hasColor}`);
// positions: Float32Array — flat xyz (length = pointCount * 3)
// colors: Float32Array | null — flat rgb normalized to 0-1Using with Three.js
The parsers return raw typed arrays. Here's how to create Three.js geometry:
import * as THREE from 'three';
import { parseM3D } from '3d-file-parsers';
const data = parseM3D(buffer);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(data.positions, 3));
if (data.normals) {
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(data.normals, 3));
}
if (data.indices.length > 0) {
geometry.setIndex(new THREE.BufferAttribute(data.indices, 1));
}
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial());
scene.add(mesh);Point cloud with Three.js
import * as THREE from 'three';
import { parsePTS } from '3d-file-parsers';
const data = parsePTS(buffer);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(data.positions, 3));
if (data.colors) {
geometry.setAttribute('color', new THREE.Float32BufferAttribute(data.colors, 3));
}
const material = new THREE.PointsMaterial({
size: 0.01,
vertexColors: data.hasColor,
});
const points = new THREE.Points(geometry, material);
scene.add(points);Using with Babylon.js
import * as BABYLON from '@babylonjs/core';
import { parseM3D } from '3d-file-parsers';
const data = parseM3D(buffer);
const mesh = new BABYLON.Mesh('model', scene);
const vertexData = new BABYLON.VertexData();
vertexData.positions = Array.from(data.positions);
vertexData.indices = Array.from(data.indices);
if (data.normals) {
vertexData.normals = Array.from(data.normals);
}
vertexData.applyToMesh(mesh);Using with Raw WebGL
import { parseM3D } from '3d-file-parsers';
const data = parseM3D(buffer);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, data.positions, gl.STATIC_DRAW);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.indices, gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, data.indices.length, gl.UNSIGNED_INT, 0);API Reference
parseM3D(buffer: ArrayBuffer): M3DData
| Property | Type | Description |
|----------|------|-------------|
| positions | Float32Array | Flat xyz vertex positions |
| normals | Float32Array \| null | Auto-computed vertex normals |
| indices | Uint32Array | Triangle indices |
| vertexCount | number | Number of vertices |
| faceCount | number | Number of triangles |
parseX3D(xmlText: string, domParser?): X3DData
| Property | Type | Description |
|----------|------|-------------|
| meshes | X3DMesh[] | Array of parsed meshes |
| meshes[].positions | Float32Array | Flat xyz vertex positions |
| meshes[].normals | Float32Array \| null | Computed vertex normals |
| meshes[].indices | Uint32Array | Triangle indices |
| meshes[].color | [r, g, b]? | Diffuse material color (0-1) |
parseIrrMesh(xmlText: string, domParser?): IrrMeshData
| Property | Type | Description |
|----------|------|-------------|
| buffers | IrrMeshBuffer[] | Array of mesh buffers |
| buffers[].positions | Float32Array | Flat xyz vertex positions |
| buffers[].normals | Float32Array | Vertex normals from file |
| buffers[].indices | Uint32Array | Triangle indices |
| buffers[].vertexCount | number | Vertices in this buffer |
| buffers[].faceCount | number | Triangles in this buffer |
parseTerragen(buffer: ArrayBuffer): TerragenData
| Property | Type | Description |
|----------|------|-------------|
| positions | Float32Array | Flat xyz vertex positions |
| indices | Uint32Array | Triangle indices (2 per grid quad) |
| normals | Float32Array | Computed vertex normals |
| xpts | number | Grid points along X axis |
| ypts | number | Grid points along Y axis |
parsePTS(buffer: ArrayBuffer): PTSData
| Property | Type | Description |
|----------|------|-------------|
| positions | Float32Array | Flat xyz point positions |
| colors | Float32Array \| null | RGB colors normalized 0-1 (null if no color data) |
| pointCount | number | Number of points |
| hasColor | boolean | Whether the file contained color data |
Browser Support
Works in all modern browsers with ArrayBuffer and DataView support (Chrome 49+, Firefox 42+, Safari 10+, Edge 14+).
For Node.js, XML-based parsers (X3D, IrrMesh) require a DOM parser like @xmldom/xmldom.
Related Projects
- 3D Tools by FabRapid — Free online 3D file converter (70+ formats, zero uploads, browser-only)
- 3D File Viewer — Online 3D model viewer with support for all major formats
- 3D Model Compressor — Browser-based GLB compression with before/after comparison
- Assimp — The industry-standard C++ import library (covers most common formats)
- Three.js — WebGL rendering library with many built-in loaders
- fflate — Fast JavaScript compression library (used for M3D zlib decompression)
Contributing
Contributions are welcome! If you need support for additional niche 3D formats, please open an issue.
License
MIT
