This repository contains a browser-based brush/solid editor for creating lightweight interactive 3D scenes, plus a built-in browser runner for playing those scenes.
The product goal is:
- Hammer / TrenchBroom style spatial authoring
- modern browser delivery
- glTF asset import/export
- fast edit -> run iteration
- lightweight interactive runtime with spatial audio, navigation modes, and simple entity-driven logic
This is not a general-purpose DCC.
This is not a full game engine.
This is a focused authoring + runtime tool for browser-delivered 3D spaces.
---
## Product pillars
1.**Brushes are sacred**
- Layout authoring must remain faster than Blender.
- Brush editing is a first-class workflow, not a legacy compatibility mode.
- The editor must feel immediate, precise, and grid-friendly.
2.**Imported assets are first-class**
- glTF / GLB import must feel native.
- Imported meshes, materials, textures, and animations must coexist cleanly with brush-authored worlds.
- Imported assets complement brushes; they do not replace them.
3.**The runner is built in**
- Every meaningful authoring step should be testable in-browser.
- Switching from edit mode to play mode should be nearly instant.
- The runner is part of the product, not a demo app.
4.**Web-native sharing matters**
- Scenes should be easy to load, embed, preview, and eventually share by URL.
- The browser is a target platform, not a secondary export target.
---
## Architectural stance
When making design decisions, prefer:
- plain three.js over unnecessary abstraction
- explicit data models over implicit scene graph state
- deterministic rebuilds over hidden mutations
- command-based editing over ad hoc state changes
- typed scene entities over free-form JSON blobs
- vertical slices over speculative infrastructure
- boring, maintainable code over cleverness
The project uses:
- React for application shell and editor UI
- three.js for viewport and runtime rendering
- a canonical editor document model independent of three.js
- command pattern for undo/redo
- runtime entity systems for navigation, triggers, audio, and interaction
- glTF / GLB as the main interchange asset format
- JSON as the canonical authoring format
Do not collapse editor state into raw three.js objects.
Do not make the three.js scene the source of truth.
Do not make glTF the canonical editor save format.
---
## Early binding decisions
These defaults are intentionally fixed for the early slices unless a later slice explicitly changes them.
### Coordinate system
- world space is right-handed and **Y-up**
-`+X` is right, `+Y` is up
- scene units are meter-like and should be used consistently for movement, collision, and audio distances
### Early repo shape
- start as a single Vite app
- keep domain folders under `src/`
- do not introduce `/apps` + `/packages` or a monorepo split until the current code actually needs it
### State ownership
- do not use the React tree as the canonical state container
- keep canonical editor state in a thin external editor store/service
- React renders and dispatches commands; it does not own the document
### Persistence
- the canonical scene document is versioned from day one
- M0-M2 may use local draft persistence plus explicit JSON import/export
- when binary assets arrive, they must survive reloads via embedded data or project-scoped packaged storage
- never rely on ephemeral Blob URLs as the only persisted asset reference
### Early brush defaults
- Slice 1.1 box brushes are axis-aligned only
- arbitrary brush rotation is explicitly deferred
- canonical box face IDs are fixed and stable:
-`posX`
-`negX`
-`posY`
-`negY`
-`posZ`
-`negZ`
-`posY` is the top face and `negY` is the bottom face
### Model placement
- placed imported models are **model instances**, not typed entities
- keep model instances in a document collection separate from `entities`
### Runtime interaction scope
- keep trigger/action/target links explicit and typed
- do not activate actions for systems that do not exist yet
- add sound and animation actions only when those runtime systems are implemented
### Early clipping scope
- until a dedicated convex-brush slice exists, clipping must be constrained to results representable by currently supported brush kinds
- unsupported clip cases must fail clearly instead of inventing new hidden geometry rules
---
## Non-goals
Unless explicitly added to the roadmap, do not turn this project into:
- a general CAD package
- a Blender replacement
- a multiplayer MMO editor
- a full node-based visual scripting environment
- a full physics sandbox
- a photoreal AAA renderer
- a React Three Fiber showcase
- an ECS research project
We may add optional scripting, plugins, collaboration, or advanced baking later.
They are not v1 priorities.
---
## Core product vocabulary
Use these terms consistently:
- **Document**: canonical editor state
- **Brush**: author-authored solid/primitive in canonical brush form
- **Face**: one editable surface of a brush
- **Material**: logical authoring material definition
- **Texture**: image resource backing material channels
- **Asset**: imported external resource, usually GLB/GLTF or audio and related media
- **Model Instance**: placed scene instance of an imported asset
- **Prefab**: reusable asset/entity package placeable in scenes
- **Entity**: typed scene object with runtime/editor semantics
- **Runner**: browser runtime that loads and plays scenes
- **Viewport**: editor rendering surface
- **Command**: undoable state transition
- **Tool**: editor interaction mode such as select, move, box-create, face-edit
- **Build**: deterministic transformation from document -> runtime scene data
- **Export**: transformation to external deliverables such as GLB
Avoid vague terms like “object”, “thing”, “item”, or “component” when a more precise domain term exists.