Skip to content

WASM Bindings API

API documentation for the WebAssembly bindings.

Overview

IFClite provides WebAssembly bindings for high-performance parsing and geometry processing in the browser.

flowchart LR subgraph JS["JavaScript"] App["Application"] API["@ifc-lite/parser"] end subgraph WASM["WebAssembly"] Bindings["ifc-lite-wasm"] Core["ifc-lite-core"] Geo["ifc-lite-geometry"] end App --> API --> Bindings Bindings --> Core Bindings --> Geo

Loading the WASM Module

Automatic Loading

The TypeScript packages handle WASM loading automatically:

import { IfcParser } from '@ifc-lite/parser';

// WASM is loaded automatically
const parser = new IfcParser();
await parser.parseColumnar(buffer);

Manual Loading

For custom setups:

import init, { IfcAPI } from '@ifc-lite/wasm';

// Initialize WASM
await init();

// Or with custom URL
await init('/path/to/ifc-lite_bg.wasm');

// Create API instance
const api = new IfcAPI();

IfcAPI Class

Main entry point for WASM functionality.

Constructor

class IfcAPI {
  constructor();
}

Methods

The methods below reflect the real IfcAPI surface (see packages/wasm/pkg/ifc-lite.d.ts). There is no single parse() call: scanning, geometry, and export are separate entry points.

Entity Scanning

SIMD-accelerated scanners that return entity references for the data-model layer to decode.

scanEntitiesFast(content: string): any;         // all entities, from a decoded string
scanEntitiesFastBytes(data: Uint8Array): any;   // all entities, straight from bytes (no TextDecoder)
scanGeometryEntitiesFast(content: string): any; // only entities that carry geometry

Example:

const api = new IfcAPI();
const text = await fetch('model.ifc').then(r => r.text());
const entities = api.scanEntitiesFast(text);

Geometry Pipeline

Geometry is produced in two phases: a pre-pass over the file, then one or more batched mesh-production calls.

buildPrePassOnce(data: Uint8Array): any;
buildPrePassStreaming(
  data: Uint8Array,
  onEvent: (event: unknown) => void,
  chunkSize: number,
  disabledTypeNames: string[] | null,
  skipTypeGeometry: boolean,
): any;

// jobsFlat is [id, start, end] triples from the pre-pass; the trailing args
// carry unit scale, RTC offset, void keys, and styles (see the .d.ts).
processGeometryBatch(
  data: Uint8Array,
  jobsFlat: Uint32Array,
  unitScale: number,
  /* rtcX, rtcY, rtcZ, needsShift, void + style args */
): MeshCollection;

processGeometryBatchInstanced (returns an IFNS instancing shard as a Uint8Array) and processGeometryBatchPartitioned (returns a PartitionedBatch) are variants of the same call. Each returned MeshCollection exposes length, get(i) / takeMesh(i), and RTC offsets (see Data Types).

Export

Each exporter takes the raw IFC bytes (or already-produced meshes) and returns a Uint8Array.

exportGlb(content, includeMetadata, hidden, isolated, hiddenTypesCsv, lit?): Uint8Array;
exportGlbFromMeshes(/* flattened MeshData buffers */): Uint8Array;
exportKmz(glb, latitude, longitude, altitude, xAxisAbscissa, xAxisOrdinate, name): Uint8Array;
exportObj(content, includeNormals, hidden, isolated): Uint8Array;
exportCsv(content, mode, delimiter, includeProperties): Uint8Array;
exportJson(content, pretty, includeProperties, includeQuantities): Uint8Array;
exportJsonld(content, context, includeProperties, includeQuantities, pretty, included): Uint8Array;
exportIfcx(content, onlyKnownProperties, pretty): Uint8Array;
exportStep(content, schema, included, mutationsJson): Uint8Array;
exportMerged(concatenated, lengths, schema): Uint8Array;
exportHbjson(content, name): Uint8Array;

Example:

const obj = api.exportObj(ifcContent, true, new Uint32Array(), new Uint32Array());
const hbjson = api.exportHbjson(ifcContent, 'my_model');

exportGlb fails closed: when the visible mesh set is empty it throws an Error whose message starts with NO_RENDER_GEOMETRY rather than returning an empty GLB.

Other Parsing

extractProfiles(content: string, modelIndex: number): ProfileCollection;
parseGridLines(content: string): Float32Array;                        // flat line-list vertices
parseGridAxes(content: string): GridAxisCollection;                   // axes with tags
parseAlignmentLines(content: string): Float32Array;
parseSymbolicRepresentations(content: string): SymbolicRepresentationCollection;
diagnoseGeometry(content: Uint8Array): any;                           // CSG / opening diagnostics

Diagnostics and Tuning

getPipelineDiagnostics(): any;
getMemory(): any;

setEntityIndex(ids: Uint32Array, starts: Uint32Array, lengths: Uint32Array): void;
setMergeLayers(enabled: boolean): void;
setSkipSmallCuts(on: boolean): void;
setRectParamFastPath(enabled: boolean): void;
setTessellationQuality(level?: string | null): void;
setComputeGeometryHashes(tolerance?: number | null): void;
clearPrePassCache(): void;

Getters

readonly version: string;   // build version string
readonly is_ready: boolean; // true once the API is initialized

Data Types

MeshCollection

Returned by processGeometryBatch. Holds the batch's meshes plus RTC and totals.

class MeshCollection {
  readonly length: number;
  get(index: number): MeshDataJs | undefined;      // clones (non-destructive)
  takeMesh(index: number): MeshDataJs | undefined; // moves out (read-once, faster)
  hasRtcOffset(): boolean;
  readonly rtcOffsetX: number;
  readonly rtcOffsetY: number;
  readonly rtcOffsetZ: number;
  readonly totalVertices: number;
  readonly totalTriangles: number;
  readonly buildingRotation: number | undefined;
  readonly diagnostics: any;
}

MeshDataJs

A single triangulated mesh. All typed arrays are copied to JS on access.

class MeshDataJs {
  readonly expressId: number;
  readonly ifcType: string;           // e.g. "IfcWall"
  readonly positions: Float32Array;   // xyz triplets
  readonly normals: Float32Array;     // xyz triplets
  readonly indices: Uint32Array;      // triangle indices
  readonly uvs: Float32Array;         // uv pairs (empty when untextured)
  readonly color: Float32Array;       // [r, g, b, a]
  readonly origin: Float64Array;      // per-element local-frame origin (metres)
  readonly vertexCount: number;
  readonly triangleCount: number;
  readonly geometryClass: number;     // 0 = occurrence, 1 = orphan type, 2 = instanced type
}

Pre-pass Streaming Events

buildPrePassStreaming invokes its callback with one of:

type PrePassEvent =
  | { type: 'meta'; unitScale: number; rtcOffset: [number, number, number]; needsShift: boolean; buildingRotation?: number }
  | { type: 'jobs'; jobs: Uint32Array }   // [id, start, end] triples
  | { type: 'complete'; totalJobs: number };

Error Handling

WASM functions surface failures as standard JavaScript Error objects (a Rust Result::Err is mapped across the boundary). Wrap calls that can fail:

try {
  const glb = api.exportGlb(content, true, new Uint32Array(), new Uint32Array(), '');
} catch (error) {
  if (error instanceof Error) {
    console.error('Export error:', error.message);
  }
}

Sentinel Messages

Some boundaries fail closed with a sentinel-prefixed message so callers can distinguish an expected empty result from a real failure. For example, exportGlb throws an Error whose message starts with NO_RENDER_GEOMETRY when the visible mesh set is empty, instead of returning a structurally valid but empty GLB.

Performance Tips

1. Stream the Pre-pass for Large Files

// Good: stream jobs as the file is scanned (time-to-first-geometry drops sharply)
api.buildPrePassStreaming(bytes, onEvent, 512, null, false);

// Avoid on very large files: a single blocking pre-pass over the whole file
api.buildPrePassOnce(bytes);

2. Read Each Mesh Once

// Good: takeMesh moves the mesh out (one fewer vertex-data copy per mesh)
const meshes = api.processGeometryBatch(bytes, jobsFlat, unitScale /* ... */);
for (let i = 0; i < meshes.length; i++) {
  const mesh = meshes.takeMesh(i);
  if (mesh) uploadMesh(mesh);
}

3. Release Memory

// Clean up when done
api.free();

Browser Compatibility

Feature Chrome Firefox Safari Edge
WebAssembly 57+ 52+ 11+ 16+
WASM SIMD 91+ 89+ 16.4+ 91+
Streaming 61+ 58+ 15+ 16+
Threads 74+ 79+ 14.1+ 79+

Module Size

Approximate on-disk sizes of the built packages/wasm/pkg/ artifacts (uncompressed):

Component Size
WASM binary (ifc-lite_bg.wasm) ~3.4 MB
JS glue code (ifc-lite.js) ~140 KB

scripts/build-wasm.sh targets a 1100 KB budget for the single-thread bundle and warns when the binary exceeds it (wasm-opt is currently disabled). Sizes vary by build profile and target.

Building from Source

# Install wasm-pack
cargo install wasm-pack

# Build WASM module
cd rust/wasm-bindings
wasm-pack build --target web --out-name ifc-lite --release

# Output files
# pkg/ifc-lite.js
# pkg/ifc-lite_bg.wasm
# pkg/ifc-lite.d.ts

Build Targets

Target Output Use Case
web ES modules Modern browsers
bundler CommonJS Webpack/Rollup
nodejs Node.js Server-side
no-modules Global Script tag