Skip to main content
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9 differs or is newly documented. For any other topic, switch to Current (2026.8 and earlier) in the version selector.
If you write geometry into a bundle yourself, you encode it as SGEO, a small binary format with a fixed header and a per-primitive body. Get the header wrong and consumers reject the blob on its checksum. If you only consume geometry, you either decode SGEO from the bundle’s geometry table or stream the viewer .dat that the server builds from it. You never write the .dat. Before 2026.9, as the Geometry Schema describes, geometry is a list of displayValue objects in JSON on each DataObject. In 2026.9 it is a table of blobs keyed by a geometry key, attached to objects through relations. This page covers the blob format, how the table is laid out, and what the .dat is for. For which relations attach geometry to what, see Display geometry.

Where geometry lives in a bundle

The geometry table is {versionId}.geometries.parquet, sharded into {versionId}.geometries.{n}.parquet files once the current shard holds 1536 MiB of uncompressed content. Shard zero keeps the canonical name, so a small model has one file and a reader always globs all shards. Each row holds one blob, its dense integer key, and an id that is the SHA-256 of the blob bytes. Because every producer encodes SGEO identically, the same mesh from any host gets the same id, and readers deduplicate on it. A row also records its encoding: display meshes and centerlines are SGEO, while a SOLID row carries the host’s own bytes, such as a 3dm body, with a type label. Nothing in the geometry table says which object a blob belongs to. That is a relation: DISPLAY, SOLID, or CENTERLINE from the object, or DEFINES from a definition node whose placements reuse the blob. Each of those keeps its own ordinal per object, so an object’s first display mesh is ordinal zero whether or not a solid was written before it.

The SGEO header

Every blob starts with 16 bytes, little-endian. Conventions that every encoder follows:
  • The body starts 8-byte aligned at 0x10, and every array of doubles stays 8-aligned. Pairs of 32-bit scalars are padded to keep it so.
  • Doubles are IEEE-754 binary64. Face indices and colors are signed 32-bit integers.
  • Derived values such as length, area, volume, bounding box, and arc radius are not stored. Readers recompute them.
  • One unit per blob. Sub-objects inside a composite lose their own units.

Bodies

The mesh body is the one every producer needs. Curves follow one rule: the viewer has no NURBS evaluator, so a curve body leads with the display polyline it renders and trails with the analytical definition (degree, points, weights, knots). A polycurve or region nests each segment as its own SGEO blob, prefixed by its length and padded to 8 bytes. Arcs, circles, and ellipses are stored analytically. Points and point clouds store coordinates plus optional colors and sizes.
Treat the SDK encoders as the byte-exact reference for every body: SgeoEncoder in Speckle.Sdk and sgeo.py in specklepy. Both ship decoders and round-trip tests you can port.
The CRC covers the body only, and the geometry id covers the whole blob including the CRC. A producer that computes either over the wrong span produces blobs that decode nowhere and never deduplicate against anyone else’s.

The viewer .dat

After the upload completes, the server builds {versionId}.viewer.dat from the bundle and appends it to the version’s artifact list. The version is not visible until that build finishes. The WebGPU viewer downloads only the .dat, never the parquet files. The .dat is one self-contained binary: a geometry region of chunk-encoded meshes and lines, an index region of fixed-order sections (meta, primitives, chunks, placements, materials, colors), and a 128-byte trailer that maps the file. Its current format expands reusable definitions into placements and gives each occurrence a realization id, which is what lets the viewer select one instance of a repeated object. That identity exists only in the .dat. The bundle keeps the compact authored graph.
The .dat is an internal artifact of the viewer and the server. Its layout can change without a bundle spec change. Do not parse it, and do not write one.

Read geometry from a script

Receive the version as a Model through receive3 / Receive3. The SDK decodes SGEO into the mesh and curve types you already use and exposes them per object. If you read the parquet files directly through the artifacts endpoint, decode the blobs with the SDK decoder or a port of it, and read every geometry shard.

FAQ

Read the parquet geometry table, not the .dat. Download every geometry shard from the artifacts listing and open the files with any parquet library. Each row’s blob starts with the 16-byte header on this page: check the magic, read the primitive type and unit code, and verify the CRC-32 over the body. Then parse the body for that primitive, starting with the mesh layout above. Port the decoder from sgeo.py in specklepy or SgeoDecoder in Speckle.Sdk rather than writing one from the tables alone; both are short and tested. The .dat is the viewer’s own build of the same geometry and is not readable by anything else.
No. The .dat is built by the server and read only by the Speckle viewer. Its layout is not published, it can change without a bundle spec change, and it carries viewer-specific realization ids that mean nothing elsewhere. There is nothing to license because there is nothing to build against. The portable geometry is the bundle itself: SGEO blobs in the parquet geometry table, with open-source decoders in specklepy and Speckle.Sdk. To show a model, embed the Speckle viewer rather than reading its artifact.
No. The solid is the host’s own format and only a matching host can read it. Every consumer, including the viewer, renders the SGEO display mesh. Ship both.
Skip that primitive, keep the owning object, and report the skip. If nothing renderable remains and there is no solid, mark the object as failed in your results so the report matches the bundle.
Last modified on September 16, 2026