Skip to main content

Behavior & the runtime

@scenography/engine is the how: it materializes a scene with three.js, runs the render loop, and drives a free-roaming first-person camera (drag to look, WASD or the arrow keys to move, a subtle head-bob while walking).

createGallery

import { createGallery } from '@scenography/engine';
import { wall } from '@scenography/plugin-wall';
import { artwork } from '@scenography/plugin-artwork';
import { fpc } from '@scenography/plugin-fpc';

const gallery = createGallery(scene, {
canvas,
plugins: [wall(), artwork(), fpc()],
});
gallery.start();

Runtime plugins

The kernel renders nothing on its own — a camera, a canvas and a render loop. Everything else is a plugin you pass in options.plugins:

  • Element plugins (wall(), artwork(), floor(), water(), sky(), audio(), …) materialize the scene key they own into three.js objects and keep them reconciled as the definition changes.
  • System plugins drive shared, per-frame runtime state: fpc() (the free-roaming first-person camera), focus() (frame a clicked artwork) and cardboard() (stereoscopic VR).

Order matters: a plugin that publishes a capability (the floor's terrain, a wall's colliders) must come before the plugins that consume it.

The returned Gallery exposes:

MethodPurpose
setScene(next)Apply a new definition, reconciling only what changed.
start() / stop()Control the render loop.
resize(w, h)Resize renderer and camera.
on(event, handler)Subscribe to a named event; returns an unsubscribe fn.
emit(event, data)Emit a named event.
dispose()Tear down and release GPU resources.
ctxThe high-level API handed to behavior code.

The ctx — a high-level API, not raw three.js

Behavior code talks to ctx, not to three.js directly. This keeps three.js an implementation detail that can be upgraded or swapped without breaking your code.

const wall = gallery.ctx.entity('north'); // the live object for an id

When you genuinely need it, there is a documented escape hatch to the underlying three.js objects:

const { scene, camera, renderer } = gallery.ctx.three;

Wiring behavior by id

Behavior is connected to data by id, never embedded inside the definition. That separation is what keeps the definition pure data.

gallery.on('entrance:enter', () => {
// ... react to a zone, play media, move the camera, etc.
});

Zones and triggers are part of the roadmap; the event surface above is the seam they plug into.