Your first camera

Build a follow camera and switch to a second shot, step by step.

This guide builds a camera that follows a moving box, then adds a second shot and switches between the two.

The core works with any renderer. Where the code touches yours, it says engine.

Create Klipp

main.ts
import { Klipp } from '@pmndrs/klipp';

const klipp = new Klipp();
klipp.setSize(canvas.width, canvas.height);

setSize matters for pieces that frame by screen position, like the composers. Call it again whenever the viewport resizes.

Add a moving target

The core doesn't read your scene. A target is a TargetPose you keep up to date. After update, klipp.shot holds the shot to put on your camera:

import { vec3 } from 'math';
import { targetPose } from '@pmndrs/klipp';

const player = targetPose.create();

function frame(dt: number) {
  const t = engine.time;
  vec3.set(player.position, Math.sin(t) * 4, 0.5, Math.cos(t) * 4);
  engine.box.setPosition(player.position);

  klipp.update(dt);
  engine.camera.setPosition(klipp.shot.position); // [x, y, z]
  engine.camera.setRotation(klipp.shot.quaternion); // [x, y, z, w]
  engine.camera.setFov(klipp.shot.fov); // degrees
  engine.render();
}

Add a virtual camera

A virtual camera is one shot, described by a Body, which decides where the camera is, and an Aim, which decides where it looks:

import { FollowBody, HardLookAtAim, VirtualCamera } from '@pmndrs/klipp';

const follow = new VirtualCamera('follow', { priority: 10 });
klipp.add(follow);
follow.body = new FollowBody(player, { offset: [0, 3, 8] });
follow.aim = new HardLookAtAim(player);

The camera now stays 8 units behind and 3 units above the box, looking straight at it.

Smooth it out

damping lets the camera lag behind a little instead of being glued to the box. The value is roughly how many seconds it takes to catch up:

follow.body = new FollowBody(player, { offset: [0, 3, 8], damping: 0.5 });

Add a second shot

A wide overview camera with a higher priority, turned on and off with active:

import { HardLockToTargetBody, VirtualCamera } from '@pmndrs/klipp';

const above = targetPose.create();
vec3.set(above.position, 0, 15, 15);
const center = targetPose.create();

const overview = new VirtualCamera('overview', { priority: 20, active: false });
klipp.add(overview);
overview.body = new HardLockToTargetBody(above);
overview.aim = new HardLookAtAim(center);

button.onclick = () => {
  overview.active = !overview.active;
};

The highest-priority active camera is the one on screen. Toggle overview and Klipp blends between the two shots.

Change the blend

By default a blend takes 2 seconds with an ease-in-out curve:

import { BlendCurves } from '@pmndrs/klipp';

const klipp = new Klipp({ defaultBlend: { curve: BlendCurves.easeInOut, time: 0.8 } });

Where to go next

  • Body and Aim for every way to position and aim a camera.
  • Blending for curves, per-camera transitions and blend hints.
  • Building on the core to read your engine's objects directly, or to use plain data and functions.