Camera Switch Animation

This article will provide an example of how to use code to animate switching from one camera to another camera.



This example in this article will utilize Threejs, the Player API, Animations, and the Camera API to find information about the camera and move/rotate it.


Change the Camera attribute value to move the camera from one camera to another.



The camera change above is achieved by a Configuration Rule executing a custom script every time the Camera attribute value changes. The code can be seen here:


// Initialize all variables for animation callback at top level of javascript
let startCamera;
let endCamera;
let swapCamera;
let startTrans;
let startQuaternion;
let startTargetTrans;
let startTargetDist;
let endTrans;
let endQuaternion;
let endCameraBaseNode;
let endTargetDist;
let endTargetTrans;
let maxCameraTime = 2000;
let start = undefined;

// Initialize Configurator to get Cameras
const config = api.configurator.getConfiguration();

// Pull end camera value from string configuration attribute that holds names of all cameras
endCamName = config["Camera"];

// Get both current and end cameras into storage
endCamera = api.scene.get({from: api.instanceId, name: endCamName});
startCamera = api.scene.get({from: api.instanceId, id: api.configurator.player.cameraController.activeCamera});

// **IMPORTANT** Place a null with the same translation as each of your cameras' start translations. Name the nulls <Camera name>_Base_Null. Make sure your camera and the base null are on the same level in the scene graph. These cameras and nulls should *NOT* be parented to one another
endCameraBaseNode = api.scene.get({from: api.instanceId, name: endCamName + "_Base_Null"});


function animateCameraTo(endCameraBaseNode){
// Initialize the camera to perform a swap between two locations

// Initialize information about start camera position/orientation
startTrans =;
startQuaternion =;
startTargetTrans = getTargetTranslation(startCamera);
startTargetDist = getDistanceBetween(startTrans, startTargetTrans);

// Initialize information about end camera position/orientation
endTrans = endCameraBaseNode.plugs.Transform[0].translation.valueOf();
endTargetTrans = getTargetTranslation(endCamera);
endQuaternion = getQuaternion(endTrans, endTargetTrans);
endTargetDist = getDistanceBetween(endTrans, endTargetTrans);

// Call animation callback function

function camStep(timestamp){
// Get Start time for animation
if(start === undefined) start = timestamp;

// Calculate how far along camera movement should be based on desired time
const elapsed = timestamp - start;
const elapsedPercent = elapsed / maxCameraTime;

// Call easing function for smoother, more natural, movement
const animPercent = easeIn(elapsedPercent);

// Init variables for calculation of next camera position
let camTrans = new api.THREE.Vector3();
let targetTrans = new api.THREE.Vector3();
let camQuat = new api.THREE.Quaternion();
let directionVector = new api.THREE.Vector3(0,0,1);

// Linear interpolation between how far the camera is from its original target to distance from final target
targetDist = startTargetDist + ((endTargetDist - startTargetDist) * animPercent);

// Linear interpolation of the camera target from start camera's target to end camera's target
targetTrans.lerpVectors(startTargetTrans, endTargetTrans, animPercent);

// Linear interpolation of camera quaternion between start quaternion and end quaternion **NOTE WE DO NOT USE ROTATION**
api.THREE.Quaternion.slerp(startQuaternion, endQuaternion, camQuat, animPercent);

// Calculate camera position based on distance from target point between start target and end target
camTrans.copy(targetTrans).addScaledVector(directionVector, targetDist);

if(elapsed < maxCameraTime){
// Set camera position & quaternion;;
} else {
// Set active camera to final cam, delete temporary swap camera, and reset start variable for future animations
api.setActiveCamera(api.scene.get({from: api.instanceId, name:}).id);
api.scene.deleteNode(api.scene.get({from: api.instanceId, name: 'Swap_Cam'}));
start = undefined;

function createAndSetSwapCamera(startCam){
let camCopy = JSON.parse(JSON.stringify(api.scene.get({from: api.instanceId, id:})));
delete; = 'Swap_Cam';
// Disable camera constraints because this could prevent camera movements while using cloned camera
camCopy.plugs.Camera[0].constraintLatitudeMode = 0;
camCopy.plugs.Camera[0].constraintLongitudeMode = 0;
newCamId = api.scene.addNode(camCopy, api.instanceId);

function getTargetTranslation(cam){
const targetId = api.scene.get({from: api.instanceId, name:, plug: "Camera", property: "targetNode"});
return targetId ? api.scene.get({from: api.instanceId, id: targetId, plug: "Transform", property: "translation"}) : {x: 0, y: 0, z: 0};

function getDistanceBetween(point1, point2){
return Math.sqrt(Math.pow(point2.x - point1.x,2) + Math.pow(point2.y - point1.y,2) + Math.pow(point2.z - point1.z,2));

function getQuaternion(camTrans, camTargetTrans){
// Calculate camera Quaternion based on distance and direction from camera target, Note: We do this because we cannot directly request the quaternion from a non-active camera
const camVector = {x: camTargetTrans.x - camTrans.x, y: camTargetTrans.y - camTrans.y, z: camTargetTrans.z - camTrans.z};

let euler = new api.THREE.Euler();
euler.setFromVector3(camVector, 'ZYX');
let camQuat = new api.THREE.Quaternion();
camQuat = camQuat.setFromEuler(euler);
return camQuat;

// Cubic easing function
function easeIn(x){
return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;


Setup Requirements

The above code example can be copy-pasted into a new scene and work with minimal setup. The code uses the following assumptions:

  • Each camera has a set Target Node and is pointed at that Target Node. If there is no Target Node designated, then the origin {0,0,0} is used.
    • For best results with this code, cameras should always be pointed directly at their desired target nodes.
  • Each camera has Keep Player Changes = False
  • Each camera has a null with the same translation in the scene as the camera. Those nulls are named <camera name>_Base_Null
  • A configuration attribute named “Camera” exists on the scene. The options for the configuration attribute match the names of the Camera nodes.


  • The script should be placed on a configuration rule with a condition that checks if attribute Camera has changed


Camera Limitations

  • This code assumes we do not wish to store camera position changes. Users are placed at the same default position each camera change and the orbit/zoom limits are always the same.
    • Example: If you start with a camera, zoom out to a max zoom distance. After re-positioning the camera via setPosition(), you are able to zoom out the camera the full zoom distance yet again.
  • Initial position null values must be created to reset camera movement data on the scene graph in order to preserve a Keep Player Changes = False state such that cameras revert to their starting values when users switch between camera options.
  • If the start and end cameras do not represent the same field of view, there will be a noticeable point at the end of the animation where the swap camera is switched to the final camera.
Share this