I Built an Open-Source 3D Configurator for Geodesic Shelters
Volver al Blog

I Built an Open-Source 3D Configurator for Geodesic Shelters

Three.js, vanilla JavaScript, 23 modules, 10 languages, and zero frameworks. Here is how it works.

March 1, 2026 Pete Thios 15 minutes

Most product configurators are built by teams of 10 engineers using React, Redux, and a year of runway. I built one by myself using Three.js, vanilla JavaScript, and stubbornness — and I am not a software engineer.

I am a product designer. For 15 years I have designed enterprise software at places like John Deere and FourKites, led design teams, built design systems, and obsessed over user research. I know Figma and OnShape. I know HTML and CSS. Before this project, I had never written a production JavaScript application.

The Thios configurator lets you design a modular geodesic structure in 3D, choose materials, generate a bill of materials, and get quotes from local suppliers — all in the browser. It supports 10 languages, runs on mobile, and the entire codebase is about 17,000 lines of JavaScript with zero framework dependencies.

I built it with Claude Code as my technical co-founder.

This is how it works.

The Problem

Thios makes modular geodesic shelters. Each structure is a truncated icosahedron — a football shape — built from 23 distinct panel types. Roof panels, wall sections, windows, doors, corners, and structural supports. You mix and match them to create saunas, greenhouses, offices, or whatever else you need.

The problem: how does a customer figure out which panels they need?

A PDF catalog does not work. You need to see how panels fit together in 3D space. You need to rotate the structure, swap materials, add a door where a wall used to be. You need to understand what you are building before you commit to buying $4,000 worth of lumber.

So I built a configurator.

The Stack

Here is the full stack. No surprises, no magic:

3D rendering: Three.js r172 (ES modules via CDN)

Frontend: Vanilla JavaScript. No React. No Vue. No build step. 26 JS files loaded in dependency order.

Backend: PHP 8 with PDO (MySQL). The same stack as the rest of the site.

3D models: GLB format (GLTF 2.0) with Draco compression. 23 modules, each 5-15 MB uncompressed, 60-900 KB compressed.

Environment: 7 HDRI environment maps for realistic lighting. You can preview your sauna in a forest clearing or a backyard at sunset.

CAD integration: OnShape REST API for live bill-of-materials data.

i18n: Custom translation system — 339 keys across 10 languages.

I chose vanilla JS deliberately. The configurator has complex state (module instances, 3D transforms, undo history, pricing), and I wanted to control all of it without fighting a framework's opinions about reactivity.

Three.js Scene Architecture

The 3D viewport is a standard Three.js setup with a few specific decisions:

OrbitControls for camera. Users expect to click-drag to rotate and scroll to zoom. OrbitControls does this out of the box with auto-rotate on load — so the structure spins slowly when the page first renders, giving you an immediate sense of the 3D model.

OutlinePass for selection. When you hover over a panel, it gets a gold outline glow. This uses Three.js post-processing (EffectComposer + OutlinePass) rather than changing the material itself. Cleaner visually, and the original material stays untouched.

HDRI environments for lighting. Flat lighting makes 3D models look like plastic toys. HDRI maps give you realistic reflections and ambient light. I included 7 presets (studio, courtyard, forest, sunset, etc.) — the user can switch between them to see how their structure looks in different contexts.

Draco compression for models. The raw GLB files are too large for web. Draco compresses geometry data (vertices, normals, UVs) dramatically. A panel that is 15 MB raw becomes 60 KB with Draco. The tradeoff is decode time, but the WASM-based Draco decoder handles this in under 100ms.

The Module System

This is the core of the configurator. A Thiosphere is not a single model — it is an assembly of 23 panel types that the user combines.

Each module has:

  • A unique ID (like "01" for roof panel, "16" for door frame)
  • A 3D model (GLB file)
  • A category (roof, walls, windows, corners, doors, structure)
  • A price
  • A maximum quantity
  • A set of subparts (the actual lumber cuts, hardware, and materials)

When a user adds a module to their configuration, the system:

  1. Checks if the GLB is already loaded. If yes, clones the existing mesh. If not, loads it via GLTFLoader + DRACOLoader.
  2. Creates a unique instance ID (like module01instance_0).
  3. Positions the module in the 3D scene.
  4. Updates the price calculator.
  5. Pushes a snapshot to the undo history.
  6. Triggers an auto-save to localStorage.

Module instances are tracked in a flat map:

```

moduleInstances = {

'module01instance_0': { mesh: THREE.Group, data: {...} },

'module05instance_2': { mesh: THREE.Group, data: {...} }

}

```

This flat structure makes serialization trivial. When you save a configuration, I just serialize the instance map, compress it with LZ-String, and encode it as a URL parameter. Sharing a configuration is sharing a URL.

Hex Grid: Multi-Sphere Layouts

A single Thiosphere is useful. But the real power is in multi-sphere configurations — two, three, four, or more structures connected by door panels.

The placement system uses a hexagonal grid with axial coordinates (q, r). Each sphere sits at a hex position, and door panels create passageways between adjacent hexes.

The hex math uses the standard cubic coordinate approach:

```

hexDistance(a, b) = (|a.q - b.q| + |a.q + a.r - b.q - b.r| + |a.r - b.r|) / 2

```

Rounding to the nearest hex from a floating-point position requires the cubic constraint (q + r + s = 0). The standard algorithm rounds all three coordinates independently, then resets whichever one drifted the most.

The configurator includes presets for common layouts: single, duo, trio, quad, 7-pack, and 12-pack. Each preset places spheres on the hex grid with appropriate spacing and rotation, so a user can jump straight to a cluster layout without placing each sphere individually.

Bill of Materials Generation

This is where software meets physical reality.

Each module is not just a 3D model — it maps to real physical parts. Module 01 (a roof panel) requires specific lumber cuts, fasteners, and sealing materials. The database stores all of this:

```

module_subparts:

module_id: "01"

part_number: "THI-01-FRAME-001"

part_name: "Truss Support Beam"

material: "Timber (Oak)"

quantity: 4

unit_cost: 45.50

dimensions_mm: "2400×100×50"

```

When you click "Generate BOM," the configurator sends your module selections to /api/get-full-bom.php. The API aggregates subparts across all selected modules, groups them by material type (timber, hardware, glass, insulation), and returns a complete parts list with costs.

The real trick: OnShape integration. The physical CAD models live in OnShape (a cloud CAD platform with a REST API). The BOM API can fetch the latest part data directly from the CAD source, ensuring that if I update a design in OnShape, the configurator reflects the change without manual database updates.

OnShape data is cached for 24 hours in the filesystem to avoid API rate limits. The cache key is a hash of the module combination, so identical queries hit the cache.

The Consumer-to-Vendor Pipeline

This is the part that makes the configurator more than a 3D toy.

After designing a structure, the user hits "Build Summary." This opens an overlay showing:

  • Module count and estimated cost
  • Parts grouped by supplier category (timber, hardware, glass, insulation)
  • A zip code field

The zip code does two things. First, it finds nearby building suppliers via an OpenStreetMap/Overpass API proxy (with 24-hour caching). Second, it creates vendor share tokens — unique URLs that give suppliers read-only access to the parts list for their specific category.

A timber yard sees only the lumber cuts. A hardware store sees only the fasteners. A glass supplier sees only the glazing. Nobody sees the full BOM or pricing for other categories.

Each token is a 64-character cryptographically secure string. The vendor opens the URL, sees a clean parts list, and can submit a quote. The consumer tracks all incoming quotes on a share dashboard.

The entire flow — from 3D design to vendor quotes — happens without a phone call.

Internationalization

The configurator supports 10 languages: English, Spanish, German, French, Swedish, Japanese, Dutch, Polish, Portuguese, and Italian.

The i18n system is simple by design:

```javascript

// i18n.js

function ct(key, params) {

let str = translations[key] || key;

if (params) {

Object.keys(params).forEach(k => {
  str = str.replace('{' + k + '}', params[k]);
});

}

return str;

}

```

339 translation keys stored in JSON files. Language detection follows a priority chain: URL parameter, localStorage, document referrer path, browser language, default English.

The ct() function supports interpolation: ct('notify_undo', {action: 'Add Module'}) returns the translated equivalent of "Undo Add Module" in whatever language is active.

One non-obvious challenge: emoji handling. Many UI strings use emoji prefixes (like a checkmark or warning icon). These must stay outside the ct() call because emojis are language-independent and should not be translated or duplicated in every language file.

State Management Without a Framework

The configurator has complex state:

  • Which modules are selected (and how many of each)
  • Which 3D instances exist (and their transforms)
  • Current material selections
  • Camera position
  • Undo/redo history (up to 50 levels)
  • Auto-saved configuration

Without React or Redux, I manage this with a few simple patterns:

Single source of truth: The selectedModules array and moduleInstances map are the canonical state. Everything else derives from these.

HistoryManager: Records state snapshots before each mutation. Undo pops the last snapshot and restores it. Redo pushes forward. Max 50 levels. Simple and predictable.

Auto-save: Every 30 seconds, the current state serializes to localStorage. On page load, if a saved state exists, the user gets a prompt to restore it. No registration required, no server roundtrip.

Config serialization: The entire configuration compresses to a URL-safe string via LZ-String. A complex multi-sphere layout compresses to maybe 200 characters. This means sharing a design is just copying a URL.

Mobile UX

Half of configurator traffic is mobile. 3D on mobile is hard.

The mobile UI uses a bottom drawer panel (like Google Maps). Swipe up to see modules, swipe down to collapse. The 3D viewport fills the screen above the drawer.

Touch gestures map to OrbitControls: one-finger drag rotates, two-finger pinch zooms, two-finger drag pans. Long-press on a module shows details. The gesture system uses raw touch events rather than a library — because the interaction between the drawer panel and the 3D viewport requires careful control over which element receives which events.

There is also an AR preview via Model Viewer. Export your configuration as a GLB, then view it in your actual backyard through your phone camera. This works on iOS and Android with no app install.

Performance Decisions

A few choices that keep it fast:

Deferred mesh instantiation: GLB files are loaded once and cached in memory. Each new instance is a scene.clone() — no redundant network requests.

Service Worker: Static assets (HTML, CSS, JS, HDRIs) are cached aggressively. After the first load, the configurator works offline. Only API calls need network.

CSS containment and GPU promotion: After a Reddit user reported scroll jank on older Android devices, I added contain: layout style and will-change: transform to the right elements. The MutationObserver that was scanning the DOM for progress bars (leftover from an earlier UI pattern) was removed — it was calling querySelectorAll('*') on every mutation.

Batch analytics: Instead of firing a network request on every user action, events accumulate in memory and flush every 5 seconds or when the batch hits 10 events.

How Claude Code Changed What Is Possible

I need to be direct about this: the configurator would not exist without AI.

Not because the code is particularly clever. Most of it is straightforward — load a 3D model, track state, serialize to JSON, call an API. The kind of code a mid-level engineer writes without thinking. But I am not an engineer. I am a designer who has spent 15 years on the other side of the handoff.

My progression went: GitHub Copilot for autocomplete, then Cursor for conversational coding, then Claude Code for full-stack development. Each step expanded what I could build alone.

Claude Code is where it clicked. The workflow looks like this:

  1. I describe what I want in product terms — "the user should be able to undo the last 50 actions and the state should serialize to a URL"
  2. Claude writes the implementation — HistoryManager with snapshots, LZ-String compression, URL encoding
  3. I review, test, and iterate — "this breaks when you undo past a sphere deletion, the hex grid positions are not restoring"
  4. Claude debugs and fixes — understanding the full context of 17,000 lines across 26 files

The critical insight: I was never learning to code. I was directing an implementation the same way I have always directed implementations — with clear requirements, user scenarios, and acceptance criteria. The difference is that my engineering partner works at 3am, never forgets context (within a session), and does not need a sprint planning meeting.

What made this work was not AI writing code. It was a designer with 15 years of product experience knowing exactly what to build. The hard part of software has never been typing the code. It is knowing what the code should do, for whom, and why. That is design.

The entire Thios platform — three websites, a 3D configurator, a store with Stripe integration, a blog, a partner portal, APIs, databases, 10-language i18n — was built by one person in 18 months. Not because I learned to be a developer. Because the barrier between "knowing what to build" and "building it" collapsed.

If you are a designer reading this and thinking you could never build something like this: you are wrong. You already have the hardest skill. You know how to think about users, define problems, and make decisions under ambiguity. The code is the easy part now.

What I Would Do Differently

Use TypeScript. The codebase is 17,000 lines of untyped JavaScript. It works. But refactoring is scary, and catching type errors requires running the app rather than reading the code.

Use a module bundler from the start. The 26-file sequential load chain works but is fragile. One missing script tag and everything breaks silently. A proper bundler with tree-shaking would reduce load time and eliminate dependency ordering bugs.

Separate the UI layer. The main configurator file is 3,000+ lines because it handles both 3D logic and UI updates. A clean separation between the 3D engine and the UI would make both easier to maintain.

Write tests. The BOM calculation, hex grid math, and pricing engine are all testable in isolation. I skipped tests because I was the only developer and could hold the whole system in my head. That works until it does not.

Try It

The configurator is live at thios.co/configurator. Place some modules, rotate the model, switch environments, generate a BOM. The full experience takes about 5 minutes.

The source is available on GitHub. It is open-source under the CERN-OHL-S license — the same license used for open-source hardware at CERN.

If you build something similar (a 3D product configurator for any physical product), I would love to hear about it. The intersection of web 3D and physical manufacturing is underexplored, and there is room for more open-source tooling.


Open the configurator — design a modular geodesic structure in your browser.

View the source — all code, all models, open-source.

Join the community — builders, engineers, and the occasionally confused.

Etiquetas: engineering open-source configurator three-js javascript
Compartir esta publicación
X LinkedIn Facebook
Publicación anterior
The Fourth Space Is Not a Place. It Is a Passion.

Publicaciones relacionadas

The Shape of Shelter

February 25, 2026

The Geometry of Everything Else: How the Thiosphere Platform Works

February 20, 2026

The Hyposphere: A Thought Experiment in Pressurized Geometry

February 25, 2026

Why Not a Dome? Geodesic vs Modular Prismatic Design

February 23, 2026
Volver a todas las publicaciones

¿Listo para construir?

Diseña tu propio espacio modular con nuestro configurador 3D, u obtén la guía de construcción completa.

Obtener el manual Diseña tu esfera

SUSCRIPCIÓN AL BOLETÍN

Respetamos tu privacidad. Tu correo nunca será compartido con terceros. Lee nuestra política de privacidad.

🔥 ¡Nuestro primer producto ha sido lanzado!

Obtén el Manual Completo de Thiosphere - la guía completa de espacios modulares de código abierto - ¡desde $29!

¡La Edición Fundador incluye acceso vitalicio a todos los manuales planeados!

Compra el tuyo hoy

☕ Apoya el Proyecto

Ayúdanos a hacer los espacios modulares sostenibles accesibles para todos.