You describe what exists. Volume figures out how to draw it — and keeps it in sync as the world changes.
Most 3D code is imperative: you build a scene graph by hand, wiring meshes, cameras, and lights together call by call. Orbital Volume inverts that. You declare entities decorated with a volume component — a cube, a light, a camera, an avatar — and publish them onto a shared event bus. Volume observes that traffic and renders the three.js scene for you. Add, change, or remove a declaration and the view follows. The programmer focuses on stating intent; the rendering is a reaction.
A scene is a list of objects, not a script. Entities carry components; Volume maps each geometry to a renderer and keeps pose, material, and lighting live.
Layouts load as JavaScript modules, so you get loops, variables, and computed values — declarative shape with the full power of the language. Twelve avatars in one line.
Built on @orbitalfoundation/bus, a tiny pub/sub kernel. Services self-assemble at runtime; nothing is wired ahead of time.
The same kernel runs in the browser and on the server. Volume draws on the client and no-ops cleanly where there is no display — one codebase, two homes.
The red cube in the scene above is just this:
// declare *what* exists — not how to render it const cube = { volume: { geometry: 'cube', material: { color: 'red' }, pose: { position: [-1, 2, 0], scale: [0.1, 0.1, 0.1] }, }, } // hand it to the bus; Volume observes and draws it, then keeps drawing each frame const bus = createBus() await bus.resolve([{ load: 'here/volume.js' }, cube]) bus.resolve({ run: 'realtime' })
See demo-scene.js for the full scene used on this page.
Volume is a thin, declarative shim over three.js: scenes, cameras, lights, primitives, and GLB/VRM model loading with animation retargeting and facial visemes. It descends from a long line of experiments in data-driven, agent-style applications — the bus it rides on is the distilled, published successor to orbital-sys.