Webstructor

A browser-based kitbashing tool for assembling 3D scenes from a library of parts and exporting the result as a single .glb file. No install, no account, no cloud round-trip — open the page, build, export.

The longer-term goal is to make procedural widgets — parameterised, generative geometries — first-class citizens of the kit. The current primitive and GLB libraries are scaffolding for that bigger idea.

Why I’m building it

Most 3D tools optimise for either modelling from scratch (Blender, Maya) or viewing finished assets (Sketchfab, model viewers). The middle ground — fast, gestural assembly of pre-made parts into a new whole — is where a lot of real creative work happens, and it’s underserved on the web.

Webstructor is my answer to that gap:

  • Compose, don’t model. Drag parts in, transform them, group them, ship a GLB.
  • Live in the browser. Three.js does the heavy lifting; everything else is plain TypeScript.
  • Stay open at the seams. Every library entry — primitive, GLB asset, or future procedural widget — implements the same ElementDefinition interface. One factory, infinite content types.

What’s in it today

A working v0.3.0 editor with:

  • Library of 8 geometric primitives + GLB-backed assets, all served from a data-driven registry.
  • Selection (single / multi), translate-rotate-scale gizmo with snapping, centroid-pivot multi-select transforms.
  • Undo / redo across add, delete, duplicate, group, and transform operations.
  • Image-based lighting via PMREM-prefiltered room environment.
  • Named camera views (perspective + 6 axis views), animated transitions, FOV presets, frame-selection.
  • Session save / restore + autosave with toast feedback.
  • One-click GLB export of the entire composed scene.

How it’s wired

src/
├── library/    — registry + primitives + GLB-backed assets
├── scene/      — renderer, camera, IBL, named views
├── editing/    — selection, transform, picking, history, commands
├── export/     — GLTFExporter wrapper
└── ui/         — sidebar + right rail

Two design decisions carry the project:

  1. Element registry. Every entry is { id, label, category, create }. The create() factory returns an arbitrary Object3D (sync or async). Primitives, GLBs, and future procedural widgets all plug into the same one-line interface — no parallel systems.
  2. Helpers vs. content. Everything user-created lives under viewport.root; that’s what the exporter sees. Grid, axes, and selection outlines live under viewport.helpers and are never exported. The split keeps “what you see” and “what you ship” cleanly separated.

What’s next

The roadmap is openly tracked in the repo. The headline items unlocking the procedural direction:

  • Parameter schema on ElementDefinition — let factories take a typed bag of values (numbers, ints, bools, colors, enums) instead of zero args.
  • Inspector panel — TRS values and live parameter sliders for the active selection. Editing procedural widgets has to be immediate.
  • First procedural widget — one real, useful generator (panel, hull frame, bolted plate) to validate the pipeline end-to-end.

After that, the editor essentials queue up: outliner, JSON scene save/load, drag-and-drop GLB import, material overrides, pivot controls.