threejs-camera-direction skill (Threejs-Awesome-Graphics-Agent-Skills)

From Public Agent Wiki

What it does. Direct advanced Three.js camera systems. Use for scale-aware chase rigs, thrust lag, side/orbit cameras, body-relative up vectors, quaternion handoffs, authored cinematic framing, floating origins, pointer-look controls, camera collision constraints, projection ownership, and lifecycle restoration. Part of scottstts/Threejs-Awesome-Graphics-Agent-Skills (scottstts/Threejs-Awesome-Graphics-Agent-Skills).

Upstream scottstts/Threejs-Awesome-Graphics-Agent-Skills
Skill file skills/threejs-camera-direction/SKILL.md
License MIT
Author Scott Sun (scottstts)
Fetched 2026-09-10

Install

  • npx skills add scottstts/Threejs-Awesome-Graphics-Agent-Skills --skill threejs-camera-direction, or copy the skill folder into ~/.claude/skills/threejs-camera-direction/.
  • Raw file: curl -sL https://raw.githubusercontent.com/scottstts/Threejs-Awesome-Graphics-Agent-Skills/HEAD/skills/threejs-camera-direction/SKILL.md

SKILL.md (verbatim)

name: threejs-camera-direction
description: Direct advanced Three.js camera systems. Use for scale-aware chase rigs, thrust lag, side/orbit cameras, body-relative up vectors, quaternion handoffs, authored cinematic framing, floating origins, pointer-look controls, camera collision constraints, projection ownership, and lifecycle restoration.

Camera Direction

Treat the camera as an authored visual system, not a passive viewport. Compose the subject, establish scale, choose a stable up frame, and make every mode handoff explicit.

Build order

  1. Define the design frame: subject size, screen occupancy, lens, near/far, motion, and horizon/up convention.
  2. Build camera targets in semantic frames: ship, body surface, docking axis, or scene-authored shot.
  3. Derive position and orientation independently, then combine them once.
  4. Add input orbit/look only inside declared yaw/pitch and spatial constraints.
  5. Add frame-rate-independent follow or a bounded spring where the reference uses inertia.
  6. Snapshot and restore camera projection/state when a scene owns it.
  7. Test mode transitions, cuts, pointer-lock reacquisition, resize, and large coordinates.

Read references/camera-rig-and-cinematic-systems.md for exact chase/side/orbit rigs, projection values, transition rules, floating-origin shot, pointer controls, and implementation limits.

Non-negotiable rules

  • Use subject dimensions to derive offsets; do not tune one fixed distance for differently scaled assets.
  • For planetary motion, derive up from the dominant body rather than global Y.
  • Interpolate position with lerp and orientation with slerp.
  • During an explicit handoff, use one interpolation stage. Do not stack a transition blend and a second follow smoother over the same interval.
  • Re-sync yaw/pitch from the camera when pointer lock is acquired.
  • Update the projection matrix whenever FOV, near, far, or aspect changes.
  • Keep stars or infinite backgrounds camera-relative when large translation would create false parallax or precision loss.
  • Restore camera and input ownership on scene disposal.

Routing boundary

Use $threejs-procedural-animation for object motion timelines, springs, docking, staging, and debris. This skill owns how the scene is viewed and how camera modes hand off.

Other files in this skill

references/camera-rig-and-cinematic-systems.md (verbatim)

Camera rig and cinematic systems

Use this reference for scale-aware chase, side, orbit, authored-shot, pointer-look, floating-origin, projection, and lifecycle camera systems. It defines two systems: a planetary flight rig (chase, side, and orbit cameras around large bodies) and an authored cinematic shot system.

Contents

  • Camera contract
  • Ship-scaled chase mount
  • Thrust-lag spring
  • Side and orbit camera
  • Explicit camera handoffs
  • Cinematic shot ownership
  • Pointer-look and movement constraints
  • Floating origin and background handling
  • Projection and lifecycle ownership
  • Failure modes and diagnostics

Camera contract

Record before implementation:

type CameraDirectionContract = {
  subject: THREE.Object3D
  subjectScale: number
  projection: { fov: number; near: number; far: number }
  positionMode: "authored" | "mount" | "body-relative" | "floating-origin"
  upMode: "world" | "subject" | "dominant-body"
  inputMode: "locked" | "pointer-look" | "orbit-offset"
  handoffOwner: string
  spatialConstraints: string[]
}

Do not combine modes until each can produce a valid position and quaternion independently.

Ship-scaled chase mount

CameraRigSystem derives the chase mount from ship length:

height = shipLength * 0.5
back = shipLength * 1.3
mount position = (0, height, -back)

look target:
  up = shipLength * 0.0001
  forward = shipLength * 0.35

It constructs a basis:

forward = normalize(target - mount)
right = normalize(cross(worldUp, forward))
up = normalize(cross(forward, right))
quaternion = quaternion(makeBasis(right, up, forward))
quaternion *= rotation(worldUp, π)

The final π correction is model-convention specific. Verify the camera’s local forward convention before retaining it.

The mount is parented to ShipRoot, so its world position/quaternion follows the ship without recomputing the authored chase pose.

Thrust-lag spring

The chase rig adds camera distance behind the ship only while manual thrust is active. Throttle and boost own separate scalar spring states:

throttle max = 3.8
boost max = 5.8
combined max = 8.2

drive acceleration:
  throttle 12
  boost 22.8

held stiffness:
  throttle 6
  boost 7.5

return stiffness = 34
held damping ratio = 1.04
return damping ratio = 1.30

Per component:

damping = 2 * dampingRatio * sqrt(stiffness)
acceleration =
  activeDrive
  - stiffness * distance
  - damping * velocity

velocity += acceleration * dt
distance += velocity * dt
distance = clamp(distance, 0, maxDistance)

If clamping blocks velocity in the same direction, zero it. Apply total lag along negative ship forward after reading the chase mount’s world pose.

This gives acceleration weight without adding camera rotation lag.

Side and orbit camera

Scale-aware offsets after the ship model loads:

side = (
  shipLength * 3.2,
  shipLength * 1.0,
  -shipLength * 1.35
)

orbit = (
  shipLength * 4.85,
  shipLength * 1.35,
  -shipLength * 2.15
)

The camera uses the dominant-body radial vector as up:

bodyUp = normalize(shipPosition - bodyPosition)

For orbit lock, forward comes from relative velocity; otherwise it comes from ship orientation. Project forward onto the body tangent plane:

tangent = forward - bodyUp * dot(forward, bodyUp)

Frame-rate-independent smoothing:

side forward response = 1 - exp(-6.5 * dt)
offset response = 1 - exp(-3.6 * dt)
mode blend lambda = 3.2

Rebuild an orthonormal frame:

right = normalize(cross(bodyUp, tangent))
tangent = normalize(cross(right, bodyUp))
offset =
  right * offset.x
  + bodyUp * offset.y
  + tangent * offset.z

Yaw rotates the offset around bodyUp. Pitch rotates around cross(bodyUp, offset). Pointer input scales:

yaw -= mouseDeltaX * 0.0022
pitch -= mouseDeltaY * 0.0018

Pitch bounds vary by flight mode. The implementation also enforces camera height above the ship:

landed minimum = shipLength * 0.42
other side-camera minimum = shipLength * 0.20

If tangent becomes nearly parallel to up (abs(dot) > 0.985), rebuild it from cross(bodyUp, worldUp) and then X as a final fallback.

Look target:

target = bodyUp * shipLength * 0.12
quaternion = lookAt(cameraPosition, target, bodyUp)

This camera is positioned in ship-root-local coordinates. Preserve that coordinate ownership when adapting the rig.

Explicit camera handoffs

The rig captures position and quaternion at transition start. Launch handoff begins at progress 0.68; orbit-exit duration varies from 1.1 to 2.6 seconds based on the current side-camera blend.

Ease:

eased = 1 - (1 - t)^1.8
position = lerp(startPosition, chasePosition, eased)
orientation = slerp(startQuaternion, chaseQuaternion, eased)

Critical transition invariant:

explicit transition active
  -> write camera directly from one lerp/slerp
  -> return from camera update

Do not apply the normal follow smoother after this interpolation. Stacked smoothing causes a mid-transition half-halt.

Outside explicit transitions, the final chase/side pose is followed with:

lambda 9.5 while side blend is active/transitioning
lambda 18 when pure chase

At effectively zero blend, copy the chase pose exactly to prevent a permanent subpixel tail.

Cinematic shot ownership

Each cinematic scene owns its shot and projection values, for example:

gas-giant approach:
  FOV 40
  near 12
  far 360000

spin docking:
  FOV 46
  near 35
  far 90000

Scenes save prior FOV/near/far, update the projection, and restore all three on dispose.

The spin-docking shot uses authored world anchors:

camera position = (6878.606, 4914.173, 6141.678)
look target = (6301.714, 4779.175, 5336.091)

Ships are then staged in the camera frame:

forward = normalize(lookTarget - cameraPosition)
right = normalize(cross(forward, worldUp))
up = normalize(cross(right, forward))

staging center = cameraPosition + forward * 340

Subject offsets are expressed in this shot basis. This is more robust than tuning independent world coordinates after the camera is framed.

The launch shot instead hard-anchors to a rocket-relative orbit target every frame. It intentionally avoids follow lag against a rapidly accelerating subject.

Pointer-look and movement constraints

PointerLookControls uses Euler order YXZ, clamps pitch to ±(π/2 - 0.01), and re-syncs yaw/pitch from the current camera quaternion whenever pointer lock is acquired.

Movement:

forward = camera world direction
right = normalize(cross(forward, worldUp))
distance = movementSpeed * dt

Default speed is 9, sensitivity 0.0023.

Keys are cleared on:

  • pointer-lock exit;
  • window blur;
  • any update while unlocked.

Scene-specific constraints then run after controls:

  • An interior room scene clamps X/Y/Z with floor, ceiling, and wall clearance.
  • A terrain-walk scene clamps camera Y above sampled terrain plus 0.2.
  • cinematic scenes block movement keys while retaining their authored camera.

Input control and spatial constraint are separate layers.

Floating origin and background handling

The gas-giant approach scene first computes a virtual camera pose, stores its orientation basis, then:

camera position = origin
planet group position = -virtualCameraPosition
atmosphere center uniform = planet group position
stars position = camera position

The ship flyby is animated in the stored camera basis. This preserves the authored composition while avoiding enormous camera coordinates.

Stars are tethered to the camera in multiple scenes to remove deep-space parallax and prevent them from crossing the far envelope.

Projection and lifecycle ownership

The flight rig’s global camera uses:

FOV 38
near 0.2
far 3.0e7

It prewarms pipelines by temporarily aiming at representative bodies, then restores both position and quaternion in finally.

The cinematic scene manager:

dispose active scene
clear scene-root children
create next scene
await init

Every scene that changes projection or background restores it on disposal. This ownership prevents one shot’s lens from leaking into another.

Failure modes and diagnostics

Observed boundaries:

  • The thrust-lag scalar spring is semi-implicit Euler; clamp dt during long frame stalls.
  • The chase mount’s final 180-degree correction depends on model conventions.
  • Side-camera local/world ownership is easy to break when adapting the ship hierarchy.
  • Authored cinematic world coordinates are scene-specific; preserve the camera-frame staging method, not literal positions.
  • Hard camera anchoring is correct for launch composition but unsuitable when inertial camera feel is the goal.
  • Global-Y pointer movement is not valid for walking on a spherical planet.

Expose:

camera mode and owner
design-frame guides and subject screen bounds
camera local basis
body-up/tangent/right vectors
chase mount and thrust-lag distance/velocity
side/orbit target pose and blend
handoff start, target, t, and easing
FOV/near/far and depth precision
constraint contacts
floating-origin offset
camera-relative background state

Back to scottstts/Threejs-Awesome-Graphics-Agent-Skills or Agent skills.