AI generates 3D models.
GLBForge ships them.

Generation is the commodity. GLBForge is the deterministic layer after it: read a mesh semantically in a tenth of a second while you are still editing it, diff what the last edit broke, then optimize it 90%+ smaller and gate it on a budget that either passes or fails. CLI, in-browser Studio, MCP server, GitHub Action.

$ npx glbforge ship model.glb

Node 20+ · MIT · runs locally, nothing uploaded. Hand ship a PNG or SVG instead and it forges the artwork; hand it a photo and it routes to a generator. npx glbforge ui opens the Studio.

a PNG logo, forged to layered 3D in one second — glbforge extrude --layers 4 --pillow · drag to orbit
−94%
89MB Meshy export → 5.5MB, in 7s
~0.1s
to read a 150k-triangle mesh (0.8s at 2M)
0
credits to forge artwork into 3D
100%
local — your assets stay on your machine

The pipeline, not the hype

Generation is a commodity. The gap between "generated" and "shipped" is where projects die — GLBForge closes it with deterministic tooling: same input, same settings, same bytes and pixels, every time.

◎Inspect

The read after every edit: one connected shell or floating pieces, watertight or holes, size in metres, up axis, origin placement, unapplied or mirrored transforms. Versioned rule ids, measured vs heuristic, a likely cause with its own confidence, a concrete fix. front is never guessed. ~0.1 s on a 150k-triangle asset; under a second on a raw 2M-triangle generation.

⇄Diff

Two versions in, a change note out: per-part size deltas, triangle and shell deltas, topology regressions (was watertight, now has three open loops), origin drift, meshes added or removed, optional SSIM with the cameras fixed to the before framing. Regressions first.

▦Analyze

Lighthouse-style report card: triangle/file/texture/GPU-memory budgets, welded-space topology, named lint rules with fixes. Exits non-zero over budget — it drops into CI like a linter.

⚒Optimize

Weld → simplify to budget → smooth normals → WebP or KTX2 textures (4–8× less GPU memory) → meshopt compression → LOD chains. Typically 90%+ smaller, with the visual loss measured rather than claimed.

◆Forge

Logos, wordmarks, SVGs → watertight 3D, no AI and no credits. Exact silhouette, layered colours (--layers auto measures the artwork first), pillow/emboss relief, bevels, enamel/chrome/neon/acrylic/rubber presets. --matte auto lifts a subject off a plain background first, so a photo forges as a sticker. Watertight, so it prints.

⎔Generate

For photographic subjects: Meshy 7, plus open weights on fal.ai GPU inference — Hunyuan3D-2, TRELLIS, TripoSR. Bring your own key; every generation lands already analyzed and one step from web-ready.

⬇Export

Web-ready GLB, print-ready STL (mm-scaled, watertightness verdict), and USDZ for iOS AR Quick Look — binary crate written in pure TypeScript, UsdPreviewSurface materials, UsdSkel skeletons and blend shapes, and node animation as xform time samples so a baked clip plays in AR. Plus a React Three Fiber viewer scaffold with LOD switching.

✓Budgets as contracts

mobile-hero, desktop-hero, product-configurator — opinionated, versioned caps with a published methodology; pin mobile-hero@1 in CI and it never moves under you.

⛭Animated assets

Skinned meshes and morph targets simplify bone-aware: the simplifier sees skin weights and morph deltas as attributes, dominant-joint boundaries are locked, and skins, inverse bind matrices and clips survive — into GLB and into UsdSkel.

↻Animate

A looping idle, bob, spin, sway, breathe or hop baked into any GLB without a rig: a pivot at the base centre carries the motion, your nodes are untouched, and re-running replaces the clip instead of stacking. Deterministic keys, and USDZ bakes them as time samples so AR Quick Look plays it.

☻Desktop companion

The finished asset as a talking character on your desktop: transparent always-on-top window, plays its clips, gazes at the cursor, and answers typed messages — with a Claude agent on your CLI login, or with any MCP client driving it. --hooks wires it to Claude Code, so every session on the machine shows its start, its permission prompts and its finish on the character's face.

In the edit loop, not just at the end

A tool you run once per asset never becomes a habit. The inner loop is where the work happens — an agent driving a mesh through bpy.ops is blind after every call. inspect answers what it cannot see, in a fraction of a second; diff says what the last edit broke. Both read GLB, glTF, USDZ, USDA and USDC, and both are MCP tools as well as commands.

$ glbforge inspect model.glb
authoring@1 · core-geometry@1, core-scene@1 · 110 ms

1 mesh, 150,000 triangles, 1.43 × 1.90 × 1.28 m,
Y-up. One shell, not watertight. Origin at the
bounding-box centre, 0.95 m above the base.
Front: unknown (declare it with an expectation).

WARN topo/non-manifold   measured
  152 non-manifold edges (0.10% of its triangle
  count): faces meet three or more to an edge.
  cause (70%): a simplification artifact rather
  than an authoring error.
  → Harmless for display. For printing or
    booleans, repair the source mesh.
$ glbforge diff before.glb after.glb
authoring@1 · diff@1 · 87 ms

The asset is 100% deeper along Z (8.0 cm →
16.0 cm). Triangles 4,292 → 20,024 (+367%).
Shells 1 → 44: 43 new separate pieces.
3 meshes added, 1 removed.
1 regression at warning or above.

WARN diff/shells-changed   measured
  cause (60%): a cut or boolean split geometry
  off, or new parts were added without joining.
  → If the pieces should be one solid,
    boolean-union them.

Add --expect "chair, single-shell, 0.4–1.2m tall, watertight, origin base" and the run becomes a contract: shells, watertightness, size range and origin are measured and fail as errors; a bare category is a plausibility warning with a stated confidence; front is recorded as declared, never measured.

"No visible loss" is a measured number

Every optimize and ship renders the asset from four fixed cameras before and after — deterministic software rasterizer, linear-light shading, 2× supersampled — and scores the pairs with SSIM. The weakest view must clear the profile's floor or the report card takes an error and the command exits non-zero, exactly like a budget violation.

  visual fidelity ✓ SSIM 96.7%  weakest view 95.8% @ verify_135 · floor 94.0% · geometric deviation ≤ 0.1%

Forge: 2D artwork → real 3D, free

Image-to-3D models produce mush from flat graphics. GLBForge traces the silhouette exactly, stacks the colors in layered depth, pillows the surface, and hands you a watertight mesh you can ship — or print. A photo of an object on a plain background is not a dead end either: --matte auto lifts the subject off the ground first — deterministic connectivity, no model download — and the cut is previewable, with a stated confidence, before any geometry exists.

neon preset — layers glow in their own colors
Meshy generation: 73MB raw → this 3.2MB web asset, one flag

GLBForge Studio

Try it right now in your browser — drag in a GLB or a logo, read the report card, optimize with one click, compare original-vs-optimized with a split slider and the measured SSIM, then export GLB, STL or USDZ. The whole pipeline runs client-side in WASM: your assets never leave the device. Only optional signed-in generation touches a server. The same Studio runs locally, with KTX2 and your own generation keys, via npx glbforge ui.

# the whole studio, no install:
npx glbforge ui

# or the CLI, piece by piece:
npx glbforge inspect model.glb --expect "chair, single-shell, watertight"
npx glbforge diff before.glb after.glb --visual
npx glbforge analyze model.glb --profile mobile-hero
npx glbforge optimize model.glb --ktx2 --lods 40000,10000
npx glbforge extrude logo.png --layers auto --pillow 0.03 --preset enamel
npx glbforge animate model.web.glb --preset idle
npx glbforge stl model.glb --size 80
npx glbforge usdz model.web.glb
npx glbforge init   # wire it into a project: scripts, MCP, CLAUDE.md

Built for agents and CI, not just humans

The same pipeline is an MCP server — 28 tools, published in the official registry as dev.glbforge/glbforge — so Claude Code, Cursor and friends can inspect, diff, fix and ship an asset inside one conversation, looking at rendered previews of their own output. Every tool answers one envelope (ok, summary, duration_ms, errors[], data) where each diagnostic carries a stable code, a severity and the prim path it refers to; every response validates against a published schema; mutating tools take dry_run and return a diff plus a re-validation, so nothing changes silently. And a GitHub Action posts report cards on pull requests and opens a PR with the optimized, perceptually verified files.

# MCP server (Claude Code):
claude mcp add glbforge -- npx -y @glbforge/mcp

# then just ask:
"inspect chair.glb — is it one
 watertight shell, origin at the base?"
"make logo.png into a web-ready
 3D asset under the mobile budget"
# .github/workflows/assets.yml
- uses: actions/checkout@v7
  with: { fetch-depth: 0, lfs: true }
- uses: glbforge/glbforge@main
  with:
    profile: mobile-hero
    optimize: true
# → report card on every PR, plus a PR
#   with the web-ready files (SSIM-verified)

Where it is today

An honest scope statement, kept in step with the code — the same one /llms.txt serves to agents.

Shipping now

  • inspect / diff / --expect contracts
  • analyze + versioned budget profiles
  • optimize with SSIM-gated fidelity
  • ship: one call, any input, budget-gated
  • forge (2D → watertight 3D, --matte auto lifts a subject off its background), STL, USDZ
  • generation: Meshy 7, Hunyuan3D-2, TRELLIS, TripoSR
  • skinned + morph-target assets, end to end
  • animate: looping motion without a rig, into GLB and USDZ
  • reading GLB, glTF, USDZ, USDA and USDC — pure TypeScript, no Pixar runtime
  • desktop companion; local opt-in usage counter
  • Studio (browser and local), 28-tool MCP server, GitHub Action, init, audit

Deliberately not

  • not a modeling suite — it reads and repackages meshes, it does not author them
  • not a renderer: the software rasterizer exists to measure fidelity, not to make beauty shots
  • not a model trainer, and not a Meshy clone
  • no Blender dependency — pure Node, gltf-transform + meshoptimizer + sharp
  • your files are never uploaded; the local usage counter is opt-in and never networked

Known gaps

  • USD composition arcs (references, payloads, variants) are reported, not resolved — the readers see one layer
  • LOD chains are not perceptually scored yet (they are intentionally lossy, so no gate)
  • no progress streaming on long MCP tools; generation is poll-based by design
  • budget caps will not move without real device and network failure data