Skip to content

@cinevva/rt — Cinevva Game Runtime

A tiny, versioned, CDN-hosted component library for browser Three.js games. It owns the sign-bearing player-controls math (camera, movement, gravity, collision) so a game never has to write it. Hand-rolled control rigs ship inverted signs constantly (W walks backward, mouse-look pitches the wrong way, camera clips into walls); this library is correct by construction and regression-tested.

Peer dependency: three (resolved from the host game's import map). Nothing else.

Versioning

Immutable, path-versioned on the CDN:

  • https://cdn.cinevva.com/rt/1.0.0/controls.mjs — exact, never changes.
  • https://cdn.cinevva.com/rt/1/controls.mjs — latest non-breaking 1.x (gets bug fixes).

Pin to rt/1/ for automatic fixes, or rt/1.0.0/ for a frozen build. Breaking changes ship under rt/2/.

controls.mjs — createControls(options)

One call gives you a first-person, third-person, or top-down player with movement, gravity, jump, wall/ground collision, mouse-look, and mobile touch controls (on-screen joystick + drag-look + jump button, auto-enabled on touch devices).

js
import * as THREE from 'three';
import { createControls } from 'https://cdn.cinevva.com/rt/1/controls.mjs';

// ...set up renderer, scene, camera, and a `player` Object3D added to the scene...

const controls = createControls({
  mode: 'thirdPerson',   // 'fps' | 'thirdPerson' | 'topDown'
  camera,
  renderer,              // (or pass domElement) — where pointer-lock / touch attach
  scene,                 // colliders default to every mesh in the scene except the player
  target: player,        // the Object3D the controls move
});

renderer.setAnimationLoop(() => {
  const dt = clock.getDelta();
  const s = controls.update(dt);   // moves player + camera, returns state
  renderer.render(scene, camera);
});

controls.update(dt) returns per-frame state you can drive animation from:

js
{
  name: 'idle' | 'walk' | 'run' | 'jump' | 'fall' | 'land',
  position: { x, y, z },
  velocity: { x, y, z },
  grounded: boolean,
  facing: number,   // radians, target's Y rotation
  moving: boolean,
}

Options

optiondefaultmeaning
mode'thirdPerson''fps', 'thirdPerson', or 'topDown'
camerarequired. Your THREE.Camera.
targetrequired. The Object3D to move (the player body). In fps you can hide it.
scenescene to auto-collect colliders from (needed for collision unless you pass colliders)
renderer / domElementdocument.bodyelement that receives pointer-lock and touch input
collidersautoexplicit array of meshes to collide against (overrides autoColliders)
moveSpeed6m/s walk speed
runMultiplier1.8Shift-to-run multiplier
jumpSpeed8initial jump velocity
gravity-22m/s²
radius / height0.35 / 1.8capsule size for collision
slopeLimitDeg55steeper ground blocks like a wall
distance6third-person boom length / top-down camera height
lookSensitivity0.0025mouse/touch look speed
invertYfalseinvert vertical look
cameraCollisiontruethird-person anti-clip: the camera pulls in when a wall is behind the player, so you never get "camera/arm in my face"
pointerLocktruefps/third-person grab the pointer on click for mouse-look
enableTouchtrueauto-show the mobile joystick + jump button on touch devices

Controls given to the player

  • Desktop: WASD / arrows to move, mouse to look (click to capture), wheel to zoom (third-person/top-down), Shift to run, Space to jump.
  • Mobile: left-thumb virtual joystick to move, right-thumb drag to look, on-screen jump button. Auto-enabled; no extra code.

Notes

  • Collision is raycast-based against your scene meshes: a downward ray for ground height/normal and a forward ray per axis for walls (so you slide along walls). Good for flat ground, platforms, mazes, and props. Very dense scenes or thin fast-moving geometry may want custom handling.
  • Movement is always resolved against the camera yaw (except top-down, which uses fixed screen axes), so "forward" is always where the camera looks. This is the single most common bug in hand-written rigs, fixed here.
  • Call controls.dispose() to remove all listeners and the touch UI.

Roadmap

  • anim.mjs (v1.1) — AutoAnimator: feed it the state.name + velocity from controls.update() and a GLTF character on the Quaternius universal skeleton, and it cross-fades idle/walk/run/jump/fall/land clips automatically. Sourced from Cinevva World's resolver-driven animation FSM.