For years, if you wanted to build a web-based 3D configurator, a mobile app, or a server-side pipeline that processed SketchUp (.skp) files, you had one choice: integrate Trimbleβs official, closed-source C++ SDK.
This meant managing heavy native binaries, dealing with cross-platform compiling issues, and complying with proprietary licenses.
We wanted to change that. Today, we are open-sourcing OpenSKP β the world's first pure, multi-language binary parser and WebGL viewer for modern SketchUp (v2021+) files. It runs natively in Python, TypeScript, Dart, and .NET with zero native dependencies.
Here is how we reverse-engineered the format and built the rendering engine.
π Dissecting the SketchUp VFF Container
Starting in SketchUp 2021, the file structure changed from a legacy OLE compound document to a Versioned File Format (VFF).
Under the hood, a modern .skp file is actually a ZIP archive disguised with a custom header.
If you read the first few bytes of a .skp file, you will find:
- A 4-byte VFF magic number (
FF FE FF 0E). - A UTF-16 encoded version string indicating the SketchUp build (e.g.,
{24.0.594}). - A local ZIP file marker (
PK\x03\x04).
By finding the byte offset of the PK marker, we can seek directly to it and extract the ZIP contents natively in memory using standard libraries (zipfile in Python or fflate in JavaScript).
Inside the zip, we find:
-
model.dat: The core binary buffer containing all 3D geometry and instance nodes. -
materials/: Folder containing XML files defining colors and opacity for each material. -
meta/model_thumbnail.png: A pre-rendered PNG preview of the model.
π³ Parsing the TLV (Tag-Length-Value) Tree
The model.dat file is encoded using a binary TLV (Tag-Length-Value) schema. Every node in the tree starts with:
- Tag (2 bytes): Indicates the entity type (in hex).
- Length (4 bytes): Indicates the size of the payload.
- Value (variable bytes): The payload itself or child TLV nodes.
To parse it, we recursively traverse the buffer starting at offset 0. Here are the key tags we reverse-engineered:
-
F601: Root model container -
7C15: Component definition (defining reusable 3D geometries) -
6419: Component instance (placing a definition in 3D space) -
C409: 3D Vertex (coordinate payloads) -
B80B: Edge (linking two vertices) -
AC0D: Face (linking edge loops and defining normals) -
D007&D207: Component metadata and Layer/Tag assignments
Here is the core recursive TLV parser implemented in Python:
def parse_tlv_recursive(data, start, end, container_tags):
pos = start
elements = []
while pos < end - 6:
tag_bytes = data[pos:pos+2]
size = struct.unpack_from('<I', data, pos+2)[0]
if pos + 6 + size > end:
break
tag_hex = tag_bytes.hex().upper()
children = []
if tag_hex in container_tags and size > 0:
children = parse_tlv_recursive(data, pos+6, pos+6+size, container_tags)
elements.append({
'tag': tag_hex,
'size': size,
'children': children,
'payload': data[pos+6 : pos+6+size] if not children else b''
})
pos += 6 + size
return elements
π 3D Planar Triangulation
SketchUp faces can be complex N-gon polygons containing outer borders and inner holes (like a wall with window cutouts). To render them in WebGL or export to GLB, we must triangulate these loops.
Since the vertices are defined in 3D space, we:
- Retrieve the face's outward-facing normal vector from the
AD0Dtag. - Project the 3D vertices onto a local 2D plane perpendicular to the normal.
- Perform a 2D polygon triangulation (handling holes) using libraries like Earcut (JS) or Shapely (Python).
- Map the resulting 2D triangle indices back to the original 3D vertex IDs.
π Getting Started
OpenSKP supports multiple environments natively. Here is how you can use it today:
Python π (Server-Side Pipelines)
pip install openskp
from openskp import SkpFile
from openskp.export import glb
# Open and parse the model
skp = SkpFile.open("villa.skp")
model = skp.parse()
print(f"SketchUp Version: {model.version}")
print(f"Total Layers: {len(model.layers)}")
# Export directly to web-ready GLB
glb.export(skp, "output.glb")
TypeScript / Browser π (Client-Side Rendering)
npm install openskp
import { parseSkp } from 'openskp';
// Parse array buffer locally in the browser
const reader = new FileReader();
reader.onload = (e) => {
const model = parseSkp(e.target.result);
console.log("Parsed Layers:", model.layers);
// Render model._glbPrimitives directly into a Three.js scene!
};
reader.readAsArrayBuffer(file);
π Production Battle-Tested
OpenSKP is already running in production to power real-time 3D configurations for framing systems at Frame-Smart!
Special thanks to Noor Ali Qureshi (@nooraliqureshi) for contributing crucial parsing fixes that enabled seamless support across multiple legacy and modern SketchUp file versions.
βοΈ Contributions welcome!
OpenSKP is licensed under the MIT License. If you want to contribute, check out the source code, open issues, or submit PRs on GitHub:
Top comments (0)