Intermediate Bun @vulfram/engine@vulfram/camera-control

Camera Control + UI

Switch camera controller modes at runtime and keep World3D + WorldUI mounted in the same window.

What You Will Explore

  • Creating and disposing camera controllers without leaking input bindings.
  • Combining 3D and UI worlds in one target window.
  • Maintaining stable camera behavior while swapping controller strategies.

Prerequisites

  • Bun runtime installed.
  • Vulfram engine initialized with transport-bun.
  • A world with at least one perspective camera entity.

Install

bash
bun add @vulfram/engine @vulfram/transport-bun @vulfram/camera-control

Boot snippet (Bun)

ts
import { Mount, World3D, WorldUI, createWindow, initEngine, tick } from '@vulfram/engine';
import {
  createOrbitController,
  createSpectatorController,
  createFirstPersonController,
  createThirdPersonController,
  createTopViewController
} from '@vulfram/camera-control';
import { transportBunFfi } from '@vulfram/transport-bun';

initEngine({ transport: transportBunFfi });
const { windowId } = createWindow({ title: 'Camera Control Demo', size: [1280, 720] });

const world3d = World3D.create3DWorld();
const worldUI = WorldUI.createUIWorld();
Mount.mountWorld(world3d, { target: { kind: 'window', windowId } });
Mount.mountWorld(worldUI, { target: { kind: 'window', windowId } });

const camera = World3D.create3DEntity(world3d);
World3D.create3DCamera(world3d, camera, { kind: 'perspective', near: 0.1, far: 300, order: 0 });

let controller = createOrbitController(world3d, camera, { target: [0, 0, 0], radius: 8 });
// switch controller at runtime
controller.dispose?.();
controller = createFirstPersonController(world3d, camera, { alwaysLook: true });

let last = performance.now();
function frame(now: number) {
  const dt = now - last;
  last = now;
  tick(now, dt);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

// Install:
// bun add @vulfram/engine @vulfram/transport-bun @vulfram/camera-control
Live demo canvas