Skip to content

Camera Systems

The @web-engine-dev/camera package provides a flexible camera system supporting 2D follow cams, 3D perspective/orthographic cameras, smooth transitions, screen shake, zoom, and multi-player split-screen.

Setting Up a 2D Camera

The camera package is not component-based - it provides plain TypeScript classes. Create a CameraController, set a follow target, and call update() each frame:

typescript
import { CameraController } from '@web-engine-dev/camera';

const player = { position: { x: 0, y: 0 } }; // any object with { position: Vec2 }

const camera = new CameraController()
  .setFollowTarget(player)
  .setSmoothing(5) // higher = smoother/slower follow
  .setOffset({ x: 0, y: -50 })
  .setBounds({ minX: 0, minY: 0, maxX: 4096, maxY: 2048 });

// Every frame:
function update(deltaTime: number): void {
  camera.update(deltaTime);
  const { position, zoom, rotation } = camera.getState();
  // Apply position/zoom/rotation to your renderer
}

Any object that has a position: { x: number; y: number } property can serve as the follow target.

Camera Zones

For boss rooms or vertical-scroll sections you can temporarily override the target or bounds:

typescript
// Lock camera in place for a boss room
camera.setBounds({ minX: 2000, minY: 500, maxX: 3280, maxY: 1220 });

// Restore world bounds after boss is defeated
camera.setBounds({ minX: 0, minY: 0, maxX: 4096, maxY: 2048 });

Zoom

Zoom is a property on the camera state. Set it via the controller:

typescript
// Instant change
camera.setZoom(1.5);

// Read current state
const { zoom } = camera.getState();

Screen Shake

CameraController has built-in shake support. Trigger it directly:

typescript
// Trigger a one-shot shake
camera.triggerShake({ intensity: 10, duration: 0.3 });

// Preset shakes for common events
import { ShakePresets } from '@web-engine-dev/camera';

camera.triggerShake(ShakePresets.lightImpact()); // light hit
camera.triggerShake(ShakePresets.heavyImpact()); // explosion
camera.triggerShake(ShakePresets.earthquake()); // continuous
camera.triggerShake(ShakePresets.recoil()); // weapon recoil

For a standalone shake without a follow camera, use CameraShake:

typescript
import { CameraShake } from '@web-engine-dev/camera';

const shake = new CameraShake();
// shake.update(deltaTime);
// share shake.offset with your renderer

Cinematic Sequences

The CinematicCamera follows a list of waypoints with easing:

typescript
import { CinematicCamera } from '@web-engine-dev/camera';

const cutscene = new CinematicCamera()
  .addWaypoint({ position: { x: 0, y: 0 }, duration: 0 })
  .addWaypoint({ position: { x: 100, y: 50 }, duration: 2, zoom: 1.5 })
  .addWaypoint({ position: { x: 200, y: 0 }, duration: 1.5, pause: 1 })
  .onComplete(() => resumeGameplay())
  .setLoop(false)
  .play();

// Call every frame while it plays:
cutscene.update(deltaTime);
const { position, zoom } = cutscene.getState();

3D Cameras

The package provides TypeScript config/state interfaces for 3D cameras, not concrete component classes. Use factory functions to create configs with sensible defaults:

typescript
import {
  createOrbitControllerConfig,
  createFlyControllerConfig,
  createFollow3DControllerConfig,
  createPerspectiveProjection,
  createCamera3DState,
} from '@web-engine-dev/camera';

// Orbit camera
const orbitConfig = createOrbitControllerConfig({ distance: 20 });

// Free-fly FPS camera
const flyConfig = createFlyControllerConfig({ invertY: true });

// Third-person spring follow
const followConfig = createFollow3DControllerConfig({ mode: 'spring' });

// Perspective projection
const projection = createPerspectiveProjection({ fov: Math.PI / 3 });

// Initial camera state
const state = createCamera3DState({ position: { x: 0, y: 5, z: 10 } });

Wire these into your renderer directly.

Presets

typescript
import { CameraPresets } from '@web-engine-dev/camera';

CameraPresets.fps(); // first-person shooter
CameraPresets.thirdPerson(); // third-person action
CameraPresets.rts(); // real-time strategy
CameraPresets.topDown(); // top-down view
CameraPresets.sideScroller(); // side-scrolling platformer

Multi-Target Camera

The MultiTargetCamera automatically frames multiple targets:

typescript
import { MultiTargetCamera } from '@web-engine-dev/camera';

const camera = new MultiTargetCamera({
  targets: [player1, player2],
  padding: 100,
  screenSize: { x: 800, y: 600 },
  minZoom: 0.5,
  maxZoom: 2,
});

// Every frame:
camera.update(deltaTime);
const { position, zoom } = camera.getState();

Easing

typescript
import { CameraEasing } from '@web-engine-dev/camera';

CameraEasing.linear;
CameraEasing.easeOut;
CameraEasing.easeInOut;
CameraEasing.smoothstep;

Next Steps

  • Physics, physical cameras with collision
  • Visual Effects, post-processing effects attached to the camera
  • UI & HUD, screen-space overlay on top of the camera view

Proprietary software. All rights reserved.